绘画二进制0x000001攻击的艺术 【基于栈的溢出】
字数 1375 2025-08-24 20:49:31

二进制漏洞利用艺术:基于栈的缓冲区溢出攻击详解

1. 二进制漏洞攻击概述

二进制漏洞攻击是网络安全中一个核心领域,目的是发现程序中的漏洞并利用这些漏洞获取系统控制权或修改程序功能。计算机以二进制(0和1)操作,而二进制漏洞利用就是将程序中的弱点转化为攻击者的优势。

先修知识要求

  • 编程基础(特别是C语言)
  • 64位汇编语言基础
  • Linux终端使用经验

2. 缓冲区溢出原理

缓冲区溢出分为两种主要类型:

  1. 基于堆的缓冲区溢出
  2. 基于栈的缓冲区溢出

这两种情况都利用了应用程序接收用户输入的机制,可能导致程序崩溃或执行任意代码。当程序尝试向内存缓冲区写入超过其容量的数据时,就会发生缓冲区溢出。

高危函数

gets()函数特别危险,因为它:

  • 不检查输入长度
  • 无法防止缓冲区溢出
  • 已被标记为不应使用的函数

3. 漏洞示例分析

示例C程序

#include <err.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

char *gets(char *);

void abracadabra() {
    printf("Success..! Function called :D\n");
    exit(0);
}

int main(int argc, char **argv) {
    struct {
        char buffer[64];
        volatile int (*point)();
    } hackvist;
    
    hackvist.point = NULL;
    gets(hackvist.buffer);
    
    if(hackvist.point) {
        printf("Function Pointer → %p\n", hackvist.point);
        fflush(stdout);
        hackvist.point();
    } else {
        printf("Try Again\n");
    }
    exit(0);
}

攻击目标

执行未被调用的abracadabra()函数

攻击步骤

  1. 程序使用64字节缓冲区和函数指针*point
  2. 通过gets()函数接收输入
  3. 如果输入超过缓冲区大小,将覆盖相邻内存(包括函数指针)
  4. 将函数指针覆盖为abracadabra()的地址

4. 调试与利用过程

  1. 设置断点b main
  2. 分析执行流程:使用ni指令逐步执行
  3. 确定溢出点:通过填充可识别模式(如"AAAABBBB")确定哪些字符覆盖了指针
  4. 获取目标函数地址:使用调试器查看abracadabra()的地址
  5. 构造payload
    • 64字节填充数据
    • 目标函数地址(注意小端序)

分段错误(Segmentation Fault)

当程序尝试访问不被允许的内存位置时发生,是缓冲区溢出利用过程中的常见现象。

5. 更复杂的例子:无函数指针的情况

示例代码

#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>

void win() {
    printf("code flow successfully changed\n");
}

int main(int argc, char **argv) {
    char buffer[64];
    gets(buffer);
}

攻击方法

  1. 溢出缓冲区并覆盖返回地址
  2. 确定win()函数地址:objdump -x stack4 | grep win
  3. 计算精确的溢出点(通过逐步增加输入长度)
  4. 构造payload:
    • 76个'A'(填充缓冲区)
    • win()函数地址(小端格式)

6. Shellcode注入

Shellcode概念

一段机器码指令,允许在易受攻击的应用程序中注入并执行恶意代码,通常用于获取系统shell。

攻击步骤

  1. 找到缓冲区起始地址和返回地址
  2. 计算两者之间的偏移量
  3. 构造payload:
    • NOP sled(提高命中率)
    • Shellcode(实际执行的代码)
    • 填充数据
    • 指向shellcode的返回地址

示例shellcode

shellcode = '\x6a\x3b\x58\x48\x31\xd2\x49\xb8\x2f\x2f\x62\x69\x6e\x2f\x73\x68\x49\xc1\xe8\x08\x41\x50\x48\x89\xe7\x52\x57\x48\x89\xe6\x0f\x05\x6a\x3c\x58\x48\x31\xff\x0f\x05'

完整payload结构

  • NOP sled: 90字节
  • setreuid: 14字节
  • Shellcode: 40字节
  • 随机字符: 8字节
  • 内存地址: 6字节
  • 总计: 158字节

7. 防御措施

  1. 使用安全的替代函数(如fgets()代替gets()
  2. 启用栈保护机制(如Canary)
  3. 使用地址空间布局随机化(ASLR)
  4. 设置非可执行栈(NX)
  5. 代码审计和静态分析

8. 总结

基于栈的缓冲区溢出是二进制漏洞利用的基础技术,通过精心构造的输入可以:

  • 修改函数指针
  • 覆盖返回地址
  • 注入并执行任意代码

掌握这些技术需要深入理解程序内存布局、汇编语言和调试技巧。本文介绍的示例虽然简单,但包含了缓冲区溢出攻击的核心原理,可以作为更复杂漏洞利用的基础。

二进制漏洞利用艺术:基于栈的缓冲区溢出攻击详解 1. 二进制漏洞攻击概述 二进制漏洞攻击是网络安全中一个核心领域,目的是发现程序中的漏洞并利用这些漏洞获取系统控制权或修改程序功能。计算机以二进制(0和1)操作,而二进制漏洞利用就是将程序中的弱点转化为攻击者的优势。 先修知识要求 编程基础(特别是C语言) 64位汇编语言基础 Linux终端使用经验 2. 缓冲区溢出原理 缓冲区溢出分为两种主要类型: 基于堆的缓冲区溢出 基于栈的缓冲区溢出 这两种情况都利用了应用程序接收用户输入的机制,可能导致程序崩溃或执行任意代码。当程序尝试向内存缓冲区写入超过其容量的数据时,就会发生缓冲区溢出。 高危函数 gets() 函数特别危险,因为它: 不检查输入长度 无法防止缓冲区溢出 已被标记为不应使用的函数 3. 漏洞示例分析 示例C程序 攻击目标 执行未被调用的 abracadabra() 函数 攻击步骤 程序使用64字节缓冲区和函数指针 *point 通过 gets() 函数接收输入 如果输入超过缓冲区大小,将覆盖相邻内存(包括函数指针) 将函数指针覆盖为 abracadabra() 的地址 4. 调试与利用过程 设置断点 : b main 分析执行流程 :使用 ni 指令逐步执行 确定溢出点 :通过填充可识别模式(如"AAAABBBB")确定哪些字符覆盖了指针 获取目标函数地址 :使用调试器查看 abracadabra() 的地址 构造payload : 64字节填充数据 目标函数地址(注意小端序) 分段错误(Segmentation Fault) 当程序尝试访问不被允许的内存位置时发生,是缓冲区溢出利用过程中的常见现象。 5. 更复杂的例子:无函数指针的情况 示例代码 攻击方法 溢出缓冲区并覆盖返回地址 确定 win() 函数地址: objdump -x stack4 | grep win 计算精确的溢出点(通过逐步增加输入长度) 构造payload: 76个'A'(填充缓冲区) win() 函数地址(小端格式) 6. Shellcode注入 Shellcode概念 一段机器码指令,允许在易受攻击的应用程序中注入并执行恶意代码,通常用于获取系统shell。 攻击步骤 找到缓冲区起始地址和返回地址 计算两者之间的偏移量 构造payload: NOP sled(提高命中率) Shellcode(实际执行的代码) 填充数据 指向shellcode的返回地址 示例shellcode 完整payload结构 NOP sled: 90字节 setreuid: 14字节 Shellcode: 40字节 随机字符: 8字节 内存地址: 6字节 总计: 158字节 7. 防御措施 使用安全的替代函数(如 fgets() 代替 gets() ) 启用栈保护机制(如Canary) 使用地址空间布局随机化(ASLR) 设置非可执行栈(NX) 代码审计和静态分析 8. 总结 基于栈的缓冲区溢出是二进制漏洞利用的基础技术,通过精心构造的输入可以: 修改函数指针 覆盖返回地址 注入并执行任意代码 掌握这些技术需要深入理解程序内存布局、汇编语言和调试技巧。本文介绍的示例虽然简单,但包含了缓冲区溢出攻击的核心原理,可以作为更复杂漏洞利用的基础。