import socketserverimport os, sys, signalimport string, random, time, jsonfrom hashlib import sha256from secret import flagfrom Crypto.Cipher import AESfrom Crypto.Util.Padding import pad,unpadfrom ast import literal_eval as eval key = os.urandom(32 ) def decrypt (ciphertext:str ,iv ): try : iv = bytes .fromhex(iv) ciphertext = bytes .fromhex(ciphertext) cipher = AES.new(key, AES.MODE_CBC, iv) decrypted = unpad(cipher.decrypt(ciphertext),16 ) if not b'Admin' in decrypted: return 'Permissino denied !' except ValueError as e: return str (e) return decrypted def encrypt (plaintext:str ,admin=False ): try : iv = os.urandom(16 ) payload = {'permission' :'Guest' ,'Time' :f'{time.time()} ' ,'data' :plaintext} print (payload) payload = pad(json.dumps(payload).encode(),16 ) cipher = AES.new(key, AES.MODE_CBC, iv) encrypted = cipher.encrypt(payload) except ValueError as e: return str (e) return iv.hex () + encrypted.hex () class Task (socketserver.BaseRequestHandler): def _recvall (self ): BUFF_SIZE = 2048 data = b'' while True : part = self.request.recv(BUFF_SIZE) data += part if len (part) < BUFF_SIZE: break return data.strip() def send (self, msg, newline=True ): if type (msg) is str : msg = msg.encode() try : if newline: msg += b'\n' self.request.sendall(msg) except : pass def recv (self, prompt=b'> ' ): self.send(prompt, newline=False ) return self._recvall() def close (self ): self.send(b"Bye~" ) self.request.close() def proof_of_work (self ): random.seed(os.urandom(8 )) proof = '' .join([random.choice(string.ascii_letters+string.digits) for _ in range (20 )]) _hexdigest = sha256(proof.encode()).hexdigest() self.send(f"sha256(XXXX+{proof[4 :]} ) == {_hexdigest} " .encode()) x = self.recv(prompt=b'Give me XXXX: ' ) if len (x) != 4 or sha256(x+proof[4 :].encode()).hexdigest() != _hexdigest: return False return True def handle (self ): if 0 and not self.proof_of_work(): return menu = '''1. Encrypt data\n2. Decrypt data\n3. Get encrypted flag\n''' while 1 : self.send('\n' + menu) try : r = int (self.recv()) except : continue if r == 3 : self.send(b'Encrypted flag:\n' ) self.send(encrypt(flag.hex ())) elif r == 2 : iv,data = self.recv(prompt=b'Data to decrypt:' ).strip().split(b'||' ) self.send(decrypt(data.decode(),iv.decode())) elif r == 1 : data = self.recv(prompt=b'Data to encrypt:' ).strip() self.send(encrypt(data.decode())) self.close() class ThreadedServer (socketserver.ThreadingMixIn, socketserver.TCPServer): pass class ForkedServer (socketserver.ThreadingMixIn, socketserver.TCPServer): pass if __name__ == "__main__" : HOST, PORT = '0.0.0.0' , 10000 server = ForkedServer((HOST, PORT), Task) server.allow_reuse_address = True server.serve_forever()
思路分析
对代码进行分析,发现代码当中主要有3
个关键函数。
proof_of_work()
这个函数在程序里没起到作用,不知道是不是复现的时候师傅们把这个功能修改了。
encrypt()
这个函数会首先将我们传入的明文,修改成如下形式:{'permission':'Guest','Time':f'{time.time()}','data':plaintext}
然后采用CBC
的方式对修改后的payload
进行加密,并且返回IV
和密文。
decrypt()
这个函数会根据我们传入的密文和IV
对密文内容进行解密,但是这里要求我们传入密文解密后得到的明文一定满足'permission':'Admin'
·
Encrypted flag
会调用encrypt()
这个函数对我们想要的flag
进行加密。
经过对代码的分析,现在我们的思路很明确,考点就是CBC字节反转
.
我们首先调用Encrypted flag
得到加密后的密文,那么这时候的payload
是{'permission':'Guest','Time':f'{time.time()}','data':flag}
我们将这样加密后的密文,直接送去解密,肯定会返回错误,我们需要通过字节反转,将这里的Guest
修改成Admin
解题过程
Encrypted flag: 6af6da83fa4b83f1e6dd21da8267c6f70b8c817ed8b15688ad751e1717c8596465bba256789656c4d2e7a9f37cec3fba3710d47c7af313979739347140aa2a080841ff385e49d82502bc6a153e2b7f7b161fc1d3732e4b7b509cc1be3928d9f107edd36b27f2319e7b72e35665d589ed689f3055740bf8ed5bcb6b5ca9f8e592ea1da515841fb19f597ba50e9eb239d6f14757df3d6e080317915da69b394e61d13353248bac03fd6efd68a09bb83ca3 IV=6af6da83fa4b83f1e6dd21da8267c6f7 C=0b8c817ed8b15688ad751e1717c8596465bba256789656c4d2e7a9f37cec3fba3710d47c7af313979739347140aa2a080841ff385e49d82502bc6a153e2b7f7b161fc1d3732e4b7b509cc1be3928d9f107edd36b27f2319e7b72e35665d589ed689f3055740bf8ed5bcb6b5ca9f8e592ea1da515841fb19f597ba50e9eb239d6f14757df3d6e080317915da69b394e61d13353248bac03fd6efd68a09bb83ca3
关于字节反转的详细思路,可以参考这篇cryptohack的博文 ,我们这里直接进行操作。
from Crypto.Util.number import *import binasciifrom pwn import xoradmin = b'Admin\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' guest = b'Guest\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' c_hex = 'dbca2f27304d1311453f623acab96c56b75fad2e760031bb74943d2419a681d101bfb6ec5e6e141e1e2c3b8acc9456b9aacdcfded5049814e0abc2d112a46e5219ee30460564df68981d07ca744ff9601a50f2c331fc0023edb71ea80649daabc7bdf7e7264f13dfeffeadb037e4a33bf96eb2e524ea3f175ff3e2ee753b08e122cb7764dece1bf93319c206e58e94fb96f2239583fd1a82d9beb20f01b09aaada6f78da43d835e13b5934596f60abcf' iv = c_hex[:32 ] c_hex = c_hex[32 :] c = binascii.a2b_hex(c_hex) fake_c = xor(admin, c[:16 ]) fake_c = xor(fake_c, guest) fake_hex = str (binascii.b2a_hex(fake_c))[2 :-1 ] + c_hex[32 :] print (iv + "||" + fake_hex)flag = "4e53534354467b42724f6e59615f5a61596348694b5f42726f6e79615f42726f6e79615f42726f6e79617d" print (long_to_bytes(int (flag, 16 )))