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
, choc
chạy trong tập ký tự, nếubruteforce_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àmfunc_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
Gitalking ...