Chủ Nhật, 4 tháng 5, 2014

Lưu writeup CTF 0x3004 Từ [peternguyen.uns.vn]

<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 :nghihoac:
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 :imlang: .
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 :sexy:
Code được viết đơn giản lại như này :ethen:
Vấn đề chỉ đơn giản vậy thôi :smile: , input 20 ký tự bất kỳ, 4 ký tự cuối là hàm để trỏ về :ethen: . 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 :sexy:

  • 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:masad:
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) :fuck:
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. :cuoi:

[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 :them:
Đầu tiên xem thằng main cái đã :sexy:
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). :ethen:
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 :smile:
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.
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 :putnam: ( 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 :sexy:
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é :sexy:

Đâ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. :sad:
Để ý đề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 :sexy:
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 :putnam:
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 :smile: )
Sau khi input như trên mình sẽ có được như vầy.
Nhấn là mình đã pwn được :yeu:
Code:

[WRITE-UP] 0×3004 CTF – PWN 150 : OANTUXI

Đây là một bài PWN khá thú vi :sexy:
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ì đã :fuck:
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 :sexy:
Chon thử option 2:
Win và được cộng 50 điểm :smile: , 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 :fuck:
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 :sexy:
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 :chaymau:
Túm lại mình có đoạn code ngắn ngon sau đâu :smile:
Ở đây thì mĩnh nghĩ ngay đến 1 thứ gọi là Integer Overflow:cuoi: , 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 :smile:
Và đây là kết quả :sexy:

[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, :masad:
  • Đổi lại đây là lần đầu tiên mình compat với thím yeuchimse :sexy:
  • 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 :fuck:
Đá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 :masad:
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 :sexy:
Ở đâ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 :sad:
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]] .
Đâ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ử. :sexy:
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 :haha:

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}


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
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)
Đúng theo format của flag là 0x3004{XXXXXXXXXXXXXXXXXXXXX}
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 sys
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","{")
C:\Users\Phuongnamsoft>E:\CTF\0x3004\pyd.py
0x3004{from_dis_import_Fl4G}