server.py
#!/usr/bin/python
# -*- coding: utf-8 -*-
from Crypto.Cipher import AES
from Crypto.Util.strxor import strxor
from Crypto.Random import get_random_bytes
from FLAG import flag
class MAC:
def __init__(self):
self.key = get_random_bytes(16)
self.iv = get_random_bytes(16)
def pad(self, msg):
pad_length = 16 - len(msg) % 16
return msg + chr(pad_length) * pad_length
def unpad(self, msg):
return msg[:-ord(msg[-1])]
def code(self, msg):
res = chr(0)*16
for i in range(len(msg)/16):
res = strxor(msg[i*16:(i+1)*16], res)
aes = AES.new(self.key, AES.MODE_CBC, self.iv)
return aes.encrypt(res).encode('hex')
def identity(self, msg, code):
if self.code(msg) == code:
msg = self.unpad(msg)
if msg == 'please send me your flag':
print 'remote: ok, here is your flag:%s' % flag
else:
print 'remote: I got it'
else:
print 'remote: hacker!'
if __name__ == '__main__':
mac = MAC()
message = 'see you at three o\'clock tomorrow'
print 'you seem to have intercepted something:{%s:%s}' %(mac.pad(message).encode('hex'), mac.code(mac.pad(message)))
print 'so send your message:'
msg = raw_input()
print 'and your code:'
code = raw_input()
mac.identity(msg.decode('hex'), code)
exit()
这里主要用到一个如下原理:
C = A xor B。若我们想得到X,则有以下推导
C = A xor B ==>> C xor A xor B = 0 ===>> C xor A xor B xor X = X
而整个解密过程中,B(密文)是我们可以控制的,A由于key未知,而无法准确控制,C是原始的明文,在输出端,无法控制。
所以,我们可以控制B,让B首先变成C xor B xor X (这里的C是指原始的明文),这样最终A与B的异或操作就能变成X
参考文献: http://momomoxiaoxi.com/2016/12/08/WebCrypt/
https://www.anquanke.com/post/id/158233
在这道题中我们进行伪造,伪造主要绕过一下两个条件:
- 使用code()的函数加完密的要等于你自己提供的code的值
- 使用unpad(msg.decode('hex'))消除填充之后,必须等于:please send me your flag
进行伪造:
题目中会给出两组数据,如下图所示:
伪造条件如下:
- 构造 A xor B,也就是:
1.A的每十六字节“异或”并使用pad()填充:
msg=('706c656173652073656e64206d6520796f757220666c6167'+'8'*16).decode('hex')
print c
res = chr(0)*16
for i in range(len(msg)/16):
res = strxor(msg[i*16:(i+1)*16], res)
- B的每十六进制“异或”并使用pad()填充“”
res1 = chr(0)*16
msg1=('73656520796f75206174207468726565206f27636c6f636b20746f6d6f72726f77'+'0f'*15).decode('hex')
for i in range(len(msg1)/16):
res1 = strxor(msg1[i*16:(i+1)*16], res1)
3.最终将他们异或 A xor B:
last_res = strxor(res1, res).encode('hex')
-
构造第一个输入:
1.由前一组16位和后一组16位异或:
2.我们只要构造(A xor B) xor A =B:
(A xor B):
last_res = strxor(res1, res).encode('hex')
A:
msg
payload = (msg.encode('hex')+last_res).ljust(124,"a")+'27'
- 绕过 msg = self.unpad(msg)的检测:
- 可以看出:他用来填充的单位使用的是16的差值.
def pad(self, msg):
pad_length = 16 - len(msg) % 16
return msg + chr(pad_length) * pad_length
2.去除填充函数,通过最后一位的数值,进行去除,这里可以伪造:
'''
def unpad(self, msg):
return msg[:-ord(msg[-1])]
'''
3.伪造内容在最后加一个数值,数值等于去掉“please send me your flag”之后,即可绕过保护。
代码我借用一下别人的,代码如下:
from pwn import *
from Crypto.Util.strxor import strxor
p=remote('47.240.41.112',12345)
p.recvuntil('0f:')
c = p.recv(32)
msg=('706c656173652073656e64206d6520796f757220666c6167'+'8'*16).decode('hex')
print c1
res = chr(0)*16
for i in range(len(msg)/16):
res = strxor(msg[i*16:(i+1)*16], res)
res1 = chr(0)*16
msg1=('73656520796f75206174207468726565206f27636c6f636b20746f6d6f72726f77'+'0f'*15).decode('hex')
for i in range(len(msg1)/16):
res1 = strxor(msg1[i*16:(i+1)*16], res1)
last_res = strxor(res1, res).encode('hex')
payload = (msg.encode('hex')+last_res).ljust(124,"a")+'27'
print payload
p.recvuntil("so send your message:\n")
p.sendline(payload)
p.recvuntil("and your code:\n")
p.sendline(c)
p.interactive()
参考网站:
https://www.jianshu.com/p/02ef24117d7b
。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
基础知识:
异或两个bytes
from Crypto.Util.strxor import strxor
strxor(b'abc', b'def')
参考网站:
https://www.jianshu.com/p/02ef24117d7b
https://code.felinae98.cn/ctf/crypto/python%E4%B8%AD%E7%9A%84crypto%E5%9C%A8ctf%E4%B8%AD%E7%9A%84%E5%BA%94%E7%94%A8/