get_started_3dsctf_2016(mprotect修改内存的权限)

这题不难,熟悉一下方法
还是先看一下基本信息:
file
32位,开了NX

再ida看一下:
file

gets(v4),也许可以溢出,然后看见有get_flag函数

file

首先想的是溢出到if里面输出flag.txt,但是这里只有本地可以打通,远程打不通,应该是libc版本不同。

exp:
file

结果是可以的,自己写了个flag.txt试了一试。

file

然后,想一想,exp是绕过了if的判断语句的。就想能不能将返回地址写成带参数,使if为真。

地址传参

payload构造:'a'*0x38 + 'get_flag的地址' + get_flag的返回地址 + 参数1 + 参数2
先溢出到get_flag然后填充get_flag的返回地址,再跟上参数,有一点要注意get_flag的返回地址,打远程时,这里如果程序是异常退出,最后是不回显的,所以得让程序正常退出。这就需要把get_flag的返回地址写成exit的地址

file

这里可以找到exit的地址,下面是a1和a2两个参数的值,但是写的时候记得去掉后面的h

file

exp:
file
本地打通了
file
远程也打通了
file

shellcode

还有其它的做法,通过 mprotet 函数修改内存的权限为可读可写可执行,然后在该内存中写入自己的shellcode,再执行代码。
简单记一下mprotect函数的用法:

  mprotect函数:
  int mprotect(void *addr, size_t len, int prot);
  addr :内存启始地址,指需要进行操作的地址
  len  :修改内存的长度
  prot :内存要赋予的权限
  • 简单来说,mprotect()函数把自start开始的、长度为len的内存区的保护属性修改为prot指定的值。然后prot=7(2的0次方+2的1次方+2的2次方) 是可读可写可执行。

  • 另外指定的内存区间必须包含整个内存页(4K)。区间开始的地址start必须是一个内存页的起始地址,并且区间长度len必须是页大小的整数倍。意思就是只需要修改某一段内存就行了。

思路
先确定可修改的内存,再溢出到 mprotect函数的地址,用mprotect修改内存权限,再调用read函数来写入shellcode
用paylaod来表示比较直观:

  payload = ‘A’ + 0x38 + p32(mprotect_addr)  #溢出并调用mprotect
  paylaod += p32(ret_addr1) + p32(m_addr) + p32(m_len) + p32(m_prot)  #给mprotect传参修改内存权限并还原栈
  payload += p32(ret_addr2)  #调用read函数
  paydaod += p32(m_addr) + p32(0x0) + p32(mprotect_addr) +p32 (0x100)   #给read参数并返回内存地址,这里返回到修改的地址
  payload += p32(mprotect_addr)  #跳回mprotect
  payload2= asm(shellcraft.sh(),arch = 'i386', os = 'linux')   #调用read的时候写入shellcode

确定内存
gdb调试,在main下断点用vmmap查看内存权限,这里就用0x080ea000到0x080ec000了,大小为0x1000
file

地址
IDA里搜索
mprotect:0x0806ec80
file

read: 0x0806e140

file

偏移:0x38

file

还原栈
为了再能使用栈ret,构造一下栈的布局,因为mprotect函数push入了3个参数,就找存在3个连续pop的指令。函数传参是使用push,所以要为了堆栈还原,函数调用结束时就使用pop来保证堆栈完好。如下图所示:
file
找到一个,0x0804f460,其它也有可以的。
所以ret_addr1就可以为0x0804f460,而ret_addr2则为read函数的地址,ret_addr3就为mprotect的地址

最终exp:
file
结果也是本地和远程都能打通

《get_started_3dsctf_2016(mprotect修改内存的权限)》有1条评论

发表评论

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据