Tag: python

[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!

Advertisements

Decompyle++ – A great python dissasembler/decompiler

Decompyle++

A Python Byte-code Disassembler/Decompiler

Decompyle++ aims to translate compiled Python byte-code back into valid and human-readable Python source code. While other projects have achieved this with varied success, Decompyle++ is unique in that it seeks to support byte-code from any version of Python.

Decompyle++ includes both a byte-code disassembler (pycdas) and a decompiler (pycdc).

As the name implies, Decompyle++ is written in C++. If you wish to contribute, please fork us on github at https://github.com/zrax/pycdc

I’ve tested it in Windows 7 32bit and Arch Linux i686, and works good. It supports from py10 to py34 (according to list of map files in /bytes folder, tried with pyc compiled by py34 but not cecompiled yet, maybe support for py34 should be updated in future)

How to make it run under Linux:
Grab files from git and build it:

git clone git://github.com/zrax/pycdc
cd pycdc
make

Compiled files are pycdas for Python Disassembler and pycdc for Python decompiler.
Usage:

pycdas [FILE_NAME]

to disassemble pyc/pyo file
or

pycdc [FILE_NAME]

to decompile pyc/pyo file

Enjoy and best regards,
Levis