UDP通信程序的fuzz思路与CVE-2018-18066分析
字数 1404 2025-08-24 16:48:16

UDP通信程序的Fuzz思路与CVE-2018-18066分析

前言

随着网络交互程序越来越多,传统fuzz工具如afl-fuzz对网络协议支持有限。本文介绍如何通过修改net-snmp源码,将其UDP通信转换为stdin/stdout方式,使afl-fuzz能够有效进行fuzz测试,并分析发现的CVE-2018-18066漏洞。

环境准备

net-snmp编译安装

使用ASAN和afl-clang-fast编译器进行编译:

CXX=afl-clang-fast++ CC=afl-clang-fast ./configure --prefix=/root/0524/mortysnmp
AFL_USE_ASAN=1 make -j4
make install

启动snmpd服务监听1024端口:

./snmpd -f 127.0.0.1:1024

测试UDP通信:

echo 'hellotest' > /dev/udp/127.0.0.1/1024

程序流程分析

SNMP解析流程

通过动态调试(gdb)和静态分析,确定SNMP解析主要流程:

  1. snmpd.c: main(int argc, char *argv[])
  2. snmpd.c: receive()
  3. snmp_api.c: snmp_read2(&readfds)
  4. snmp_api.c: snmp_sess_read2((void *) slp, fdset)
  5. snmp_api.c: rc = _sess_read(sessp, fdset)
  6. snmp_api.c: rc = _sess_process_packet(sessp, sp, isp, transport, opaque, olength, rxbuf, length)
  7. snmp_api.c: ret = snmp_parse(sessp, sp, pdu, packetptr, length)
  8. snmp_api.c: rc = _snmp_parse(sessp, pss, pdu, data, length)

请求包验证

_sess_process_packet函数设置断点,确认packetptr指向接收到的UDP数据包内容,与网络抓包数据一致。

UDP通信转stdin/stdout实现

修改思路

  1. 在监听端口后添加发送socket请求的代码
  2. 通过stdin控制socket请求内容
  3. 修改程序在解析完成后立即退出

具体实现

  1. 添加UDP发送代码

init_master_agent函数后添加:

int mortys = 0;
int n = 0;
int reuse = 1;
struct sockaddr_in srv;
char buf[256] = {0};

bzero(&srv, sizeof(srv));
srv.sin_family = PF_INET;
srv.sin_addr.s_addr = inet_addr("127.0.0.1");
srv.sin_port = htons(1024);
/* 创建UDP套接字 */
s = socket(AF_INET, SOCK_DGRAM, 0);

memset(buf, 0, 256);
/* 从stdin读取用户输入到buf中 */
fgets(buf, 256, stdin);

/* 通过套接字s向服务器发送数据 */
if ((n = sendto(mortys, buf, strlen(buf), 0, (struct sockaddr *) &srv, sizeof(struct sockaddr))) < 0) {
    perror("sendto");
    return -1;
} else {
    printf("send to len %d:%s\n", n, buf);
}
  1. 修改请求包内容

_sess_read函数中替换请求包内容为stdin输入。

  1. 添加程序退出点

receive_snmp_parse函数中添加exit(0),使程序在完成SNMP解析后立即退出。

重新编译

make clean
AFL_USE_ASAN=1 make -j4
make install

功能验证

修改后程序可以直接通过stdin输入测试数据,无需网络发包:

echo "测试数据" | ./snmpd

CVE-2018-18066漏洞分析

漏洞描述

Net-SNMP 5.8之前版本存在错误的SNMP解析方式,可导致DoS漏洞。

漏洞触发流程

  1. 解析函数从_sess_process_packet开始,packetptr指向请求包,length为包长度
  2. 解析后的内容通过pdu对象传入snmp_oid_compare函数
  3. 执行到if (*(name1) != *(name2))时,r14寄存器为0
  4. 执行mov r15,QWORD PTR [r14]导致空指针异常

根本原因

SNMP OID比较函数未正确处理空指针情况,当接收到特定格式的恶意数据包时,会导致空指针解引用,引发程序崩溃。

参考资源

  1. 利用AFL-Fuzz-Server
  2. Net-SNMP 5.7.3 Remote DoS
UDP通信程序的Fuzz思路与CVE-2018-18066分析 前言 随着网络交互程序越来越多,传统fuzz工具如afl-fuzz对网络协议支持有限。本文介绍如何通过修改net-snmp源码,将其UDP通信转换为stdin/stdout方式,使afl-fuzz能够有效进行fuzz测试,并分析发现的CVE-2018-18066漏洞。 环境准备 net-snmp编译安装 使用ASAN和afl-clang-fast编译器进行编译: 启动snmpd服务监听1024端口: 测试UDP通信: 程序流程分析 SNMP解析流程 通过动态调试(gdb)和静态分析,确定SNMP解析主要流程: snmpd.c: main(int argc, char *argv[]) snmpd.c: receive() snmp_api.c: snmp_read2(&readfds) snmp_api.c: snmp_sess_read2((void *) slp, fdset) snmp_api.c: rc = _sess_read(sessp, fdset) snmp_api.c: rc = _sess_process_packet(sessp, sp, isp, transport, opaque, olength, rxbuf, length) snmp_api.c: ret = snmp_parse(sessp, sp, pdu, packetptr, length) snmp_api.c: rc = _snmp_parse(sessp, pss, pdu, data, length) 请求包验证 在 _sess_process_packet 函数设置断点,确认 packetptr 指向接收到的UDP数据包内容,与网络抓包数据一致。 UDP通信转stdin/stdout实现 修改思路 在监听端口后添加发送socket请求的代码 通过stdin控制socket请求内容 修改程序在解析完成后立即退出 具体实现 添加UDP发送代码 : 在 init_master_agent 函数后添加: 修改请求包内容 : 在 _sess_read 函数中替换请求包内容为stdin输入。 添加程序退出点 : 在 receive 和 _snmp_parse 函数中添加 exit(0) ,使程序在完成SNMP解析后立即退出。 重新编译 功能验证 修改后程序可以直接通过stdin输入测试数据,无需网络发包: CVE-2018-18066漏洞分析 漏洞描述 Net-SNMP 5.8之前版本存在错误的SNMP解析方式,可导致DoS漏洞。 漏洞触发流程 解析函数从 _sess_process_packet 开始, packetptr 指向请求包, length 为包长度 解析后的内容通过 pdu 对象传入 snmp_oid_compare 函数 执行到 if (*(name1) != *(name2)) 时, r14 寄存器为0 执行 mov r15,QWORD PTR [r14] 导致空指针异常 根本原因 SNMP OID比较函数未正确处理空指针情况,当接收到特定格式的恶意数据包时,会导致空指针解引用,引发程序崩溃。 参考资源 利用AFL-Fuzz-Server Net-SNMP 5.7.3 Remote DoS