[BSidesSF2020]haystack
题目
This vendor claims they have figured out a way to preserve the integrity and confidentiality of a message using signing instead of encryption. We only have a binary pycache file and a message off the wire – can you find the content of the message?
解题
该供应商声称,他们已经找到了一种使用签名而不是加密来保护消息完整性和机密性的方法。我们只有一个二进制pycache文件和一条离线消息——你能找到消息的内容吗?
pyc文件是python程序编译生成的字节码文件,uncompyle工具或者在线反编译工具可以很容易将pyc文件反编译,通过加入混淆指令可以达到在不影响运行的情况下欺骗过反编译工具的目的,从而达到保护目的。混淆的方法可以是加入一些无意义的跳转分支,错误的语句使反编译工具工作失败,但又在跳转语句的作用下跳过了执行。
这道题应该并没有加入混淆指令,先来下载uncompyle工具😂
最方便的就是使用pip安装 pip install uncompyle uncompyle6 --help 查看帮助 uncompyle6 models.pyc > models.py 将models.pyc反编译成py文件 uncompile -o . *.pyc 将当前文件夹中所有的pyc文件反编译成后缀名为.pyc_dis的源文件
反翻译得到:
import hmac, hashlib, random, struct
CHAFF_SIZE = 32
SIG_SIZE = 16
ALL_BYTES = set((c for c in range(256)))
KEY = 'af5f76f605a700ae8c0895c3e6175909'
def byte(v):
return bytes([v])
def sign_byte(val, key):
return hmac.new(key,
val, digestmod=(hashlib.sha256)).digest()[:SIG_SIZE]
def chaff_byte(val, key):
msgs = {}
msgs[val[0]] = sign_byte(val, key)
while len(msgs) < CHAFF_SIZE:
vals = list(ALL_BYTES - set(msgs.keys()))
c = random.choice(vals)
if c == val:
raise ValueError('Chose duplicate!')
fake_sig = bytes(random.choices((list(ALL_BYTES)), k=SIG_SIZE))
msgs[c] = fake_sig
pieces = []
for k, v in msgs.items():
pieces.append('%s%s' % (byte(k), v))
random.shuffle(pieces)
return ''.join(pieces)
def chaff_msg(val, key):
if not isinstance(val, bytes):
val = val.encode('utf-8')
msg_out = []
for b in val:
msg_out.append(chaff_byte(byte(b), key))
outval = ''.join(msg_out)
return struct.pack('>I', len(val)) + outval
def winnow_msg(val, key):
if not isinstance(val, bytes):
val = val.encode('utf-8')
msglen = struct.unpack('>I', val[:4])[0]
val = val[4:]
chunk_len = (SIG_SIZE + 1) * CHAFF_SIZE
expected_len = chunk_len * msglen
if len(val) != expected_len:
raise ValueError('Expected length %d, saw %d.' % (expected_len, len(val)))
pieces = []
for c in range(msglen):
chunk = val[chunk_len * c:chunk_len * (c + 1)]
res = winnow_byte(chunk, key)
pieces.append(res)
return ''.join(pieces)
def winnow_byte(val, key):
while val:
c = byte(val[0])
sig = val[1:SIG_SIZE + 1]
if sign_byte(c, key) == sig:
return c
val = val[SIG_SIZE + 1:]
raise ValueError('No valid sig found!')
def main():
inp = 'This is a test message!'
msg = chaff_msg(inp, KEY)
ret = winnow_msg(msg, KEY)
if inp != ret:
print('Wrong ret: %s' % ret)
if __name__ == '__main__':
main()
解题的话,来看一下大佬的代码吧:
import hmac, hashlib, random, struct
from collections import Counter
CHAFF_SIZE = 32
SIG_SIZE = 16
ALL_BYTES = set((c for c in range(256)))
def extract(val):
if not isinstance(val, bytes):
val = val.encode(‘utf-8‘)
msglen = struct.unpack(‘>I‘, val[:4])[0]
val = val[4:]
chunk_len = (SIG_SIZE + 1) * CHAFF_SIZE
expected_len = chunk_len * msglen
if len(val) != expected_len:
raise ValueError(‘Expected length %d, saw %d.‘ % (expected_len, len(val)))
pieces = []
for c in range(msglen):
chunk = val[chunk_len * c:chunk_len * (c + 1)]
res = extract_byte_sig_pairs(chunk)
pieces.extend(res)
return pieces
def extract_byte_sig_pairs(val):
res = []
while val:
c = (val[0])
sig = val[1:SIG_SIZE + 1]
res.append((c, sig))
val = val[SIG_SIZE + 1:]
return res
msg = open(‘1.txt‘, ‘rb‘).read()
ret = extract(msg)
c = Counter(ret)
real = c.most_common(256)
print(real)
def decode(val, d):
if not isinstance(val, bytes):
val = val.encode(‘utf-8‘)
msglen = struct.unpack(‘>I‘, val[:4])[0]
val = val[4:]
chunk_len = (SIG_SIZE + 1) * CHAFF_SIZE
expected_len = chunk_len * msglen
if len(val) != expected_len:
raise ValueError(‘Expected length %d, saw %d.‘ % (expected_len, len(val)))
pieces = []
for c in range(msglen):
chunk = val[chunk_len * c:chunk_len * (c + 1)]
res = decode_byte(chunk, d)
pieces.append(res)
return b‘‘.join(pieces)
def decode_byte(val, d):
while val:
c = (val[0])
sig = val[1:SIG_SIZE + 1]
if sig in d and d[sig] == c:
return c
val = val[SIG_SIZE + 1:]
raise ValueError("WTF")
d = {s: b for (b, s), c in real}
print(‘\n‘)
print(‘\n‘)
print(‘\n‘)
print(d)
print(decode(msg,d))
运行得到:
|