FLAREON 2020 - WRITEUP PHẦN 1
Mở đầu
Flare-on CTF
Flare-on là sân chơi CTF hàng năm của những người chơi hệ nhị phân 👨💻, reverse engineer, các nhà nghiên cứu về mã độc và bảo mật. Một kỳ Flare-on CTF diễn ra trong vòng 6 tuần, thông thường gồm 11 challenge tương ứng với 11 level đầy đủ các thể loại và platform (Windows, Linux, Python, VBA, .NET, …)
Vì sao nên chơi Flare-on
- Các challenge này được
Fireeye
mô tả là “một trong những vấn đề mà teamFireeye
phải đối mặt hằng ngày”, tức là những challenge này đều lấy ý tưởng từ những vấn đề có thật (có bài dùng cả malware thật), nên đề bài sẽ có cảm giác “real-world” hơn so với các bàiRE
trong các đề thiCTF
khác. - Nếu bạn là 1 người chơi
RE
thì không nên bỏ qua Flare-on, nhất là với các bạn mới chơiRE
. Bởi vì, đề Flare-on tuy khó, các bạn có thể không giải hết các challenge, nhưng trong vòng 6 tuần đó bạn sẽ học được nhiều thứ mới (như tính kiên trì chẳng hạn 😄 !!) - Sau khi kì thi kết thúc,
Fireeye
sẽ đưa ra lời giải chi tiết cho các challenge, bạn có thể tham khảo cách họ làm. Ngoài ra trên mạng cũng có rất nhiều write-up cho các bạn tham khảo, từ đó bạn có thể học hỏi cách họ làm, học cách tiếp cận bài toán, dùng tool để giải bài, …
Mình bắt đầu chơi Flare-on từ năm ngoái, và cũng không giải được hết các challenge, tuy nhiên ít nhất mình đã không bỏ cuộc cho tới phút cuối cùng. Năm nay mình tiếp tục tham gia Flare-on và đã giải được đủ 11 bài, và đây là write-up cho 11 bài đó !
Phần 1
Mình sẽ chia 11 bài writeup ra thành nhiều phần khác nhau 😄, ở phần 1 này chúng ta cùng khởi động với 3 bài đầu tiên đó chính là: Fidler
(solution), garbage
(solution) và Wednesday
(solution)
1 - Fidler
1 | Welcome to the Seventh Flare-On Challenge! |
Như tiêu đề, bài này bao gồm file .exe
và file source code .py
.
Chạy file exe:
Chương trình yêu cầu nhập password, ta mở source code ra:
Ta dễ dàng lấy được password là ghost
, nhập vào, ta sẽ chuyển sang màn hình tiếp theo:
Ta phải click vào con mèo 100 tỷ lần để lấy được flag, hoặc ta có thể xem chương trình tạo flag như nào để có thể lấy được nó.
Ta có thể mở source code của game này ra để đọc, đó chính là file fidler.py
, lướt qua nhìn tên các hàm thì có một hàm liên quan tới “flag”, đó là hàm decode_flag
:
1 | def decode_flag(frob): |
Sau đó select chữ "decode_flag"
trong notepad++
, thì nó sẽ tự select tất cả các chỗ khác có chứa "decode_flag"
Ta có thể thấy 1 reference khác của "decode_flag"
ở hàm victory_screen
, ta lại tiếp tục select chữ "victory_screen"
để tìm reference:
Ta lại tiếp tục làm như trên với hàm game_screen
:
Vậy là ta đã biết hàm game_screen
được gọi như nào, tuy nhiên điều đó cũng không quá quan trọng vì ta sẽ chỉ quan tâm tới hàm decode_flag
.
Chỉ có duy nhất 1 chỗ gọi hàm decode_flag
, đó là chỗ này:
1 | # ... truncated |
Dễ thấy current_coin
phải lớn hơn hoặc bằng 2**36+2**35
và bé hơn 2**36+2**35+2**20
thì dòng victory_screen(int(current_coins / 10**8))
mới được gọi.
Khi đó ta viết lại đoạn code ở trên với frob=int((2**36+2**35)/10**8)
1 | print (decode_flag(int((2**36+2**35)/10**8))) |
Run:
1 | idle_with_kitty@flare-on.com |
2 - garbage
1 | One of our team members developed a Flare-On challenge but accidentally deleted it. We recovered it using extreme digital forensic techniques but it seems to be corrupted. We would fix it but we are too busy solving today's most important information security threats affecting our global economy. You should be able to get it working again, reverse engineer it, and acquire the flag. |
Ta giải nén và chạy thử file exe:
File đã bị corrupt đúng như đề bài nói. Tiếp theo ta dùng Detect it easy
để lấy thêm thông tin về file.
Detect it easy
là một tool dùng để xác định xem 1 filePE
được biên dịch bằng compiler nào, được pack bằng packer nào, từ đó ta có thể có sử dụng các tool thích hợp để phân tích file đó.
File được nén bằng UPX
, ta sẽ dùng đúng tool UPX
để unpack file này.
UPX
là một tool dùng để nén file executable, open source, bạn đọc có thể xem ở đây.
UPX
không thể unpack file này, có thể là vì nó đã bị lỗi. Giờ ta mở file lên bằng PE-bear
để xem phần nào trong file này bị lỗi.
PE-bear
là một tool dùng để xem PE header của 1 file PE, bạn đọc có thể tải ở đây.
Ta thấy ngay phần section .rsrc
bị lỗi (màu đỏ).
Ta phân tích tiếp: mục .rsrc
có Raw size
là 0x124
, tức là khi map file lên bộ nhớ tại Virtual address 0x19000
, phần data thật sự sẽ kết thúc ở 0x19123
, trong khi ở hình trên thì phần Import Directory
bắt đầu ở Virtual address 0x191DC
, tức là nằm ngoài vùng data thật sự (3 vùng màu đỏ ở hình trên đều nằm ngoài vùng data).
Như vậy ta chỉ cần pad thêm vào file các byte null để nó có thể bao hết các Virtual address
trên. Ta cần thêm vào 0x1929C+0x10-(0x19000+0x124) = 0x188
byte.
Nhưng chưa đủ, nếu ta thêm chỉ chừng đó byte thì chưa chắc file đã “hoạt động” được, vì dung lượng của file phải chia hết cho File alignment
(tham khảo về PE struct tại đây).
Như hình trên, File alignment
là 0x200
. Vậy là sau khi thêm, ta phải đảm bảo là size của file này chia cho 0x200
dư 0, đoạn code dưới đây mình đã pad thêm cho tới bội của 0x200
là 0x400
.
1 | # python3 |
Sau khi chạy code trên, ta có file fix.exe
và có thể được unpack bởi UPX
Ta mở file mới lên trong IDA và bấm F5
1 | int __cdecl main(int argc, const char **argv, const char **envp) |
Ta thấy có một số chỗ MEMORY[0x?????]
, đó là vì khi pack file bằng UPX
, phần Import data
nằm ở đó (và bị phá huỷ), nên khi ta thêm null byte vào, nó sẽ không chính xác, tuy nhiên điều này cũng không quá quan trọng.
Hai hàm sub_401000
và sub_401045
lần lượt là hai hàm set key và decrypt data bằng phép xor
cơ bản, ta dùng script sau để solve.
1 | # python3 |
Run:
1 | b'MsgBox("Congrats! Your key is: C0rruptGarbag3@flare-on.com")\x00Fqv' |
3 - Wednesday
1 | Be the wednesday. Unlike challenge 1, you probably won't be able to beat this game the old fashioned way. Read the README.txt file, it is very important. |
Đề bài cho 1 game 2D, ta mở lên chạy thử
Game chỉ đơn giản là vượt qua các chướng ngại vật (ngồi xuống hoặc nhảy để né vật thể).
Tuy nhiên, có những chỗ như hình trên, nhìn bằng mắt thì phải ngồi xuống để vượt qua, nhưng khi ngồi xuống thì character của chúng ta die ngay lập tức, hoặc có chỗ nhìn thì đáng ra phải nhảy lên, nhưng thật ra phải ngồi xuống mới qua được.
Sau 1 thời gian ngồi chơi game, mình phát hiện ra rằng:
- Game không thay đổi vị trí các vật thể sau các lần chết, để đạt được 8 điểm đầu tiên thì trạng thái lần lượt là: ngồi, ngồi, nhảy, nhảy, ngồi, ngồi, ngồi, nhảy.
- Chỉ có thể nhảy hoặc ngồi mới vượt qua được vật thể, đứng yên là die.
Vậy chỉ có 2 trạng thái là có thể giúp ta vượt qua vật thể, đến đây mình nghĩ ngay tới số nhị phân, vì số nhị phân cũng chỉ có 2 chữ số là 0 và 1. Với loạt trạng thái như ở trên thì số nhị phân có thể là: 00110001 hoặc 11001110.
Tìm thử dãy binary 00110001 trong memory ta được:
Ở trên hình, mình đã select 296 byte, giá trị của các byte này chỉ là “00” hoặc “01”. Ta có thể nghĩ ngay tới số nhị phân vì số nhị phân cũng được biểu diễn từ các con số 0 và 1. Vì vậy đoạn code dưới đây mình đã ghép 8 byte lại với nhau, rồi in ra màn hình. Lý do mình ghép 8 mà ko phải là 7 hay 9 là vì mỗi byte được tạo nên từ số nhị phân có 8 chữ số.
1 | # python3 |
Run:
1 | 1t_i5_wEdn3sd4y_mY_Dud3s@flare-on.com |
–