Challenge 06

6 - codeit

Reverse engineer this little compiled script to figure out what you need to do to make it give you the flag (as a QR code).

Ở bài này, chúng ta có 1 file .exe

“Detect it easy” đã nhận ra đây là file thực thi được viết bằng AutoIt. Chạy file thử:

Dùng “Exe2Aut” để convert file này về dạng code AutoIt, ta được:

Exe2Aut là một tool dùng để convert binary được viết bằng AutoIT về dạng source code của nó.

Có vẻ như file này đã bị obfuscated để làm khó việc RE. Đến đây có 2 lựa chọn, ta có thể dùng chức năng “Find and replace” của editor để sửa lại đống này, hoặc viết 1 đoạn regex để rename lại toàn bộ chúng. Ở đây mình chọn cách viết regex bằng python, lý do là vì:

  • Mình thấy tên biến sau khi source code bị obfuscate có quy luật, nên có thể dùng regex.
  • Giả sử có hai hàm, gọi là hàm A và hàm B cho dễ, hai hàm này đều có biến local tên là “x” chẳng hạn. Dùng “Find and Replace” để đổi tên biến “x”, thì ta sẽ thay đổi biến “x” ở cả hai hàm, còn dùng regex thì có thể thay đổi “x” của chỉ một hàm (hoặc cả hai hàm luôn cũng được).

Ý tưởng viết regex như sau:

Ví dụ, trong đoạn code có một số chỗ như sau:

Global $flavekolca = Number(" 0 ")
Global $flwecmddtc = Number(" 1 ")
...

Mình thấy tên biến (bên trái dấu bằng) lúc nào cũng gồm 10 ký tự (flavekolca, flwecmddtc, …), hơn nữa tên biến luôn bắt đầu bằng chuỗi fl. Còn vế phải dấu bằng thì luôn là Number(", tiếp đến là một dấu cách, rồi đến một hằng số, rồi theo sau đó là một dấu cách và "). Nhận ra được quy luật trên, mình có thể dùng đoạn regex sau để match các đoạn string như trên:

template = rb'(?P<name>\$fl[a-z]{8}) = Number\(" (?P<num>[0-9]+) "\)'

Sau đó mình chỉ việc replace lại thành cái gì đó đơn giản hơn là xong, ví dụ mình sẽ dùng python để sửa đoạn trên thành:

Global $global_0_0 = 0
...

Ở trên chỉ là ý tưởng, còn code mình sẽ để ở file đính kèm. Sau khi chạy code xong, ta được file source code AutoIt mới, dễ đọc hơn nhiều.

Đoạn code mới không quá dài, ta có thể đọc và hiểu các function của nó. Về cơ bản, chương trình nhận input từ người dùng, sau đó tạo ra mã QR dưới dạng hình ảnh rồi hiện lên màn hình.

func_08, chương trình nhận vào input của người dùng để xử lý, sau đó đưa data đã được xử lý vào func_05.

func_05, chương trình lấy computer_name (ở chỗ số 6), sau đó encrypt nó ở hàm func_04 (số 7). Tiếp theo, đoạn computer_name được mã hóa sẽ được tính hash để làm key cho bước CryptDecrypt ở dưới (số 5).

  • Hash được dùng trong bài này là 32780 (CALG_SHA_256) (số 2) , và thuật toán mã hóa được dùng là 24 (AES) (số 1).
  • Ở hình trên, đoạn code có đánh số 3 chính là khởi tạo $local_5 để lưu trữ data bị mã hoá, còn đoạn code đánh số 4 là gán $local_5 vào struct để chuẩn bị decrypt.
  • Điều kiện để có thể đi vào trong nhánh If cuối cùng (số 8 ở trong hình trên) đó chính là, sau khi decrypt data chứa ở $local_5, thì 5 byte đầu phải là "FLARE" và 5 byte cuối cùng là "ERALF".

Và cuối cùng, ta phải xem lại hàm func_04, vì nó biến đổi computer_name trước khi tính hash.

Đoạn code trên biến đổi $arg_0 dựa vào nội dung của file .bmp (file này được drop ra trong lúc chương trình xử lý ảnh QR).

Tóm lại tới lúc này ta biết được rằng input từ người dùng không có tác dụng gì đối với việc lấy flag, quan trọng là computer_name phải thoả mãn những điều sau:

  • key = sha256(func_04(computer_name))
  • data = AES.decrypt(encrypted_data, key, IV = 0000000...)
  • data.startswith(b'FLARE') == True
  • data.endswith(b'ERALF') == True

Tuy nhiên chúng ta không thể biết được computer_name là gì, cũng không thể bruteforce dựa trên 2 điều kiện “FLARE” và “ERALF” vì AES không bị “Known plain-text attack”.

Vì không có điều kiện nào để giải ra computer_name nên mình đoán rằng:

func_04(computer_name) == computer_name

Tức là, computer_name sẽ là một chuỗi đặc biệt mà sẽ không bị biến đổi khi qua hàm func_04. Đến đây mình sẽ bruteforce để tìm ra computer_name như sau:

  • Mình nhận thấy hàm func_04 biến đổi chuỗi bằng cách biến đổi từng ký tự một, nên mình sẽ bruteforce một ký tự một lần.
  • Thuật toán: với mỗi ký tự trong computer_name, cho c chạy trong tập ký tự, nếu bruteforce_function(c) == c thì “chọn” c, trong đó bruteforce_function là hàm biến đổi chuỗi, nói thẳng ra nó chính là hàm func_04 (có sửa đổi 1 chút).

Code bruteforce:

# python3

chars = '0123456789abcdefghijklmnopqrstuvwxyz-'

def ror8(n: int, i: int) -> str:
    i = i % 8
    n = n & 0xFF
    return (n >> i) + ((n << (8 - i)) & 0xFF)

def brute_one(index: int) -> str:
    assert index >= 1 # AutoIT uses 1-based index .
    with open('sprite.bmp', 'rb') as f:
        f.read(54) # discard
        data = f.read()
    start = 7 * (index - 1)
    tmp = 0
    for i in range(7):
        tmp += ((data[start + i] & 1) << (7 - i))
    tmp = ror8(tmp, 1)
    for c in chars:
        if ord(c) == tmp:
            return c
    return ''

if __name__ == '__main__':
    r = ''
    for i in range(16):
        r = r + brute_one(i + 1)
    print (r)
aut01tfan1999

Sau đó, ta patch hàm func_03 (hàm này lấy computer_name), cho hàm này luôn return "aut01tfan1999".

Chạy thử chương trình, nhập 1 chuỗi bất kỳ, rồi bấm “Can haz code?”, ta sẽ nhận được 1 ảnh QR, quét ảnh này ta được:

L00ks_L1k3_Y0u_D1dnt_Run_Aut0_Tim3_0n_Th1s_0ne!@flare-on.com

[+] Source code dùng để giải cho tất cả các bài nằm ở đây