flag_check – AceBear CTF 2019
Sau khi giải kết thúc thì mình mới làm xong bài này, giá như không chơi game trong lúc giải đang diễn ra thì chắc là có thể submit flag được rồi :v
Mới đầu giải nén cái file zip thấy mấy file quen quen
Nghĩ chắc quả này thơm kèo, khả năng được build bằng pyinstaller hoặc py2exe, liền vội vào kéo tool về decompile nhưng kết quả không ra gì 🙁 Đành phải dùng IDA để xem file exe có gì 🙁
Để ý ở hàm main
có sử dụng một chuỗi rất lạ là "NUITKA_TICKER"
Mình liền thử tìm google với từ khóa "nuitka python"
Đọc phần giới thiệu của nuitka là hiểu luôn, nhưng cái ngu nhất của mình là cứ nghĩ thằng nuitka chỉ là compile python code thành bytecode rồi gọi API để thực thi bytecode đó mà không để ý rằng: "Nuitka is a source to source compiler which compiles Python code to C/C++ executables, or C/C++ source code", điều này khiến cho mình tốn cực nhiều thời gian để tìm cho được đống bytecode đó nhưng thực ra là nó không tồn tại 🙁
Sau một khoản thời gian ngồi đọc source của nuitka (mà không hiểu nhiều lắm) và debug flag_check.exe thì mình quyết định build thử một chương trình bằng nuitka theo hướng dẫn http://nuitka.net/doc/user-manual.html. Mình nghĩ phương pháp này rất hiệu quả trong reverse.
Sau khi build xong thì ở thư mục hello.build có file module.main.c
chính là source code chưa hàm main
trong python
Mình có sử dụng thêm bindiff để đổi tên các hàm giống nhau cho dễ reverse, sử dụng compiler càng giống với file flag_check.exe thì số lượng hàm giống càng nhiều.
Cuối cùng là đọc hiểu lại cách nuitka biến hàm main
trong python thành C code, trong quá trình đọc hiểu và debug, mình phải lên https://github.com/cython/cython để đọc hiểu một số C API của python cũng như cấu trúc một số object type như tuple, unicode, long của python 3.6 để kiểm tra giá trị trong quá trình debug.
Đây là mã python của flag_check mà mình tạo, tư tưởng thuật toán kiểm tra flag giống như vậy nhưng cái khúc vòng lặp thì không giống với source code gốc đâu =))
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
import random import sys flag = input('give me the flag: ') random.seed(flag[:8]) f = [1, 142, 239, 69, 97, 122, 32, 159, 185, 230, 7, 30, 252, 33, 226, 157, 247, 115, 108, 42, 222, 11, 161, 115, 90, 216, 232, 103, 76, 68, 58, 163, 131, 231, 193, 206, 154, 221, 66, 129, 14] a = True for i in range(len(f)): rnd = random.getrandbits(8) if a: if f[i] ^ i != flag[i]: print('Wrong') sys.exit(1) a = False else: if f[len(f) - i - 1] ^ rnd != flag[i]: print('Wrong') sys.exit(1) a = True print('Correct') |
Với flag[:8]
chính là AceBear{
thì mình có thể đoán được kết quả của hàm random.getrandbits(8)
. Đây là hàm giải flag của mình, phải chạy trên python 3.6 thì mới tạo ra được kết quả của getrandbits
mong muốn.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
import random f = [1, 142, 239, 69, 97, 122, 32, 159, 185, 230, 7, 30, 252, 33, 226, 157, 247, 115, 108, 42, 222, 11, 161, 115, 90, 216, 232, 103, 76, 68, 58, 163, 131, 231, 193, 206, 154, 221, 66, 129, 14] random.seed('AceBear{') flag = '' for i in range(len(f)//2): rnd = random.getrandbits(8) flag += chr(f[i] ^ rnd) rnd = random.getrandbits(8) flag += chr(f[len(f) - i - 1] ^ rnd) print(flag) # AceBear{Pyth0n_v3rs4t1l1ty_4nd_CPP_sp33d} |
Cuối cùng là cảm ơn tác giả đã tạo điều kiện cho mình debug sml bằng IDA vì thông thường mình reverse chỉ đọc code và debug bằng não =)) Và hàm check flag cũng khá đơn giản chứ không cố ý làm khó khi đã "đứng trước cửa thiên đường" :v