[writeup] ABCTF Encryption Service — 140
Hế lô anh em.
Sau đây mình xin trình bày về cách mà bọn mình đã deal với bài này.
netcat phát
Hmm, vậy là server sẽ encrypt data mình gửi lên, và data phải ở dạng hex-encoded.
Đề bài họ đã cho sẵn source. Click thôi
Đập ngay vào mắt mình là
from Crypto.Cipher.AES import AESCipherVậy thì server đã dùng AES để mã hóa data mình gửi lên, vậy rất có thể FLAG sẽ nằm trong Key, hoặc FLAG sẽ có dạng :
encrypt_data = sent_data + FLAGOk, tiếp tục
Hmm, padding 16, vậy đây là AES ECB với block là 16 byte, chi tiết này có vẻ quan trọng :v
Nhìn vào hàm encrypt() nhìn thấy ngay FLAG is not KEY, và FLAG sẽ được gắm vào data mình gửi rồi sau đó sẽ encrypt rồi loằn ngoằn sau đó trả về cho mình ciphertext.
Với kiểu block cipher thế này, thì mình có thể dễ dàng tìm được độ dài của FLAG. Bằng cách thử truyền từng byte một lên server.
Nhìn thấy ngay, khi truyền 3byte ’41h’ lên thì độ dài cipher là 2 block 16byte, truyền lên 4byte ’41h’ thì độ dài cipher tăng thêm 1 block. –>
3*16 = len(‘ENCRYPT:’) + 4 +len(FLAG) + 16byte padding
3*16= 8 +4 + len(FLAG) +16 byte padding
— > FLAG length =20 byte
Mình chỉ có thể truyền vào byte mà không thể đụng chạm gì vào source….vậy làm sao để mò đc FLAG.?
Mất khá nhiều thời gian để tụi mình tìm ra phương pháp. Cơ sở chính vẫn là dựa vào ECB
Vì các block đều được mã hóa giống nhau, vậy mình có thể padding để kiếm các block giống nhau rồi so sánh được không? Chắc chắn được.
Block đầu tiên có đoạn ‘ENCRYPT:’ (8 byte từ giờ mình sẽ gọi là 8E) vậy mình phải truyền 8E vào block phía sau để có 2 block giống nhau.
ví dụ:
Block 1: 8E + \x41\x41\x41\x41\x41\x41\x41\x41
Block2: 8E + \x41\x41\x41\x41\x41\x41\x41\x41
Hmmm, Python tí
Chạy thử thì thấy kết quả như này
cd7985389a47184ce3f957b15a1c45f3
cd7985389a47184ce3f957b15a1c45f32 block đem ra thí nghiệm cho cipher như nhau. Vậy nếu thử
Block 1: 8E + \x41\x41\x41\x41\x41\x41\x41 + 1 kí tự brute force
Block2: 8E + \x41\x41\x41\x41\x41\x41\x41 + FLAG[0]
Tại sao là FLAG[0]? — Vì block 2 đã đủ 16byte rồi, nên phần còn lại của flag sẽ nằm trong block tiếp theo.
-Kí tự mình brute force nó là 1 phần của FLAG vậy nó sẽ nằm trong các kí tự đọc được
Khoảng 96 kí tự NHANH THÔI< NHANH THÔI :v :V :V
Thử sửa code để chạy trong trường hợp này.
Flag có dạng ABCTF{xxx} vậy mình có expectation đầu tiên là khi bruteforce đến kí tự ‘a’ hoặc ‘A’ thì 2 block sẽ giống nhau.
Chạy phát
hmm đợi chờ là hạnh phúc
Ơn zời, cậu đây rồi.
Vậy thuật toán là ok, chính xác rồi. Sau khi có byte đầu tiên, mình có thể tìm được các byte tiếp theo của FLAG bằng cách giảm số byte ‘\x41‘ truyền vào.
Vấn đề còn lại là, nếu chỉ xử lý với 2 block liên tục như thế này, thì FLAG lấy được sẽ chỉ là 8byte đầu của FLAG, trong khi FLAG bao gồm 20 byte :o. Vậy mình phải tăng số block mà mình brute force lên.FLAG cũng không quá dài, nên mình tăng phạm vi thêm 2block nữa
Block 1+2+3: 8E + \x41*39 + kí tự bruteforce
Block 4+5+6: 8E + \x41*(39-(len(FLAG)))
Improve code cho trường hợp tổng quát này:
ABCTF{p4dding_4_fun}
Với code như này, số các trường hợp phải thử sẽ là nO(n) nên nó cũng không mất quá nhiều thời gian để quét hết các trường hợp nên mình có thể để nó chạy dần dần và ngồi đợi thôi :v
Tệ nhất thì mình cũng chỉ phải chạy len(t)*len(FLAG)*sleeptime ở đây nó rơi vào tầm 768 giây. Nghĩa là rơi vào tầm 13 phút, cũng không quá lâu nhỉ
Thanks for your time :v
-H3xX0r-
