这题不难,熟悉一下方法
还是先看一下基本信息:
32位,开了NX
再ida看一下:
gets(v4),也许可以溢出,然后看见有get_flag函数
首先想的是溢出到if里面输出flag.txt,但是这里只有本地可以打通,远程打不通,应该是libc版本不同。
exp:
结果是可以的,自己写了个flag.txt试了一试。
然后,想一想,exp是绕过了if的判断语句的。就想能不能将返回地址写成带参数,使if为真。
地址传参
payload构造:'a'*0x38 + 'get_flag的地址' + get_flag的返回地址 + 参数1 + 参数2
先溢出到get_flag然后填充get_flag的返回地址,再跟上参数,有一点要注意get_flag的返回地址,打远程时,这里如果程序是异常退出,最后是不回显的,所以得让程序正常退出。这就需要把get_flag的返回地址写成exit的地址
这里可以找到exit的地址,下面是a1和a2两个参数的值,但是写的时候记得去掉后面的h
exp:
本地打通了
远程也打通了
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
地址
IDA里搜索
mprotect:0x0806ec80
read: 0x0806e140
偏移:0x38
还原栈
为了再能使用栈ret,构造一下栈的布局,因为mprotect函数push入了3个参数,就找存在3个连续pop的指令。函数传参是使用push,所以要为了堆栈还原,函数调用结束时就使用pop来保证堆栈完好。如下图所示:
找到一个,0x0804f460,其它也有可以的。
所以ret_addr1就可以为0x0804f460,而ret_addr2则为read函数的地址,ret_addr3就为mprotect的地址
最终exp:
结果也是本地和远程都能打通
Thanks for your blog, nice to read. Do not stop.