Web狗做不来Misc所以把Crypto给AK了
happy
一开始题目上错附件浪费快两个小时,有点无语
1 2 3 4 ('c=' , '0x7a7e031f14f6b6c3292d11a41161d2491ce8bcdc67ef1baa9eL' ) ('e=' , '0x872a335' )
a = q + q ∗ p 3 a = q+q*p^3 a = q + q ∗ p 3
b = q ∗ p + q ∗ p 2 b = q*p+q*p^2 b = q ∗ p + q ∗ p 2
x = g c d ( a , b ) x = gcd(a,b) x = g c d ( a , b )
a b = q + q ∗ p 3 q ∗ p + q ∗ p 2 = 1 + p 3 p + p 2 = a / x b / x \frac{a}{b} = \frac{q+q*p^3}{q*p+q*p^2} = \frac{1+p^3}{p+p^2} = \frac{a/x}{b/x} b a = q ∗ p + q ∗ p 2 q + q ∗ p 3 = p + p 2 1 + p 3 = b / x a / x
因式分解
1 + p 3 p + p 2 = ( 1 + p ) ∗ ( p 2 − p + 1 ) p ∗ ( 1 + p ) = p 2 − p + 1 p \frac{1+p^3}{p+p^2} = \frac{(1+p)*(p^2-p+1)}{p*(1+p)} = \frac{p^2-p+1}{p} p + p 2 1 + p 3 = p ∗ ( 1 + p ) ( 1 + p ) ∗ ( p 2 − p + 1 ) = p p 2 − p + 1
这里b/x的值为素数,尝试作为p进行解密
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 import gmpy2from Crypto.Util.number import *c = 0x7a7e031f14f6b6c3292d11a41161d2491ce8bcdc67ef1baa9e e = 0x872a335 a = 1285367317452089980789441829580397855321901891350429414413655782431779727560841427444135440068248152908241981758331600586 b = 1109691832903289208389283296592510864729403914873734836011311325874120780079555500202475594 x = gmpy2.gcd(a, b) p = b // x q = b // (p + p ** 2 ) n = p * q phi = (p - 1 ) * (q - 1 ) d = gmpy2.invert(e, phi) m = pow (c, d, n) print(long_to_bytes(m))
Yusa的密码学课堂 CBC第一课
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 from Crypto.Cipher import AESimport osflag='flag{********************************}' BLOCKSIZE = 16 def pad (data ): pad_len = BLOCKSIZE - (len (data) % BLOCKSIZE) if len (data) % BLOCKSIZE != 0 else 0 return data + "=" * pad_len def unpad (data ): return data.replace("=" ,"" ) def enc (data,key,iv ): cipher = AES.new(key, AES.MODE_CBC, iv) encrypt = cipher.encrypt(pad(data)) return encrypt def dec (data,key,iv ): try : cipher = AES.new(key, AES.MODE_CBC, iv) encrypt = cipher.decrypt(data) return unpad(encrypt) except : exit() def task (): try : key = os.urandom(16 ) iv = os.urandom(16 ) pre = "yusa" *4 for _ in range (3 ): choice = raw_input(menu) if choice == '1' : name = raw_input("What's your name?" ) if name == 'admin' : exit() token = enc(pre+name,key,iv) print "Here is your token(in hex): " + iv.encode('hex' ) + token.encode('hex' ) continue elif choice == '2' : token = raw_input("Your token(in hex): " ).decode('hex' ) iv = token[:16 ] name = dec(token[16 :],key,iv) print iv.encode('hex' ) + name.encode('hex' ) if name[:16 ] == "yusa" *4 : print "Hello, " + name[16 :] if name[16 :] == 'admin' : print flag exit() else : continue except : exit() menu=''' 1. register 2. login 3. exit ''' if __name__ == "__main__" : task()
CBC Byte Flip
如图所示
我们要把plain2中的某一字节翻转为另一字节
由于C1来自于cipher2进行Block Cipher Decryption之后的结果
而且key未知,就不能直接得知C1的值
但是字节翻转的妙处在于通过修改上一组的密文来翻转下一组的明文,从而可以完全忽视这一点
由异或运算可以推导
B1 = A1 xor C1
那么也有C1 = A1 xor B1
B1’ = A1’ xor C1
(A1’是修改之后的密文字节,B1’是翻转之后的明文字节)
而如果我们能够修改cipher1,那么就能够修改A1的值
即A1’ = A1 xor B1 xor B1’
A1’ xor C1 = A1 xor B1 xor B1’ xor C1 = C1 xor B1’ xor C1 = B1’
用三条算式来表述上面的话就是
B = A xor C
B’ = A’ xor C
A’ = A xor B xor B’
字节翻转的要求也就显而易见了
对于A完全可控
已知B的值
到这里也只完成了一半
由于修改了A1
cipher1在进行BCD的时候会得出错误的结果
再与IV相异或则会导致plain1出错
如果能够得到修改A1之后产生的错误的plain1的值
而且IV可以完全控制的话
那么就能够把刚才的把戏再玩一遍
B2 = A2 xor C2
B2’ = A2’ xor C2
A2’ = A2 xor B2 xor B2’
A2为原IV
B2是错误的plain1
B2’是正确的plain1
BTW:每一组Cipher的长度为16Byte
以上大概介绍了CBC字节翻转的攻击方式
需要密文以及IV可控来改写明文
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 from pwn import *import binasciiimport timep = remote('das.wetolink.com' ,42888 ) p.recvuntil(b"3. exit\n" ) p.sendline(b"1" ) p.recv() p.sendline(b"Admin" ) data0 = p.recvline().decode() data0 = data0[28 :124 ] iv0 = data0[:32 ] cipher00 = data0[32 :64 ] cipher01 = data0[64 :96 ] replacement = str (hex (int (cipher00[:2 ], 16 ) ^ ord ("A" ) ^ ord ("a" )))[2 :] payload0 = iv0 + replacement + cipher00[2 :] + cipher01 p.recvuntil(b"3. exit\n" ) p.sendline(b"2" ) p.recv() p.sendline(payload0.encode()) data1 = p.recvline()[:-1 ].decode() plain1 = data1[32 :64 ] iv1 = str (hex (int (binascii.hexlify("yusa" .encode()).decode() * 4 ,16 ) ^ int (plain1, 16 ) ^ int (iv0, 16 )))[2 :] payload1 = iv1 + replacement + cipher00[2 :] + cipher01 p.recvuntil(b"3. exit\n" ) p.sendline(b"2" ) p.recv() p.sendline(payload1.encode()) p.recvuntil(b"admin\n" ) print(p.recvline())
Yusa的密码学课堂 ECB
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 from Crypto.Cipher import AESimport osBLOCKSIZE = 16 flag='flag{********************************}' def pad (data ): pad_len = BLOCKSIZE - (len (data) % BLOCKSIZE) if len (data) % BLOCKSIZE != 0 else 0 return data + chr (pad_len) * pad_len def unpad (data ): num = ord (data[-1 ]) return data[:-num] def enc (data,key ): cipher = AES.new(key, AES.MODE_ECB) encrypt = cipher.encrypt(pad(data)) return encrypt def dec (data,key ): try : cipher = AES.new(key, AES.MODE_ECB) encrypt = cipher.decrypt(data) return unpad(encrypt) except : exit() def task (): try : key = os.urandom(16 ) while True : plaintext = raw_input("Amazing function: " ).decode('hex' ) yusa = plaintext + flag print enc(yusa,key).encode('hex' ) except Exception as e: print str (e) exit() if __name__ == "__main__" : task()
这里的加密的明文为"输入的hex转字符"+“flag”+Padding
如果发送的明文为
aaaaaaaaaaaa
那么加密的明文为
aaaaaaaaaaaaflag{XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX}
分组Padding之后的结果为
aaaaaaaaaaaaflag {XXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX X}+Padding
分析pad函数我们可以知道Padding的值
而ECB的加密方式就是分组明文密文之间没有关联
那么我们就可以构造 “?}+Padding"的明文去加密,?作为掩码,flag内容长度为32字节,目测是md5,所以字符集目测是"0123456789abcdef”
然后我们在字符集里去爆破一位掩码,将得到的结果与加密结果的最后一个分组进行比较,若相同则我们得到了flag最后一位的字节
理解了上述内容之后就可以写一个循环来逐位爆破flag,每次初始填充的a多一位
a在变长的过程中需要注意明文长度会多出几个分组,从而导致密文也多出几个分组
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 from pwn import *import binasciiimport timeplain = "flag{********************************}" flag = "}" BLOCKSIZE = 16 char = '0123456789abcdef' p = remote('das.wetolink.com' ,42887 ) text = p.recv() for pad1 in range (12 , 46 ): p.sendline('61' *pad1) text = p.recvline() print(len (text)) if len (text) <= 147 : res = text.decode()[-33 :-1 ] elif len (text) <= 179 : res = text.decode()[-33 -32 :-1 -32 ] else : res = text.decode()[-33 -64 :-1 -64 ] pad2 = (26 -pad1)%16 for j in char: time.sleep(0.1 ) payload = str (hex (ord (j)))[2 :]+binascii.hexlify(flag.encode()).decode()+('0' +str (hex (pad2)[2 :]))*pad2 p.sendline(payload) text = p.recvline().decode() if res == text[18 :18 +32 ]: flag = j + flag print(flag) break flag = "flag{" + flag print(flag)