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

如何通过控制流劫持与缓冲区溢出攻击C语言双向链表程序?

攻击目标

攻击目标是一个使用双向链表的C语言程序,代码如下:

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

typedef struct node {
    struct node *prev;
    struct node *next;
    char data[32];
} Node;

Node *list_insert(Node *after)
{
    Node *node = calloc(1, sizeof(struct node));
    if (after) {
        node->prev = after;
        if (after->next) {
            node->next = after->next;
            node->next->prev = node;
        }
        after->next = node;
    }

    return node;
}

void list_delete(Node *node)
{
    if (node->prev) {
        node->prev->next = node->next;
    }
    if (node->next) {
        node->next->prev = node->prev;
    }
}

int _main(int argc, char *argv[])
{
    if (argc != 4) {
        fprintf(stderr, "Error: Need three command-line arguments\n");
        return 1;
    }

    Node *a = list_insert(0);
    Node *b = list_insert(a);
    Node *c = list_insert(b);

    strcpy(a->data, argv[1]);
    strcpy(b->data, argv[2]);
    strcpy(c->data, argv[3]);

    list_delete(c);
    list_delete(b);
    list_delete(a);

    return 0;
}
环境设置
OS = Linux
ISA = x86_32

addr_of_a = 0x080002e0
addr_of_b = 0x08000320
addr_of_c = 0x08000350
ret = 0xfffe8764 # list_delete函数的返回地址(返回至_main)
当前进展与问题

程序的漏洞在于每个链表节点的char data[32]缓冲区存在溢出。我的攻击计划如下:

  • 溢出b->data到c的内存区域,将c->prev覆写为ret-0x4,c->next覆写为c->data的地址(指向注入shellcode的区域);
  • 调用list_delete(c)时,执行node->prev->next = node->next,将list_delete的返回地址替换为c->data的地址;
  • 遇到瓶颈:后续执行node->next->prev = node->prev时,shellcode的前4字节会被ret-0x4覆写。若将node->next设为null则无法覆盖栈上的返回地址,请问该如何解决?

解决方案

方案1:偏移shellcode入口点,避开被覆写区域

这是最可靠的解决思路:

  1. 向c->data中写入:4字节NOP(0x90)指令 + 完整shellcode,让shellcode从c->data + 0x4的位置开始;
  2. 溢出b->data时,将c->prev设为ret-0x4,c->next设为c->data + 0x4;
  3. 执行list_delete(c)时,node->prev->next = node->next会把返回地址修改为c->data + 0x4,直接跳转到未被覆写的shellcode起始位置;
  4. 此时node->next->prev = node->prev会修改c->data + 0x4的前4字节?可以进一步调整:在c->data中写入8字节以上的NOP sled,再放shellcode,返回地址设为c->data。即使前4字节被覆写,后续的NOP指令依然会引导执行流滑到有效的shellcode区域。

方案2:让node->next指向无关内存区域

既然node->next->prev = node->prev会修改node->next指向地址的前4字节,我们可以让node->next指向不影响shellcode的区域:

  1. 溢出b->data时,c->prev设为ret-0x4,c->next设为栈上的临时区域(比如ret + 0x4,该位置的修改不会影响shellcode);
  2. 这里需要调整逻辑:先通过溢出修改c->prev为ret-0x4,同时单独构造node->prev->next的赋值目标——比如在栈上伪造一个Node结构,让node->prev指向这个伪造结构,其next成员设为c->data。不过这个方案需要更复杂的内存布局构造,不如方案1直接。

方案3:构造“可容忍覆写”的shellcode前缀

在shellcode最前面放置一条短跳转指令(比如jmp 0x4,跳转到当前地址+4字节),这样即使前4字节被覆写成ret-0x4,只要跳转指令仍能生效,就能跳到后续的有效shellcode。但ret-0x4是栈地址,对应的机器码不一定是有效指令,可靠性较低,不推荐。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 05:05:09