- vừa được xem lúc

[Write-up] Nahamcon2022 CTF - Rev Challenges - Part 2

0 0 19

Người đăng: Nguyen Anh Tien

Theo Viblo Asia

Intro

Tiếp tục với series write-up các rev challenges của Nahamcon2022, lần này sẽ là 1 bài ở mức độ medium: Kamikaze. Không hiểu sao bài này không thấy team nào writeup luôn, trên CTF Time cũng ko có 🤕. Whatever, mình sẽ điền vào chỗ trống vậy 😤

Kamikaze

Point: 495

Rating: medium

Solved: hình như hơn 50 team gì đó, trang CTF chết rồi nên ko xem được 🤕

File binary: https://mega.nz/file/LlVGRJqR#PoTNc3XNXf7LZVMFJ7x0DK3MOBfxauuV2VHhDNqvMHg

Độ khó đã được +1. Check file:

➜ rev file kamikaze
kamikaze: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=10b91b097890dc22b1ccf7beea3887c9dfaca8c1, for GNU/Linux 3.2.0, stripped

và chạy thử:

➜ ~ ./kamikaze
./kamikaze: /lib/x86_64-linux-gnu/libc.so.6: version `GLIBC_2.32' not found (required by ./kamikaze)
./kamikaze: /lib/x86_64-linux-gnu/libc.so.6: version `GLIBC_2.34' not found (required by ./kamikaze)

oạch, lỗi này là do file được compile với libc mới hơn so với libc trong WSL2 của mình, có thể khắc phục bằng nhiều cách, nâng cấp libc hoặc đơn giản nhất là mount folder vào và chạy trong docker debian:latest:

docker run -it -v $(pwd):/src --rm debian bash

Sau khi fix và chạy lại ta có màn hình sau:

➜ rev ./kamikaze
▓██ ██▓ ▒█████ █ ██ ▓█████▄ ██▓▓█████ ▓█████▄ ▒██ ██▒▒██▒ ██▒ ██ ▓██▒ ▒██▀ ██▌▓██▒▓█ ▀ ▒██▀ ██▌ ▒██ ██░▒██░ ██▒▓██ ▒██░ ░██ █▌▒██▒▒███ ░██ █▌ ░ ▐██▓░▒██ ██░▓▓█ ░██░ ░▓█▄ ▌░██░▒▓█ ▄ ░▓█▄ ▌ ░ ██▒▓░░ ████▓▒░▒▒█████▓ ░▒████▓ ░██░░▒████▒░▒████▓ ██▒▒▒ ░ ▒░▒░▒░ ░▒▓▒ ▒ ▒ ▒▒▓ ▒ ░▓ ░░ ▒░ ░ ▒▒▓ ▒ ▓██ ░▒░ ░ ▒ ▒░ ░░▒░ ░ ░ ░ ▒ ▒ ▒ ░ ░ ░ ░ ░ ▒ ▒ ▒ ▒ ░░ ░ ░ ░ ▒ ░░░ ░ ░ ░ ░ ░ ▒ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░
_@.com/app.d(137): Assertion failure
----------------
??:? _d_assertp [0x562ed6c8e3c0]
??:? _Dmain [0x562ed6c79d55]

ok fine, đề bài cũng nó đến việc chương trình chạy sẽ bị exception, không rõ nguyên nhân tại đâu và chúng ta sẽ là người tìm hiểm giải quyết vấn đề này (chứ còn ai vào đây nữa 😹)

Nhìn vào log debug, ta thấy source code là file: app.d, có lẽ nào là 🤔

image.png

Load vào IDA nào:

image.png

không còn nghi ngờ gì nữa rồi: https://dlang.org/library/rt/dmain2/_d_run_main.html 😂

Đây là lần đầu mình RE một binary viết bằng ngôn ngữ này nên cũng ko biết bắt đầu từ đâu, chúng ta trace ngược lại từ log ở trên, lỗi nằm ở hàm _d_assertp được gọi từ _Dmain:

image.png

hàm siêu ngắn, để ý D3app3artFZv nếu bỏ bớt các char thừa sẽ có thể hiểu là D_app_art, khả năng là hàm in ra dòng ASCII Art you died rồi.

đổi tên 1 chút cho dễ nhìn:

__int64 Dmain()
{ __int64 v0; // rax __int64 v1; // rdx v0 = D_app_handler(0LL, 0LL, 0); D_app_handler(v0, v1, 5); D_app_art(); d_assertp("source/app.d", 0x89u); return 0LL;
}

Vậy thứ mà chúng ta cần quan tâm ở đây chính là hàm D_app_handler, tiếp tục đi vào hàm này đọc và đổi tên biến/hàm cho dễ hiểu nhé 😄:

image.png

Đây là một switch-case với nhiều lựa chọn, theo flow chương trình ở Dmain thì đầu tiên chúng ta sẽ đi vào case 0, tức là đi vào D_app_stage1:

image.png

nhìn trông thật kinh dị á 😱. đọc dần từ đoạn 1 nào:

  1. Đầu tiên là khởi tạo một mảng v8 gồm 47 phần tử.
  2. Sau đó chúng ta thấy 2 lệnh call đến D_app_handler với choice tương ứng là 2 và 6. Xem lại switch-case ở trên thì tương ứng với hàm D_app_vault1 và tham số là 2 và 0. Xem thử hàm D_app_vault1:

image.png

Hàm này cơ bản là trả ra giá trị tương ứng của phần tử trong mảng 4 phần tử được định nghĩa sẵn:

  • với a1 bằng 0 sẽ trả về ef66958a6097f790
  • với a1bằng 2 sẽ trả về 5b462fe26831553204b66d88d2bb05c9

chút nữa ta sẽ thấy 2 giá trị này có vai trò gì.

  • Quay lại với D_app_stage1, hai giá trị lấy ra từ vault, cùng với các giá trị khác sẽ được đưa vào làm tham số của D6crypto3aes8AESUtils__T7decryptTCQBhQBd__T3AESVki4Vki4Vki10ZQsZQBoFIAhIAaIQgEQCz7padding11PaddingModeZAh. Nhìn sơ sơ thì đây là hàm decrypt của AES. Đến đây ta cần debug 1 chút.
  • Đặt breakpoint ở lệnh call vào hàm này vào debug bằng IDA:

image.png

  • 00007FB289595000 sẽ là chuỗi 5b462..., ngay trên đó là chiều dài của nó 0x20 (32 ký tự)
  • 00007FB289593000 sẽ là mảng v8 có chiều dài 0x30 (= 48, tính cả null byte)
  • tương tự, 00007FB289596000 là chuỗi ef669...

Vậy hàm gọi sẽ là:

 v9 = D_crypto_aes_AESUtils__T_decrypt__PaddingMode( 3, // padding mode iv_len, iv, v6, key_len, key, 48LL, // cipher len cipher);

PaddingMode có giá trị là 3, tương ứng sẽ là PKCS5 theo như document ở đây: https://github.com/shove70/crypto/blob/effb9357d8de9205cf8047f6777abc34bf8f28d0/src/crypto/padding.d

enum PaddingMode
{ NoPadding, // None ANSIX923, // 00 00 00 04 (Zero + size) ISO10126, // 0A EB 02 04 (Random + size) PKCS5, // 04 04 04 04 (All size) PKCS7, // 04 04 04 04 (All size) Zeros, // 00 00 00 00 (All zero) Customized // 00 00 00 00 + (00 00 00 04) (Zero + Original size)
}

Tuy nhiên trong thời gian thi thì mình không code lại được bằng code python để ra được kết quả decrypt như bên dưới, nên đành tạm thời debug tiếp.

Tiếp tục debug qua hàm này, ta thấy kết quả trả về ở RDX tại 0x00007FB289593040 (nhận biết bằng đoạn padding 0xA ở cuối đặc trưng theo chuẩn của PKCS5). Bỏ phần padding đi thì phần decrypt ra sẽ có chiều dài 38, đúng với format flag của cuộc thi: flag{md5_hash_of_something} nên khả năng cao đây chính là flag rồi, tuy nhiên còn cần một (hoặc nhiều bước giải mã nữa).

image.png

OK xong D_app_stage1, quay lại với flow của switch-case, choice tiếp theo là 5, đưa chúng ta vào hàm D_app_stage2:

image.png

v2 chính là mảng kết quả ở bước trước. Ở đây là vòng for ngược từ cuối về đầu (chú ý cụm i - 1), và đến khi i - 1 = 4 (tức là trước khi xor ra đoạn flag) thì văng exception 🤨 (chính là lỗi mà chúng ta gặp phải ở đầu bài)

Đặt breakpoint sau mỗi bước XOR và quan sát 00007FB289595060 sẽ thấy có flag:

image.png

hoặc chạy script sau:

magic = [0x67, 0x6E, 0x62, 0x63, 0x7E, 0x3E, 0x3F, 0x38, 0x3B, 0x3B, 0x68, 0x68, 0x34, 0x39, 0x3E, 0x28, 0x22, 0x21, 0x25, 0x27, 0x74, 0x74, 0x23, 0x7A, 0x2A, 0x79, 0x2D, 0x7E, 0x7B, 0x2A, 0x2A, 0x11, 0x18, 0x1B, 0x12, 0x14, 0x12, 0x5B]
flag = ""
for i in range(38): flag += chr(magic[i] ^ (i+1))
print(flag)
flag{88021cd97183363ab4b3c6bf45199107}

End

Ở phần 3 cũng là phần cuối của series, chúng ta sẽ đến với một sự thú vị.

Bình luận

Bài viết tương tự

- vừa được xem lúc

Code sạch, Code dễ phát triển,... Lập trình viên đã biết về Code an toàn chưa??? (Phần 2)

. Như đã hứa ở cuối phần 1 thì trong phần 2 này mình sẽ nói về các lỗ hổng: PHP Type Juggling, Hard Coded, Xử lý dữ liệu quan trọng tại Client side, Sử dụng bộ sinh số ngẫu nhiên không an toàn,... Giờ thì tiếp tục với Secure Coding thôi . 3. PHP Type Junggling. Lỗ hổng typle junggling xảy ra do PHP

0 0 38

- vừa được xem lúc

Code sạch, Code dễ phát triển,... Lập trình viên đã biết về Code an toàn chưa??? (Phần 1)

. Văn vẻ mở đầu. Chắc hẳn các bạn sinh viên khi học các môn lập trình trên trường đều ít nhiều được nghe đến khái niệm Code sạch - Clean code: là cách đặt tên biến, tên hàm; cách code sao cho dễ đọc, đễ hiểu.

0 0 30

- vừa được xem lúc

[Write-up] Intigriti's December XSS Challenge 2020

Giới thiệu. Gần đây mình có làm thử một bài CTF về XSS của Intigriti (platform bug bounty của châu Âu) và nhờ có sự trợ giúp của những người bạn cực kỳ bá đạo, cuối cùng mình cũng hoàn thành được challenge.

0 0 35

- vừa được xem lúc

Java deserialization - Write up MatesCTF 2018 WutFaces

Mở đầu. Bài ctf này là 1 bài rất hay về lỗ hổng java deserialization mà các bạn muốn tìm hiểu về lỗ hổng này nên làm.

0 0 148

- vừa được xem lúc

Code sạch, Code dễ phát triển,... Lập trình viên đã biết về Code an toàn chưa??? (Phần 3)

Chắc hẳn sau phần 1 và phần 2 thì mọi người đã hiểu được mức độ quan trọng của việc đảm bảo an toàn cho sản phẩm ngay từ khi thiết kế và lập trình rồi. Ở phần 3 này, chúng ta sẽ tìm hiểu về 1 lỗ hổng

0 0 37

- vừa được xem lúc

Lần này vẫn có source code, nhưng hack thì dễ hơn

Bài trước (Đây là bài trước: https://viblo.asia/p/khi-co-source-code-roi-thi-hack-co-de-khong-maGK7G8AKj2), mình có đưa một câu hỏi là Khi có source code rồi thì hack có dễ không?.

0 0 46