ret2libc:CTF Pwn 中的关键利用技巧

目录

ret2libc:CTF Pwn 中的关键利用技巧

一、原理

二、利用步骤

(一)查找漏洞并确定溢出点

(二)获取 system () 函数和字符串 “/bin/sh” 的地址

(三)构造攻击载荷(payload)

(四)触发漏洞并执行攻击

三、示例代码

(一)漏洞程序(vuln.c)

(二)利用代码(exp.c)


在 CTF(夺旗赛)的 Pwn 题目中,ret2libc 是一种极为重要且常用的漏洞利用技术。它主要针对程序中存在栈溢出等漏洞,通过巧妙地控制程序流程,利用动态链接库(libc)中的函数来实现恶意目的,比如获取 Shell 权限,进而完全控制目标系统。

一、原理

许多程序在运行时会依赖动态链接库(如 libc.so)来调用常见的函数,像 system ()(用于执行系统命令)、exit ()(用于正常退出程序)以及 gets ()、puts () 等输入输出函数。这些函数在内存中的地址是固定的,且在程序运行时可以被获取。当程序存在栈溢出漏洞时,攻击者能够覆盖栈上的返回地址,将其修改为 libc 中某个函数(如 system ())的地址,从而让程序跳转到该函数执行恶意代码。

二、利用步骤

(一)查找漏洞并确定溢出点

  1. 分析程序逻辑
    • 仔细阅读程序源代码(如果有),理解程序的功能和流程。重点关注可能存在用户输入且未正确处理的地方,如 gets ()、scanf () 等函数的调用。这些函数在读取用户输入时,如果没有对输入长度进行严格限制,就可能导致栈溢出。
    • 对于没有源代码的情况,可以使用反汇编工具(如 IDA Pro)来分析程序的汇编代码,寻找类似的危险函数调用和可疑的代码逻辑。
  2. 确定溢出长度
    • 通过调试工具(如 GDB)向程序输入不同长度的测试数据,观察程序的行为。当输入数据长度超过某个值时,程序可能会出现异常行为(如崩溃、跳转到意想不到的地址等),这个长度就是大致的溢出点位置。

(二)获取 system () 函数和字符串 “/bin/sh” 的地址

  1. 利用已知函数泄露地址
    • 程序中可能存在一些输出函数(如 puts ()、printf () 等),可以利用这些函数输出程序中已有的函数地址(如 puts () 本身的地址)。因为在同一个程序中,动态链接库的加载基址是固定的,所以通过已知函数的地址与该函数在 libc 中的偏移量,可以计算出 libc 的加载基址。
    • 例如,如果已知 puts () 函数在程序中的地址为 0x804a010,而 puts () 在 libc 中的偏移量为 0x6f690(这个偏移量可以通过分析 libc 库文件或者查阅相关资料得到),那么 libc 的加载基址为 0x804a010 - 0x6f690 = 0xf7e69000(这里只是示例计算,实际地址会因系统和环境而异)。
  2. 计算 system () 和 “/bin/sh” 的地址
    • 有了 libc 的加载基址后,再加上 system () 函数和字符串 “/bin/sh” 在 libc 中的偏移量,就可以得到它们在内存中的实际地址。system () 在 libc 中的偏移量通常可以通过查阅资料获取,比如在常见的 libc 版本中,system () 的偏移量可能是 0x3ada0。而字符串 “/bin/sh” 在 libc 中的偏移量也可以找到,例如为 0x15ba0b。

(三)构造攻击载荷(payload)

  1. 填充数据
    • 首先要填充足够的数据来覆盖栈上的返回地址之前的部分,使返回地址能够被精确控制。填充的数据可以是任意字节,通常使用字符‘A’来填充。填充的长度要根据前面确定的溢出点来计算,确保能够准确覆盖到返回地址。
  2. 设置 system () 函数地址
    • 将 system () 函数的地址放置在合适的位置,通常是覆盖返回地址的位置。这样当函数返回时,程序就会跳转到 system () 函数执行。
  3. 设置 “/bin/sh” 字符串地址
    • 在 system () 函数执行时,它需要一个参数,即要执行的命令字符串的地址。所以要将字符串 “/bin/sh” 的地址放置在 system () 函数的参数位置(通常是栈上的某个位置,具体取决于函数调用约定,如在 x86 架构下,参数通常在栈上按顺序排列在返回地址之后)。
  4. 添加辅助数据(可选)
    • 根据具体情况,可能还需要添加一些辅助数据,比如在覆盖返回地址后,可能需要填充一些数据来满足栈对齐要求(在某些架构下,函数调用需要栈对齐)。

(四)触发漏洞并执行攻击

  1. 输入攻击数据
    • 将构造好的攻击载荷(payload)作为输入提供给存在漏洞的程序。可以通过程序的输入接口(如命令行参数、文件读取、网络输入等)将 payload 输入到程序中。
  2. 控制程序执行流
    • 当程序处理输入数据时,由于存在栈溢出漏洞,攻击载荷会覆盖栈上的返回地址等关键数据。当函数返回时,程序就会按照攻击者设定的流程,跳转到 system () 函数并执行 “/bin/sh” 命令,从而获取目标系统的 Shell 权限。

三、示例代码

(一)漏洞程序(vuln.c)

#include 
#include 

void vuln() {
    char buffer[64];
    gets(buffer);
    printf("You entered: %s\n", buffer);
}

int main() {
    vuln();
    return 0;
}

(二)利用代码(exp.c)

#include 
#include 

#define OFFSET 72  // 这里的偏移量需要根据实际调试确定,72是示例值

void main(int argc, char *argv[]) {
    char *ptr;
    long *ret;
    long shellcode_addr;

    ptr = (char *)malloc(200);

    // 填充数据
    memset(ptr, 'A', OFFSET);

    // 设置system()函数地址
    ret = (long *)(ptr + OFFSET);
    *ret = (long)system;

    // 设置“/bin/sh”字符串地址
    shellcode_addr = (long)"/bin/sh";
    ret = (long *)(ptr + OFFSET + 4);
    *ret = shellcode_addr;

    // 执行漏洞程序并传入攻击数据
    execl("./vuln", "vuln", ptr, NULL);
}

在实际应用中,需要根据具体的程序环境、漏洞情况和系统配置对代码进行调整和优化。同时,CTF 比赛中的题目可能会有各种变化和防御机制,需要不断深入学习和实践才能熟练掌握 ret2libc 技术。

你可能感兴趣的:(网络安全,安全)