点击此处获得更好的阅读体验
题目考点
解题思路
本题提供源码,以交互的方式进行。开始交互时题目会在后台生成8-12个随机字符组成的name,然后生成随机的加密初始向量iv,之后利用未知的key进行CBC方式的AES对name加密,返回给我们iv和name的加密结果的hex编码。用户输入数据,服务器会以输入的前16字节作为新的iv对其余数据进行解密,如果解密出来的name是‘Admin’则给出flag。采用CBC的加密方式,可以想到的攻击方式有字节翻转,根据加密原理已知(pad(name) ^iv) = deAES(out)
要使(pad(payload) ^newiv) = deAES(out)
可得newiv = pad(name) ^iv ^pad(payload)
其中name未知,问题的关键在于求name。关键问题代码在此:
1 2 3 4 5 6
| def check_pad(s, block_size): assert len(s) % block_size == 0 assert ord(s[-1]) <= block_size for i in range(ord(s[-1])): assert s[-1-i] == s[-1] return s[:-1-i]
|
以及主程序的一段判断:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| try: print "padplain_text:" + plain_text plain_text = check_pad(plain_text, AES.block_size) print "plain_text:"+plain_text except: print "padding error" sys.stdout.flush() return if plain_text == 'Admin': print "Welcome admin, your flag is %s" % FLAG sys.stdout.flush() return else: print "YOU. SHALL. NOT. PASS!" sys.stdout.flush() return
|
问题在于check_pad中要求pad的每一位都要一样,在解密不为admin的情况下也会又两种情况,一种通过check_pad返回YOU. SHALL. NOT. PASS
,一种不通过返回padding error
,可以根据这两点的不同来逐位计算出name。
具体过程: 首先计算出位数,不同位数pad的值不一样,设定name = 'q'i,i取8-12位,payload = '~'15,计算newiv,只有取到正确的i时才可以过check_pad。 之后利用类似的方法由后至前逐位求出name,最后求出newiv,获得flag。脚本如下:
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
|
from pwn import * from Crypto.Cipher import AES def pad(s, block_size): return s + (block_size - len(s) % block_size) * chr(block_size - len(s) % block_size) if __name__ == "__main__": robj = remote("183.129.189.62", 19206) temporary = robj.recvline()[:-1] iv = str(temporary[-64: -32]) cipher = str(temporary[-32:]) temporary = robj.recvline() payload = '~'*15 for i in range(7, 13): name = 'q'*i iwt = int(iv, 16) ^ int(pad(payload, AES.block_size).encode('hex'), 16) ^ int(pad(name, AES.block_size).encode('hex'), 16) iwt = hex(iwt)[2:] + cipher robj.sendline(iwt) temporary = robj.recvline()[:-1] if 'padding error' not in temporary: break length = i the_true_name = '' for i in range(length): for j in range(33, 128): name = '~' * (length - 1 - i) + chr(j) + the_true_name payload = '~' * (len(name)-1 - i) iwt = int(iv, 16) ^ int(pad(payload, AES.block_size).encode('hex'), 16) ^ int( pad(name, AES.block_size).encode('hex'), 16) iwt = hex(iwt)[2:] + cipher robj.sendline(iwt) temporary = robj.recvline()[:-1] if 'padding error' not in temporary: the_true_name = chr(j) + the_true_name break user_role = "Admin" iwt = int(iv, 16) ^ int(pad(user_role, AES.block_size).encode('hex'), 16) ^ int(pad(the_true_name, AES.block_size).encode('hex'), 16) iwt = hex(iwt)[2:] + cipher robj.sendline(iwt) flag = robj.recvline()[:-1] print 'flag:' + str(flag)
|