You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

跨机器缓冲区溢出漏洞利用可行性及远程攻击实现疑问

缓冲区溢出相关问题及代码排查

一、跨机器溢出利用代码有效性与内存地址一致性

  • 针对某缓冲区溢出漏洞编写的exploit无法直接在其他机器上执行,内存地址也不会保持一致。现代操作系统普遍启用Address Space Layout Randomization(地址空间布局随机化,ASLR),每次程序启动时都会随机分配栈、堆、库等内存区域的基地址,即便同一机器多次运行,地址也无法固定。

二、ASLR环境下Geohot的缓冲区溢出攻击实现

Geohot能在ASLR环境下完成本地到远程的溢出攻击,核心思路包括:

  1. 信息泄露:先通过漏洞读取目标进程内存中的关键地址(如某库函数的实际加载地址),计算出ASLR的随机偏移量,再修正exploit中的跳转地址。
  2. ROP(返回导向编程):利用内存中已存在的代码片段(gadgets)拼接攻击逻辑,无需直接跳转到固定shellcode地址,通过栈上的返回地址链执行指令,绕开ASLR限制。
  3. 动态适配payload:本地环境完成漏洞验证和偏移分析后,远程攻击时先通过信息泄露获取目标随机基地址,再生成适配的payload。

三、服务器代码未触发缓冲区溢出的问题排查

代码与测试情况

服务器代码:

#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>
#define PORT 1337

int main(int argc, char const* argv[])
{
    int server_fd, new_socket, valread;
    struct sockaddr_in address;
    int opt = 1;
    int addrlen = sizeof(address);
    static char buffer[1024] = { 0 };
    char buffer2[8] = { 0 };
    char* hello = "Hello from server";

    // 创建socket文件描述符
    if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
            perror("socket failed");
            exit(EXIT_FAILURE);
    }

    // 强制绑定端口
    if (setsockopt(server_fd, SOL_SOCKET,
                    SO_REUSEADDR | SO_REUSEPORT, &opt,
                    sizeof(opt))) {
            perror("setsockopt");
            exit(EXIT_FAILURE);
    }
    address.sin_family = AF_INET;
    address.sin_addr.s_addr = INADDR_ANY;
    address.sin_port = htons(PORT);

    // 绑定socket到端口
    if (bind(server_fd, (struct sockaddr*)&address,
                sizeof(address))
            < 0) {
            perror("bind failed");
            exit(EXIT_FAILURE);
    }
    while (1){
        if (listen(server_fd, 3) < 0) {
                perror("listen");
                exit(EXIT_FAILURE);
        }
        if ((new_socket
                = accept(server_fd, (struct sockaddr*)&address,
                                (socklen_t*)&addrlen))
                < 0) {
                perror("accept");
                exit(EXIT_FAILURE);
        }
        valread = read(new_socket, buffer, 1024);
        fprintf(stderr,"%d\n",valread);
        strcpy(buffer2, buffer);
        printf("%s\n", buffer);
        send(new_socket, buffer2, strlen(buffer2), 0);
        printf("Hello message sent\n");
        close(new_socket);
    }
}

测试发送的payload:

AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

程序输出:

8
AAAAAAAA
Hello message sent

问题原因分析

  1. 实际读取长度不足:输出显示valread为8,说明服务器仅读取到8个字符,可能是发送工具默认截断了数据(比如以换行符作为结束标志),导致buffer中只有8个'A',strcpy复制时未超出buffer2的临界范围,无法覆盖栈帧关键区域。
  2. 栈保护机制拦截:现代编译器默认启用Stack Canary(栈金丝雀),即便存在少量溢出,只要破坏金丝雀值,程序会直接触发栈检查并终止,而非触发段错误。
  3. 缓冲区初始化特性:buffer是静态初始化的全0数组,若仅读取8个'A',后续字节仍为0,strcpy会在遇到第一个0时停止复制,进一步限制了溢出范围。

解决建议

  • 确保发送完整长payload:用命令行工具发送足够长度的数据,比如python3 -c 'print("A"*100)' | nc localhost 1337,覆盖buffer2及后续栈帧区域。
  • 编译时关闭栈保护:使用gcc -fno-stack-protector -z execstack -o server server.c,禁用金丝雀和栈执行保护,便于触发段错误。
  • 检查发送逻辑:确保发送工具不会截断数据,发送完成后主动关闭socket,让read读取到完整payload。

内容的提问来源于stack exchange,提问作者Mixalis Navridis

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 06:53:20