趁着主办方环境还在 (11.28) 赛后将安洵杯三道题都复现了一遍,难度比较友好。赛后复现过程比较顺利于是水一篇 wp。
# cry1一个猜数字的签到题,连进去之后硬猜就行了,多试几次就出了
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 from pwn import *from Crypto.Util.number import *import gmpy2import stringimport itertoolsimport hashlibtable = string.ascii_letters + string.digits context(log_level = 'debug' ) ip = '120.78.131.38' port = 10001 r = remote(ip,port) def proof (): r.recvuntil(b'SHA256(XXXX' ) line = r.recvline()[:-1 ].decode() print (line) tmp = line[line.find(' + ' ) + 3 :line.find(')' )] print (tmp) aim = line[line.find(':' ) + 1 :] print (aim) for i in itertools.product(table,repeat=4 ): ans = '' .join(i) if hashlib.sha256((ans + tmp).encode()).hexdigest() == aim: print (ans) r.recvuntil(b'Give Me XXXX:\n' ) r.sendline(ans.encode()) return proof() r.recvuntil(b"If you guessed right, I'll give you the flag!, You only have 6 chances (1~20)\n" ) for _ in range (6 ): num = random.randint(1 ,20 ) r.send(str (num).encode()) r.recvline() r.interactive()
# cry2附件
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 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 import osimport randomimport stringimport hashlibimport binasciiimport socketserverfrom Crypto.Cipher import AESflag = 'D0g3{' + '' .join([random.choice(string.ascii_letters + string.digits) for _ in range (18 )]) + '}' class MyServer (socketserver.BaseRequestHandler): def proof (self ): random.seed(os.urandom(8 )) random_str = '' .join([random.choice(string.ascii_letters + string.digits) for _ in range (20 )]) str_sha256 = hashlib.sha256(random_str.encode()).hexdigest() self.request.sendall(('SHA256(XXXX + %s):%s\n' % (random_str[4 :], str_sha256)).encode()) self.request.sendall('Give Me XXXX:\n' .encode()) XXXX = self.request.recv(2048 ).strip() if hashlib.sha256((XXXX + random_str[4 :].encode())).hexdigest() != str_sha256: return False return True def pad (self, message ): if len (message) % 16 != 0 : return message.ljust((len (message) // 16 + 1 ) * 16 , '0' ).encode() else : return message.encode() def encrypt (self, key, message ): aes = AES.new(key, AES.MODE_ECB) return aes.encrypt(message) def decrypt (self, key, message ): aes = AES.new(key, AES.MODE_ECB) return aes.decrypt(message) def handle (self ): if not self.proof(): self.request.sendall(b'Error Hash!' ) return flag1, flag2 = flag[:8 ], flag[8 :] while True : self.request.sendall('You can input anything:\n' .encode()) iMessage = self.request.recv(2048 ).strip().decode() message = self.pad(flag1 + iMessage + flag2) cipher = binascii.hexlify(self.encrypt(flag2.encode(), message)) self.request.sendall(('Here is your cipher: %s\n' % cipher).encode()) if __name__ == '__main__' : sever = socketserver.ThreadingTCPServer(('0.0.0.0' , 10086 ), MyServer) sever.serve_forever()
主要逻辑在 handle 中。主要逻辑是我们输入 iMessage
变量,之后和 flag1
与 flag2
一起 pad,pad 函数用的不是库里面的函数,是题目自己写的,每次将输入的信息用 0
补齐为 16 长度的整数倍。注意 flag 被分成了两部分,第一部分 8 长度,第二部分 16 长度。
测试 pad 函数的作用 (flag 为自行生成的)
1 2 3 4 5 6 7 8 9 10 11 12 13 def pad (message ): if len (message) % 16 != 0 : return message.ljust((len (message) // 16 + 1 ) * 16 , '0' ).encode() else : return message.encode() flag = 'D0g3{ikq6HOskfLMKqJk825}' flag1, flag2 = flag[:8 ], flag[8 :] iMessage = '0000000' tmp = flag1 + iMessage + flag2 p = pad(tmp) out = [p[i:i + 16 ] for i in range (0 ,len (p),16 )] print (out)
交互次数是无限,需要依靠我们构造 iMessage
变量进而得到 flag。 flag2
作为 AES 的密钥,置于 iMessage
的后半部分,其位置是可以根据我们输入而改变的,而 flag1
相对固定在前面。
第一次尝试,首先观察到 flag 头为 D0g3{
,这就占据了 5 个长度,后面 flag1
剩下的三个长度可以尝试爆破。
1 2 iMessage = '00000000D0g3{xxx00000000'
其中 xxx 为爆破范围,这样可以得到 flag1
。但由于题目是交互的爆破的速度很慢,可以尝试多线程进行爆破,但是在比赛中并没有想到。
第二次尝试,从 flag2
入手,前面说了位置可变也就是说 flag2
可以单字节爆破。
1 2 iMessage = '00000000' + '000000000000000' + 'x' + '000000000000000'
可以看出分块进行 AES 之后,第二块和第三块是可以进行爆破的,只需不断地改变第二块的的内容即可爆破出 flag2
的内容。
得到 flag2
之后将 flag2
作为密钥即可解得 flag1
# exp1 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 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 from pwn import *from Crypto.Util.number import *import gmpy2import stringimport itertoolsfrom tqdm import tqdmimport hashlibfrom tqdm import tqdmfrom Crypto.Cipher import AEStable = string.ascii_letters + string.digits ip = '120.78.131.38' port = 10086 r = remote(ip,port) def proof (): r.recvuntil(b'SHA256(XXXX' ) line = r.recvline()[:-1 ].decode() print (line) tmp = line[line.find(' + ' ) + 3 :line.find(')' )] print (tmp) aim = line[line.find(':' ) + 1 :] print (aim) for i in itertools.product(table,repeat=4 ): ans = '' .join(i) if hashlib.sha256((ans + tmp).encode()).hexdigest() == aim: print (ans) r.recvuntil(b'Give Me XXXX:\n' ) r.sendline(ans.encode()) return proof() fleg = '' for i in tqdm(range (1 ,16 )): for t in table: payload = '0' * 8 + '0' * (16 - i) + fleg + t + '0' * (16 - i) r.recvuntil(b'You can input anything:\n' ) r.send(payload.encode()) r.recvuntil(b"Here is your cipher: b'" ) cipher = r.recvuntil(b"'" )[:-1 ] p = [cipher[i : i + 32 ] for i in range (0 ,len (cipher),32 )] if p[1 ] == p[2 ]: fleg += t print (fleg) break fleg += '}' key = fleg.encode() cipher = bytes .fromhex('ee05ba9cf6a3fe5d0681741d0206e9ce7b287f063c78b2d1775af6b4d4768cca' ) aes = AES.new(key=key,mode=AES.MODE_ECB) flag1 = aes.decrypt(cipher)[:8 ] flag = flag1 + key print (flag)r.interactive()
# cry3附件
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 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 import binasciiimport hashlibimport osimport randomimport socketserverimport stringfrom Crypto.Cipher import AESfrom Crypto.Util.Padding import padfrom Crypto.Util.number import getPrime, bytes_to_longhint = '' flag = b'' key = os.urandom(16 ) GOD = [b'Whitfield__Diffie' ] def encrypt (message ): message = pad(message, 16 ) aes = AES.new(key, AES.MODE_CBC, iv=b'\x00' * AES.block_size) return binascii.hexlify(aes.encrypt(message)[-16 :]) Authentication = encrypt(b'Whitfield__Diffie' ) class CryptoPalace (socketserver.StreamRequestHandler): def proof1 (self ): random.seed(os.urandom(8 )) random_str = '' .join([random.choice(string.ascii_letters + string.digits) for _ in range (20 )]) str_sha256 = hashlib.sha256(random_str.encode()).hexdigest() self.request.sendall(('SHA256(XXXX + %s):%s\n' % (random_str[4 :], str_sha256)).encode()) self.request.sendall('Give Me XXXX:\n' .encode()) XXXX = self.request.recv(2048 ).strip() if hashlib.sha256((XXXX + random_str[4 :].encode())).hexdigest() != str_sha256: return False return True def proof2 (self ): self.request.sendall(b'You must prove your identity to enter the palace ' + Authentication + b'\n--> ' ) name = self.request.recv(1024 ) if name in GOD: self.request.sendall(b'You are not him!\n' ) return False if encrypt(name) != Authentication: self.request.sendall(b'Wrong, try again......\n' ) return False else : self.request.sendall(hint.encode() + b"\n" ) return True def DiffieEncrypt (self ): p, q = getPrime(1024 ), getPrime(1024 ) E = [getPrime(512 ) for _ in range (3 )] e1, e2, e3 = E[0 ] * E[1 ], E[0 ] * E[2 ], E[1 ] * E[2 ] n = p * q m = bytes_to_long(flag) c1, c2, c3 = pow (m, e1, p * q), pow (m, e2, p * q), pow (m, e3, p * q) return n, e1, e2, e3, c1, c2, c3 def handle (self ): if not self.proof1(): self.request.sendall(b'Error Hash!' ) return if not self.proof2(): self.request.sendall(b'You did not enter the palace\n' ) return self.request.sendall(b'Flag has been encrypted by Diffie\n' ) cipher = self.DiffieEncrypt() self.request.sendall(str (cipher).encode()) return class ThreadedTCPServer (socketserver.ThreadingMixIn, socketserver.TCPServer): pass if __name__ == '__main__' : host, port = '127.0.0.1' , 10010 server = ThreadedTCPServer((host, port), CryptoPalace) ThreadedTCPServer.allow_reuse_address = True ThreadedTCPServer.allow_reuse_port = True server.serve_forever()
也是 AES 构造题,名字的长度为 17,刚好比一组多一点点,由于是 CBC 模式,多了一个异或。下图是 CBC 模式基本的逻辑。
观察 encrypt
函数,他只输出最后一组密文,也就是 16 长度,给出的 Authentication
是明文为 Whitfield__Diffie
的值,也就是第二块密文的值,两块明文分别为 [b'Whitfield__Diffi',b'e\x0f\x0f\x0f\x0f\x0f\x0f\x0f\x0f\x0f\x0f\x0f\x0f\x0f\x0f\x0f']
,只需构造一组 payload 让最后一组密文与 Authentication
相等即可。
由于是异或,于是想到再构造两组明文,使得在有 iv 的情况下与直接输入 Whitfield__Diffie
的值相同,即可得到 payload
1 2 tmp = bytes .fromhex('f95651580444fa622eacbccf9770b3ff' ) payload = b'Whitfield__Diffie' + b'\x0f' * 15 + xor(b'Whitfield__Diffi' ,tmp) + b'e'
过了 proof2,之后的共模就没什么问题了,缝合题实锤了
# exp1(pass proof2)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 import stringimport itertoolsfrom tqdm import tqdmimport hashlibfrom tqdm import tqdmfrom Crypto.Cipher import AESfrom pwn import *from Crypto.Util.number import *import gmpy2table = string.ascii_letters + string.digits ip = '120.78.131.38' port = 10010 r = remote(ip,port) def proof (): r.recvuntil(b'SHA256(XXXX' ) line = r.recvline()[:-1 ].decode() print (line) tmp = line[line.find(' + ' ) + 3 :line.find(')' )] print (tmp) aim = line[line.find(':' ) + 1 :] print (aim) for i in itertools.product(table,repeat=4 ): ans = '' .join(i) if hashlib.sha256((ans + tmp).encode()).hexdigest() == aim: print (ans) r.recvuntil(b'Give Me XXXX:\n' ) r.sendline(ans.encode()) return proof() r.recvuntil(b'--> ' ) tmp = bytes .fromhex('f95651580444fa622eacbccf9770b3ff' ) payload = b'Whitfield__Diffie' + b'\x0f' * 15 + xor(b'Whitfield__Diffi' ,tmp) + b'e' print ([payload[i:i + 16 ] for i in range (0 ,len (payload),16 )])r.send(payload) r.interactive()
# exp2(common attack)1 2 3 4 5 6 7 8 9 10 11 12 13 14 from Crypto.Util.number import *import gmpy2def commonattack (n,e1,e2,c1,c2 ): ee,r,s = gmpy2.gcdext(e1,e2) c = pow (c1,r,n) * pow (c2,s,n) % n return c,ee n,e1,e2,e3,c1,c2,c3 = (16486440901534735026079416493978454741894450403370110556952100122086322920105571130678250641915372185243929998120476723068695888574253004920214978656633731730934749677776991026227603288006133099065288831437504027746249576607479919237472214622575391394869006854656815062954603003816034710663602456213441098153480223142198387502026080500041270898650584914586278372694499229218974168098064855994816922143612998429807879165810732197097573046444802021890672546454283234079813058136359660986111588178104564665665995304715176338309871165836308611716088141650281105068996898382923846380193870192761814739770139653510966104751 , 65802495601435430884014164157983149653203234623955505267125287361071963315770434981892489974384879593873475142501241145601723614323897723390501569002238572356829910984628103822201481335709487416159863618946979440659041547154114671899486525434770880129425262757246368430750708258408326309187727652234815585419 , 84052869410714090640770403216113230839277321067057519586163704644665358725739224991736973759295254145044042178560300574832616468703956602526618738222660187695293011922181988315448711453435542605573956962547119211022419616008368704660669748535610493674183565028329838917447904176113207220156826317340699437593 , 100371728547256384949982888634783641299739616870044358688489836785922608820763172440149617632810996373515955734547335005130521600356592560907659378731935960310234428511482927378131465278097987481799823820647036963781201324775423352332305006477403812596207497116554664505759556095955969342792848402226512368627 , 15261004370119214706959515968030854191676456800616973538461959351992471464540836687005984690248717559047758489987075986638520702850349454321186989410905549534938155294092893987905452866242184643209787138089818105935430742879317741198594417901200437260065347672412023361258620469658844098562410561877333794386845203425148899503389833324831538953801957720002237277374463904872142541483848759043201174370604819350967351480308608451984840477429244165300686159356916664991123401159451171030927965396910313264155646628985987191742297420283292174316356246349668749837737731953899269189198217168274383101947026425204456838160 , 71541617104238844958487883021039355278444019837455251172339485579078558869398357227856788593394214298852960639376075294395532407694214339231233954736459666326541522892558895015959786142034356758961868446449789420201526410878223759959717267639969116877402196009127044915222666560782895827404862951164041265622628489520482847258656360871516832377682501732333658245587098059235130566229239937380443671627999879470372170836594286189086444701729878229745439484364124684303687046151033508502467956620647993774042769260686510115761978384809134836689414925273265255556855227957345350020071650407106382453330165304050197474 , 4766757679370684627017583733506550957309188382850543696266351427239582260983573264265386195971548917594018584776176071576579345048652554955857423115095296425367939489411211052506084195876524626560037042894405386200124478821492919219428968603842929228479615125568877310704339398411026343270768242395465278977326077353312529870220454930150817570343099012869314391994452805895801588178295809919103392518876039767473895127244168371888478145986283201565121940213119882431394835910683821456518678860431760302804169923498220501378863302643422731675387021209640484519081168169948208503539321061060436845781388861085417706035 ) cc1,ee1 = commonattack(n,e1,e2,c1,c2) cc2,ee2 = commonattack(n,e1,e3,c1,c3) m,_ = commonattack(n,ee1,ee2,cc1,cc2) print (long_to_bytes(m))