Tag: ctf

[Show up] WhiteHat Grand Prix 2014 Final RE400 Challenge

Xin chào các bạn

Đây là 1 video show up tiếp theo, cũng là để kết thúc cho việc writeup GrandPrix.

Bài RE400 này cũng là trích xuất từ 1 loại malware .NET, 1 dạng dropper khá thông dụng, cho nên bên cạnh việc tìm flag, tôi đã cố gắng phân tích nhiều hơn để có thể chia sẻ với các bạn về phương pháp decrypt code, chắc chắn sẽ hữu dụng trong việc phân tích các loại malware .NET sau này.

Tôi đã từng gặp 1 số loại malware sử dụng phương pháp decrypt và drop/execute tương tự (điển hình là con malware được embed trong IDM Silent mà tôi đã có 1 bài phân tích ở ĐÂY).

Thể loại malware .NET càng ngày càng trở nên phổ biên hơn bởi vì chúng được build rất nhanh chóng và dễ dàng, lại được .NET Framework hỗ trợ quá mạnh về mặt tính năng, nên có thể nói là kẻ xấu chỉ cần nghĩ ra ý tưởng, sau đó click, click trong VS và .NET Framework sẽ hoàn thành nốt những gì chúng muốn.

Nếu nói rằng .NET malware không mạnh và nguy hiểm bằng các loại malware native khác, thì cũng không sai, nhưng cũng không hoàn toàn đúng. Cryptographic Locker là 1 loại ransomware tương tự như CryptoLocker, và nó được viết bằng Visual Basic .NET.

Vậy nên, bên cạnh việc tìm flag, hãy chú ý vào phương pháp thực hiện. FIle RE400 và code decrypt các bạn có thể down ở ĐÂY. Và dưới đây là video:

Enjoy and best Regards,

Levis

Advertisements

[Show-up] WhiteHat GrandPrix Final RE300 Challenge

Xin chào các bạn

Mấy bữa trước tôi nhận dược thông báo của BTC kêu réo là viết write-up cho mấy challenge trong phần thi jeopardy. Thực ra cũng không muốn viết đâu nhưng vì BTC gọi điện nhiều quá nên cũng đã write-up tạm bợ trong forum WhiteHat rồi. Nhưng tính tôi là thế, đã không làm thì thôi, đã làm thì phải làm cho hẳn hoi 1 tí. Âu cũng là để lưu giữ lại chút kỉ niệm chơi CTF, và chia sẻ với các bạn.

Tại sao lại gọi là “Show up”? Đơn giản bởi vì tôi không “Write” (viết) mà sẽ “show” (trình diễn, trình bày) cho các bạn thấy. Write-up nhiều quá nên chán rồi, nếu có viết ra nữa thì rất gượng gạo và mang tính đối phó, sẽ không kích thích cho lắm. Đây là 1 file video ghi lại quá trình phân tích, trace và tìm ra flag của bài RE300 (Link Download tại ĐÂY). Target có vẻ như là 1 phần của con malware nào đó được trích xuất ra, edit để embed flag vào trong, vì vậy các chương trình AV có thể sẽ detect và clean ngay lập tức (không phải lỗi của tôi, hãy hỏi BTC, lol xD).

Thôi không nói nhiều nữa, hãy cùng xem video phía dưới nhé:

Anyway, cảm ơn BTC đã tổ chức cuộc thi này để mọi người có thể “xích lại gần nhau hơn”, và tôi cũng đã quen được rất nhiều người bạn mới, và họ đều rất tuyệt vời.

Enjoy and best regards,

Levis

[Write-up] WhiteHat GrandPrix RE100, RE200 Challenges

Lần đầu tiên chơi CTF, chả biết gì nhiều, nhưng may mắn được sự bá đạo của bạn ML và anh bibo đã xuất sắc gánh team để đạt được kết quả tuyệt vời. Thật khâm phục (y)
Cá nhân mình thì gặp phải khá nhiều vấn đề, cả nguyên nhân chủ quan và khách quan. Nguyên nhân chủ quan là do kĩ năng kém và thiếu kinh nghiệm. Khách quan là do 1 loạt các vấn đề: từ gia đình, rồi mấy ông Điện lực, rồi mấy ông nhà mạng. Gia đình thì canh hơi kĩ, cứ ngồi vào máy là tỉ lệ bị nói nặng là >80%, điện đóm thì phập phù (vùng quê nó thế), lại không có mạng internet nên phải cắm phone vào làm modem, max speed 32KB/s, cứ mỗi lần dùng quá nhiều, phone nóng lên là lại bị disconnect. Thế nên không dám reg ip với btc vì sau mỗi lần reconnect là lại bị đổi public ip, đành phải ngồi đợi team mate mớm bài cho. Thật sự đã biến thành quả tạ lớn của team :v.
Nói chung là tham gia được với mọi người là vui lắm rồi, và nhận ra kĩ năng mình còn rất thấp, và thiếu nhiều thứ. Chỉ giải được duy nhất 2 bài RE100 và RE200. RE300 thì đọc được code nhưng không nghĩ ra thuật toán để bruteforce lấy flag (kĩ năng lập trình kém), kiểu như miếng ăn đến miệng mà không biết cách ăn vậy.
Anyway, không giải được nhiều thì viết writeup của RE100 và RE200, coi như để đánh dấu lần đầu tiên chơi ctf, thành công cho toàn team và thất bại cho cá nhân.
RE100 – Pydis:
BTC đưa cho một file RE100.txt, bên trong chứa các code disassemble của python bytecode. Chưa làm dạng này bao giờ, tất cả là nhờ trực giác của 1 cracker:


14 0 LOAD_CONST 1 ('')
3 STORE_FAST 0 (_1)

16 6 SETUP_LOOP 234 (to 243)
9 LOAD_CONST 2 (102)
12 LOAD_CONST 3 (114)
15 LOAD_CONST 4 (111)
18 LOAD_CONST 5 (109)
21 LOAD_CONST 6 (32)
24 LOAD_CONST 7 (122)
27 LOAD_CONST 8 (108)
30 LOAD_CONST 9 (105)
33 LOAD_CONST 10 (98)
36 LOAD_CONST 6 (32)
39 LOAD_CONST 9 (105)
42 LOAD_CONST 5 (109)
45 LOAD_CONST 11 (112)
48 LOAD_CONST 4 (111)
51 LOAD_CONST 3 (114)
54 LOAD_CONST 12 (116)
57 LOAD_CONST 6 (32)
60 LOAD_CONST 13 (100)
63 LOAD_CONST 14 (101)
66 LOAD_CONST 15 (99)
69 LOAD_CONST 4 (111)
72 LOAD_CONST 5 (109)
75 LOAD_CONST 11 (112)
78 LOAD_CONST 3 (114)
81 LOAD_CONST 14 (101)
84 LOAD_CONST 16 (115)
87 LOAD_CONST 16 (115)
90 LOAD_CONST 6 (32)
93 LOAD_CONST 17 (97)
96 LOAD_CONST 16 (115)
99 LOAD_CONST 6 (32)
102 LOAD_CONST 18 (121)
105 LOAD_CONST 19 (10)
108 LOAD_CONST 2 (102)
111 LOAD_CONST 3 (114)
114 LOAD_CONST 4 (111)
117 LOAD_CONST 5 (109)
120 LOAD_CONST 6 (32)
123 LOAD_CONST 10 (98)
126 LOAD_CONST 9 (105)
129 LOAD_CONST 20 (110)
132 LOAD_CONST 17 (97)
135 LOAD_CONST 16 (115)
138 LOAD_CONST 15 (99)
141 LOAD_CONST 9 (105)
144 LOAD_CONST 9 (105)
147 LOAD_CONST 6 (32)
150 LOAD_CONST 9 (105)
153 LOAD_CONST 5 (109)
156 LOAD_CONST 11 (112)
159 LOAD_CONST 4 (111)
162 LOAD_CONST 3 (114)
165 LOAD_CONST 12 (116)
168 LOAD_CONST 6 (32)
171 LOAD_CONST 21 (117)
174 LOAD_CONST 20 (110)
177 LOAD_CONST 22 (104)
180 LOAD_CONST 14 (101)
183 LOAD_CONST 23 (120)
186 LOAD_CONST 8 (108)
189 LOAD_CONST 9 (105)
192 LOAD_CONST 2 (102)
195 LOAD_CONST 18 (121)
198 LOAD_CONST 6 (32)
201 LOAD_CONST 17 (97)
204 LOAD_CONST 16 (115)
207 LOAD_CONST 6 (32)
210 LOAD_CONST 21 (117)
213 BUILD_LIST 68
216 GET_ITER
>> 217 FOR_ITER 22 (to 242)
220 STORE_FAST 1 (i)

17 223 LOAD_FAST 0 (_1)
226 LOAD_NAME 0 (chr)
229 LOAD_FAST 1 (i)
232 CALL_FUNCTION 1
235 INPLACE_ADD
236 STORE_FAST 0 (_1)
239 JUMP_ABSOLUTE 217
>> 242 POP_BLOCK

18 >> 243 LOAD_FAST 0 (_1)
246 LOAD_CONST 0 (None)
249 DUP_TOP
250 EXEC_STMT

19 251 LOAD_CONST 24 (5225289992572816510375617297175513170291190435493698272730040307127264906868335001081343190341346338780406158345171271164984495365L)
254 STORE_FAST 2 (c)

20 257 LOAD_NAME 1 (eval)
260 LOAD_NAME 2 (y)
263 LOAD_NAME 3 (u)
266 LOAD_NAME 4 (hex)
269 LOAD_FAST 2 (c)
272 CALL_FUNCTION 1
275 LOAD_CONST 27 (2)
278 LOAD_CONST 29 (-1)
281 SLICE+3
282 CALL_FUNCTION 1
285 CALL_FUNCTION 1
288 CALL_FUNCTION 1
291 STORE_FAST 3 (hi)

21 294 LOAD_FAST 3 (hi)
297 LOAD_NAME 5 (raw_input)
300 LOAD_CONST 26 ('Your input:')
303 CALL_FUNCTION 1
306 CALL_FUNCTION 1
309 PRINT_ITEM
310 PRINT_NEWLINE
311 LOAD_CONST 0 (None)
314 RETURN_VALUE

Từ trước đến nay chưa bao giờ thử những cái thế này, vì chỉ re x86/x64 và .NET. Nhưng mình chú ý đến những cái giá trị số trong ngoặc của mấy lệnh LOAD_CONST. Nhìn cái là biết liền đây là decimal của các kí tự trong ASCII. Thế là ngồi convert, có được
from zlib import decompress as y
from binascii import unhexlify as u
thế là có 2 lệnh import lib của python để import zlib.decompress và đặt vào y, binascii.unhexlify đặt vào u, mấy code ở bên dưới cũng không biết nên không quan tâm lắm
sau đoạn đó là lại load thêm 1 constant 5225289992572816510375617297175513170291190435493698272730040307127264906868335001081343190341346338780406158345171271164984495365L. Đây là 1 số BigInt, và lưu lại ở c.

20 257 LOAD_NAME 1 (eval)
260 LOAD_NAME 2 (y)
263 LOAD_NAME 3 (u)
266 LOAD_NAME 4 (hex)
269 LOAD_FAST 2 (c)

Đoạn code ở trên có vẻ như gọi lệnh eval để thực hiện command. Thế là cứ theo thứ tự từ trên xuống dưới mình xếp lại, thì có thể code tương tự thế này eval(“y(u(hex(c))”). Chuyển BigInt tại c thành hex string, rồi dùng binascii.unhexlify chuyển về dạng binarydata. Sau đó dùng zlib.decompress để lấy lại data gốc. Thế là build lại code:

from zlib import decompress as y
from binascii import unhexlify as u
print((y(u("789CCB49CC4D4A4954A8B0AAB0B5554F33354C4E4A4E4CB130B534343735353232B04834B4B0B0344832324C4EB54C56070047AF0D05"))))

phần string “789C…” chính là BigInt đã được convert về dạng hexstring.
Run code và kết quả:

lambda x:x=='f51cbcad85917552208a18890b21ce9c'
-> flag: Flag{f51cbcad85917552208a18890b21ce9c}

RE200 – PyObfus
Được 1 file re200.txt có dạng rất cổ quái, lúc đầu nhìn qua muốn khóc luôn :v


(('`')|('$'))+'='+(('`')|('%'))+(('[')^('-'))+(('`')|('!'))+(('`')|(','))+'('+'"'+(('`')|(','))+(('`')|('!'))+(('`')|('-'))+(('"')^('@'))+(('`')|('$'))+(('`')|('!'))+(('{')^('['))+(('`')|('&'))+(('`')|(','))+(('`')|('!'))+(('`')|('''))+':'+(('`')|('&'))+(('`')|(','))+(('`')|('!'))+(('`')|('''))+'='+'='+'''+(('[')^('/'))+(('`')|('%'))+(('`')|('#'))+'0'+(('[')^(')'))+(('[')^('*'))+(('[')^('.'))+(('`')|('!'))+(('`')|('''))+(('`')|('#'))+(('"')^('@'))+(('`')|(')'))+(('`')|('!'))+(('`')|('%'))+(('`')|('!'))+'1'+'2'+'0'+(('[')^(','))+(('`')|('%'))+(('"')^('@'))+(('`')|('$'))+(('[')^('+'))+(('`')|('#'))+(('`')|('!'))+(('`')|('%'))+(('`')|('-'))+(('[')^('-'))+'2'+(('`')|('$'))+'0'+(('`')|('#'))+'''+'"'+')'+(('[')^('+'))+(('[')^(')'))+(('`')|(')'))+(('`')|('.'))+(('[')^('/'))+(('{')^('['))+(('`')|('$'))+'('+(('[')^(')'))+(('`')|('!'))+(('[')^(','))+'_'+(('`')|(')'))+(('`')|('.'))+(('[')^('+'))+(('[')^('.'))+(('[')^('/'))+'('+'"'+(('[')^('"'))+(('`')|('/'))+(('[')^('.'))+(('[')^(')'))+(('{')^('['))+(('`')|(')'))+(('`')|('.'))+(('[')^('+'))+(('[')^('.'))+(('[')^('/'))+':'+'"'+')'+')' 

Thế nhưng khi làm xong re100, nhìn lại bài này thì còn thấy dễ hơn re100 nhiều. Có vẻ như nó đang tạo 1 string nào đó, với các kí tự được giấu đi bởi các biểu thức trong ngoặc đơn. Có thể thấy rất nhiều các biểu thức bitwise or (|) và xor (^) của các cặp kí tự. Nhưng muốn thực hiện bitwise thì phải là numeric. Thế thì có nghĩa là bitwise với các mã ascii của các kí tự đó, dùng ord(). Nhưng bitwise xong thì là numeric, thì lại phải chuyển về kiểu char, có nghĩa là dùng thêm chr(). Đó là lí do tại sao có 2 cặp dấu ngoặc tròn, code có thể có dạng sau: chr(ord(x)|ord(y)). Thế là build lại code, dùng Find&Replace của bất cứ text editor nào: replace “((” bởi “chr((“, sau đó replace tiếp “(‘” bởi “ord(‘” (sẽ phải fix vài chỗ vì bị sửa nhầm). được code mới:

s=chr(ord('`')|ord('$'))+'='+chr(ord('`')|ord('%'))+chr(ord('[')^ord('-'))+chr(ord('`')|ord('!'))+chr(ord('`')|ord(','))+'('+'"'+chr(ord('`')|ord(','))+chr(ord('`')|ord('!'))+chr(ord('`')|ord('-'))+chr(ord('"')^ord('@'))+chr(ord('`')|ord('$'))+chr(ord('`')|ord('!'))+chr(ord('{')^ord('['))+chr(ord('`')|ord('&'))+chr(ord('`')|ord(','))+chr(ord('`')|ord('!'))+chr(ord('`')|39)+':'+chr(ord('`')|ord('&'))+chr(ord('`')|ord(','))+chr(ord('`')|ord('!'))+chr(ord('`')|39)+'='+'='+'''+chr(ord('[')^ord('/'))+chr(ord('`')|ord('%'))+chr(ord('`')|ord('#'))+'0'+chr(ord('[')^ord(')'))+chr(ord('[')^ord('*'))+chr(ord('[')^ord('.'))+chr(ord('`')|ord('!'))+chr(ord('`')|39)+chr(ord('`')|ord('#'))+chr(ord('"')^ord('@'))+chr(ord('`')|ord(')'))+chr(ord('`')|ord('!'))+chr(ord('`')|ord('%'))+chr(ord('`')|ord('!'))+'1'+'2'+'0'+chr(ord('[')^ord(','))+chr(ord('`')|ord('%'))+chr(ord('"')^ord('@'))+chr(ord('`')|ord('$'))+chr(ord('[')^ord('+'))+chr(ord('`')|ord('#'))+chr(ord('`')|ord('!'))+chr(ord('`')|ord('%'))+chr(ord('`')|ord('-'))+chr(ord('[')^ord('-'))+'2'+chr(ord('`')|ord('$'))+'0'+chr(ord('`')|ord('#'))+'''+'"'+')'+chr(ord('[')^ord('+'))+chr(ord('[')^ord(')'))+chr(ord('`')|ord(')'))+chr(ord('`')|ord('.'))+chr(ord('[')^ord('/'))+chr(ord('{')^ord('['))+chr(ord('`')|ord('$'))+'('+chr(ord('[')^ord(')'))+chr(ord('`')|ord('!'))+chr(ord('[')^ord(','))+'_'+chr(ord('`')|ord(')'))+chr(ord('`')|ord('.'))+chr(ord('[')^ord('+'))+chr(ord('[')^ord('.'))+chr(ord('[')^ord('/'))+'('+'"'+chr(ord('[')^ord('"'))+chr(ord('`')|ord('/'))+chr(ord('[')^ord('.'))+chr(ord('[')^ord(')'))+chr(ord('{')^ord('['))+chr(ord('`')|ord(')'))+chr(ord('`')|ord('.'))+chr(ord('[')^ord('+'))+chr(ord('[')^ord('.'))+chr(ord('[')^ord('/'))+':'+'"'+')'+')'

Các kí tự “‘” (nháy đơn) nếu được đem vào sử dụng trong biểu thức thì phải chuyển luôn về mã ascii để tránh bị syntax error. Và thêm “s=” ở đầu để lưu lại string này vào trong 1 biến s. Rồi dùng print(s) để in nội dung ra, được như sau


d=eval("lambda flag:flag==+chr(ord('[')^ord('/'))+chr(ord('`')|ord('%'))+chr(ord('`')|ord('#'))+'0'+chr(ord('[')^ord(')'))+chr(ord('[')^ord('*'))+chr(ord('[')^ord('.'))+chr(ord('`')|ord('!'))+chr(ord('`')|39)+chr(ord('`')|ord('#'))+chr(ord('"')^ord('@'))+chr(ord('`')|ord(')'))+chr(ord('`')|ord('!'))+chr(ord('`')|ord('%'))+chr(ord('`')|ord('!'))+'1'+'2'+'0'+chr(ord('[')^ord(','))+chr(ord('`')|ord('%'))+chr(ord('"')^ord('@'))+chr(ord('`')|ord('$'))+chr(ord('[')^ord('+'))+chr(ord('`')|ord('#'))+chr(ord('`')|ord('!'))+chr(ord('`')|ord('%'))+chr(ord('`')|ord('-'))+chr(ord('[')^ord('-'))+'2'+chr(ord('`')|ord('$'))+'0'+chr(ord('`')|ord('#'))+")print d(raw_input("your input:"))

thế là lại obfus thêm lớp nữa, loại bỏ các phần không cần thiết, và chỉ giữ lại như sau:

flag=chr(ord('[')^ord('/'))+chr(ord('`')|ord('%'))+chr(ord('`')|ord('#'))+'0'+chr(ord('[')^ord(')'))+chr(ord('[')^ord('*'))+chr(ord('[')^ord('.'))+chr(ord('`')|ord('!'))+chr(ord('`')|39)+chr(ord('`')|ord('#'))+chr(ord('"')^ord('@'))+chr(ord('`')|ord(')'))+chr(ord('`')|ord('!'))+chr(ord('`')|ord('%'))+chr(ord('`')|ord('!'))+'1'+'2'+'0'+chr(ord('[')^ord(','))+chr(ord('`')|ord('%'))+chr(ord('"')^ord('@'))+chr(ord('`')|ord('$'))+chr(ord('[')^ord('+'))+chr(ord('`')|ord('#'))+chr(ord('`')|ord('!'))+chr(ord('`')|ord('%'))+chr(ord('`')|ord('-'))+chr(ord('[')^ord('-'))+'2'+chr(ord('`')|ord('$'))+'0'+chr(ord('`')|ord('#'))
print(flag)

sau lệnh print, ta có được flag: Flag{tec0rquagcbiaea120webdpcaemv2d0c}
300 điểm ngon ơ :). Nhìn chung 2 chall này khá dễ để nhìn ra ý đồ của btc, chỉ mất nhiều thời gian ở phần viết và fix code
Còn bài RE300, không quá khó để đọc code, nhưng chỉ bị vướng lại ở phần bruteforce. Nhưng phải nói lại thêm lần nữa, ML quá bá :))
Done!