apbq II
Challenge
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from Crypto.Util.number import getPrime, bytes_to_long
from random import randint
p = getPrime(1024)
q = getPrime(1024)
n = p * q
e = 0x10001
hints = []
for _ in range(3):
a, b = randint(0, 2**312), randint(0, 2**312)
hints.append(a * p + b * q)
FLAG = open('flag.txt', 'rb').read().strip()
c = pow(bytes_to_long(FLAG), e, n)
print(f'{n = }')
print(f'{c = }')
print(f'{hints = }')
Phân tích: Challenge cho 3 số là tổ hợp tuyến tính của p và q và tích của chúng, yêu cầu tìm lại p và q để khôi phục mã RSA
Vì có 3 biến nên mình gọi 3 phương trình lần lượt là:
1
2
3
a1q + b1p == h1 (1)
a2q + b2p == h2 (2)
a3q + b3p == h3 (3)
Khử q từ phương trình 1 và phương trình 2 ta được $a_2h_1 - a_1h_2 = (b_1a_2-b_2a_1)~p$ hay là $a_2h_1-a_1h_2 = 0 ~ mod ~ p$
Tương tự $b_2h_1-b_1h_2 = 0 ~ mod ~ q$
Nhân lại với nhau ta được $a_1b_1 h_2^2 - (a_2b_1 +a_1b_2) h_1h_2 + a_2b_2 h_1^2 = 0 ~ mod ~ N (*)$
Cứ nhân như thế ta sẽ được 9 phương trình. Tuy nhiên sau khi end giải, mình có nhìn lại và thực tế chỉ cần 2 hints là đủ để giải
Xây dựng ma trận từ Lattice:
Xét phương trình *, ta có thể coi nó là phương trình 3 ẩn $xh_2^2 - yh_1h_2 + zh_1^2 = 0$
Đây là phương trình tuyến tính, ta có thể giải nó bằng LLL với ma trận
\[\begin{pmatrix} \frac{1}{2^{614}} & & & h_{2}^2 \\ & \frac{1}{2^{615}} & & -h_{1}h_{2} \\ & & \frac{1}{2^{614}} & h_{1}^2 \\ & & & N \\ \end{pmatrix}\]Ma trận này sẽ chứa Vector nhỏ chứa nghiệm phương trình là $(\frac{a_1a_2}{2^{614}}…., 0)$ (mình lười viết)
Ta lại có đẳng thức sau $a_1b_1 a_2^2 + a_2b_2 a_1^2 = (a_2b_1 + a_1b_2)a_1a_2$
Phương trình đẳng cấp bậc 2 mình có thể tìm tỷ số của $a_1/a_2 = t_1/t_2$, lại có $t_1h_2 - t_2h_1$ chia hết cho $p$, do đó ta tính được gcd
tìm được p
Solution
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
from Crypto.Util.number import long_to_bytes
n = 19604411241131769363446674414275822275458180476470552084588074159518829172495704100505151090666577360738167275118902368371310303168115405693118232835691333564186301532462296609038929723894816307213749698854885742811071911120730847279568876996811433319487396434622751326281226335521193361938724747445445693062336009352580319440819629260614301234971135026395941954109172567154559773437059174108362692061503832044135079892892132874437669744266153424229215832487832220575345912010076934611701182205068942016022731469408259665250938484104489962832891496953003599692437819685070043991828977623632396604764153766442526750861
c = 16285668872352205553535195410427806596787838055817589230402081772267074935944610262823936480340920868690431897597533868262062616060037808676539290467732178272289192993251540763800683055555842878669862850848141950704501553807462214356142018528608581546359772903806691435935873069006470208826518183488708349814610769150918356795466688732617809664831695402633481224388822180989614886783145141129157248734213457746877012517003702540070132542733288350979858743766340483503277308585752213220454288897741262154622769402029906981612699288206404870733274360554941455758083078106150045394190625280336371690998959720015636304971
hints = [773921762798470573303163880380136299117907456177270598418425239064611119645412840945432294881005107606154919094297188183879147500755682510817840590276985061404198538395146658442221987303866921786284298394947485319583779671756246394491275898343836522004269864718405399287554020900395899143149709408762450693357109188640512875099087677956684946219071353228860650842182953922400081939934842271539450654323, 1506346165967409413422972644306025635334450240732478358292064973873339506571546529986180145578525288707094235933775031106971678278204568310035260899591267480098015553318079460357474533531922997212816264042117005473870580122983241038123126855791876827449194400045937398578284016176289760858365282167537689282643483235235028505475719615108102516557986483184881658418430287064598580231630440534438966422546, 531028473288853560172466570664059891687387973059926255729701774553291285115938502989142318123512885100092638435578692032744904444237266108167428726130739434630511943317357743527053899234918340191006102436386051432760331852295930159729030763033361158582919982579082557775409422474301977588011705535991555292564664521116375190464371518812625647175447427886432255779925378071998411170643507118426289778768]
h1, h2, h3 = hints
m = Matrix.identity(QQ, 4)
m[0,0] = 1 / 2^614
m[1,1] = 1 / 2^615
m[2,2] = 1 / 2^614
m[0,3] = h2^2
m[1,3] = -h1*h2
m[2,3] = h1^2
m[3,3] = n
m = m.LLL()
a1b1 = m[0][0] * 2**614
a2b1_a1b2 = m[0][1] * 2**615
a2b2 = m[0][2] * 2**614
R.<t> = PolynomialRing(QQ)
f = a1b1*t^2 - a2b1_a1b2 * t + a2b2
t1, t2 = f.roots()[0][0].as_integer_ratio()
p = gcd(h1*t1-h2*t2,n)
q = n // p
d = pow(65537, -1, int(p-1) * int(q-1))
m = pow(c,int(d), n)
print(long_to_bytes(int(m)))
Mini DNS Server
Challenge
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import time
from dnslib.server import DNSServer, BaseResolver
from dnslib import RR, TXT, QTYPE, RCODE
class Resolver(BaseResolver):
def resolve(self, request, handler):
reply = request.reply()
reply.header.rcode = RCODE.reverse['REFUSED']
if len(handler.request[0]) > 72:
return reply
if request.get_q().qtype != QTYPE.TXT:
return reply
qname = request.get_q().get_qname()
if qname == 'free.flag.for.flag.loving.flag.capturers.downunderctf.com':
FLAG = open('flag.txt', 'r').read().strip()
txt_resp = FLAG
else:
txt_resp = 'NOPE'
reply.header.rcode = RCODE.reverse['NOERROR']
reply.add_answer(RR(qname, QTYPE.TXT, rdata=TXT(txt_resp)))
return reply
server = DNSServer(Resolver(), port=8053)
server.start_thread()
while server.isAlive():
time.sleep(1)
Chall yêu cầu mình gửi DNS tới server với yêu cầu domain là free.flag.for.flag.loving.flag.capturers.downunderctf.com
, request có độ dài không quá 72, question type == TXT
Trước đó mình cũng đang tìm hiểu dở về DNS nên gặp chall này mình cũng có hứng thú, tiếc là không đủ thời gian để làm :((
Cấu trúc của một query DNS
1
2
3
4
5
6
7
8
import dns.query
import dns.message
import dns.rdatatype
domain = 'free.flag.for.flag.loving.flag.capturers.downunderctf.com'
qname = dns.name.from_text(domain)
dns.message.make_query(qname, dns.rdatatype.TXT).to_wire()
# b'\x13\xec\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x04free\x04flag\x03for\x04flag\x06loving\x04flag\tcapturers\x0cdownunderctf\x03com\x00\x00\x10\x00\x01'
Như ta thấy 2 bytes đầu là ID
, 2 bytes tiếp theo là flags
, 2 bytes số lượng câu hỏi (là 1), 6 bytes answer, authority, additional thì không dùng đến, vậy là 12 bytes header
Mỗi subdomain sẽ có dạng \x??abcde
với \x??
là độ dài của subdomain đó
Ví dụ: blog.anhtv.live
sẽ ghi là \x04blog\x05anhtv\x04live
\x00
đánh dấu kết thúc tên miền
2 bytes gần cuối ghi QUERYTYPE (\x00\x10
là TXT)
2 bytes cuối ghi QUERYCLASS(\x00\x01
là IN - Internet?)
Như vậy thì là độ dài của nó đã là 75 rồi, vậy thì muốn giảm xuống 72 thì phải làm thế nào?
DNS Pointer and Solution
Để giải quyết tin nhắn dài, người ta sử dụng DNS Pointer để lặp lại tin nhắn
= \xc0 + offset
sẽ đưa con trỏ dns tới query đó
Nhận thấy Server không check flag, 2 bytes đầu là ngẫu nhiên, vậy ta có thể dùng được 4 bytes, ta sẽ đưa .com lên đầu
1
2
3
4
5
6
7
8
b = b"\x03com"
# Tận dụng được bytes 00 kết thúc domain
b += \x00\x01\x00\x00\x00\x00\x00\x00
# Domain
b += \x04free\x04flag\x03for\x04flag\x06loving\x04flag\tcapturers\x0cdownunderctf
# Trỏ về vị trí ban đầu
b += \xc0\x00
b += \x00\x10\x00\x01
Vậy ta đầy đủ payload, gửi tới server nào
1
2
3
4
5
6
7
8
9
10
11
import socket
host = "0.0.0.0" # Replace by DNS Server
port = 8053
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.sendto(b, (host, port))
t = sock.recv(4096)
print(t)
sock.close()