[BSidesSF2020]haystack
[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文件和一條離線消息——你能找到消息的內容嗎?
pcap文件是常用的數據報存儲格式,可以理解為就是一種文件格式,只不過里面的數據是按照特定格式存儲的,所以我們想要解析里面的數據,也必須按照一定的格式。普通的記事本打開pcap文件顯示的是亂碼,用安裝了HEX-Editor插件的Notepad++打開,能夠以16進制數據的格式顯示,用wireshark這種抓包工具就可以正常打開這種文件,愉快地查看里面的網絡數據報了,同時wireshark也可以生成這種格式的文件。當然這些工具只是我經常使用的,還有很多其它能夠查看pcap文件的工具。
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的源文件
反翻譯得到:
# uncompyle6 version 3.7.4 # Python bytecode 3.7 (3394) # Decompiled from: Python 3.8.7 (tags/v3.8.7:6503f05, Dec 21 2020, 17:59:51) [MSC v.1928 64 bit (AMD64)] # Embedded file name: /home/david/Projects/BSidesCTF/2020/challenges/haystack/challenge/chaffing.py # Compiled at: 2020-01-22 09:37:26 # Size of source mod 2**32: 2094 bytes 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_sigpieces = []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)) + outvaldef 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_SIZEexpected_len = chunk_len * msglenif 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 cval = 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() # okay decompiling chaffing.pyc然后讀程序:
set() 函數創建一個無序不重復元素集,可進行關系測試,刪除重復數據,還可以計算交集、差集、并集等。
isinstance() 函數來判斷一個對象是否是一個已知的類型,類似 type()。
hmac與hashlib類似,這兩個都是Python中加密的函數,都是不可逆的加密,hashlib模塊實現了sha1,sha224,sha256,sha384,sha512,md5等算法,交易所的加密中一般使用sha256或md5。hmac模塊需要一個msg來進行加密,hashlib并沒有msg這個參數。
random.shuffle() 隨機打亂序列
解題的話,來看一下大佬的代碼吧:
#exp.py 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_SIZEexpected_len = chunk_len * msglenif 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 piecesdef 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 resmsg = 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_SIZEexpected_len = chunk_len * msglenif 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 cval = 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))其中:
struct.unpack(fmt,string)
顧名思義,解包。比如pack打包,然后就可以用unpack解包了。返回一個由解包數據(string)得到的一個元組(tuple), 即使僅有一個數據也會被解包成元組。其中len(string) 必須等于 calcsize(fmt),這里面涉及到了一個calcsize函數。struct.calcsize(fmt):這個就是用來計算fmt格式所描述的結構的大小。
但是這個代碼是存在錯誤的,比如我們并沒有1.txt,但是我們還有一個名為message.pcap的文件,所以用這個替換1.txt,然而依然有錯誤,運行出來:ValueError: Expected length 1941859702304, saw 777384.也就是說,len(val) != expected_len。
總結
以上是生活随笔為你收集整理的[BSidesSF2020]haystack的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: [pasecactf_2019]torn
- 下一篇: 2021-09-15