引言
本文将详细介绍我在MiniDLNA的HTTP chunk解析代码中发现的堆缓冲区溢出漏洞的细节和根本原因,该漏洞影响最大版本为1.3.2。该漏洞可以被利用,导致在运行minidlna服务器的用户上下文中实现远程代码执行。
本文的第二部分包含了详细的漏洞开发过程以及针对x86_64和ARM32目标的两个完全武器化的漏洞利用示例,具体内容可以在此处找到。
漏洞总结
MiniDLNA是一个简单的媒体服务器软件,旨在与DLNA/UPnP-AV客户端完全兼容。它通常部署在Linux服务器上,并广泛应用于路由器和NAS等嵌入式设备中。
MiniDLNA/ReadyMedia媒体服务器包含一个漏洞,该漏洞存在于负责处理使用分块编码(chunked encoding)请求的HTTP请求处理代码中,可能导致越界读写,从而引发远程代码执行。该问题发生在ParseHttpHeaders()中的分块大小验证逻辑中,导致一个比较表达式的返回值错误地保存到用于跟踪解析的分块大小的变量中,而不是保存用来解析大小的strtol()函数的返回值。这允许大于请求总大小的值通过验证;应用程序随后解析并将这些分块大小值作为memmove()函数调用中的大小参数,导致堆上的越界读写。
受影响版本
-
所有1.1.5至1.3.2版本(含)
-
Debian 11和Ubuntu 22.04上apt提供的默认版本
-
部署在Netgear Nighthawk RAX30上的版本,已应用最新补丁
触发漏洞的最小测试用例
此测试用例通过传递一个比发送的总请求长度大得多的巨大值(0xffffff),触发该漏洞,导致越界读取请求缓冲区分配的末尾并进入未映射内存。应用程序应该会因段错误(Segmentation Fault)而崩溃。
GET /status HTTP/1.0\r\nTransfer-Encoding:chunked\r\n\r\nffffff\r\n0\r\n\r\n
发现
我最初是在进行对Netgear RAX45的漏洞挖掘时,通过模糊测试发现了这个漏洞。我对代码库不够熟悉,无法准确判断应该在哪里进行模糊测试,因此选择了最容易接触的代码部分——HTTP请求处理。模糊测试是通过LibFuzzer和AFL++结合自定义的测试框架进行的。我对代码做了一些小的修改,以提高其可模糊测试性,包括去除了网络读写功能,但其他方面没有做任何更改。
用于发现这个特定漏洞的核心测试框架代码如下所示。完整的框架代码和其他辅助代码会很快发布。
#include <stdio.h>
#include <stdint.h>
#include <stddef.h>
#include "minixml.h"
#include "upnphttp.h"
#include "upnpsoap.h"
#include "containers.h"
#include "upnpreplyparse.h"
#include "scanner.h"
#include "log.h"
void ProcessHttpQuery_upnphttp(struct upnphttp *);
int LLVMFuzzerTestOneInput(char *buf, size_t size)
{
struct upnphttp *h = New_upnphttp(1); // 创建 upnphttp 结构体实例
const char *endheaders;
h->req_buf = (char *)malloc(size + 1); // 为 HTTP 请求数据分配内存
if (!h->req_buf)
{
return 0; // 如果内存分配失败,返回
}
memcpy(h->req_buf, buf, size); // 将传入的模糊数据复制到请求缓冲区
h->req_buflen = size;
h->req_buf[h->req_buflen] = '\0'; // 结束符
/* 查找字符串 "\r\n\r\n" */
endheaders = strstr(h->req_buf, "\r\n\r\n");
if (endheaders)
{
h->req_contentoff = endheaders - h->req_buf + 4; // 找到 HTTP 请求头结束的位置
h->req_contentlen = h->req_buflen - h->req_contentoff; // 计算请求体的长度
ProcessHttpQuery_upnphttp(h); // 处理 HTTP 请求
free(h->req_buf); // 释放内存
free(h->res_buf);
free(h);
return 0;
}
free(h->req_buf); // 释放内存
free(h->res_buf);
free(h);
return -1; // 如果没有找到有效的 HTTP 头,返回 -1
}
代码解析
-
引入头文件:
代码包含了多个头文件,这些头文件提供了必需的函数、数据结构和工具,用于处理HTTP请求、日志记录和其他UPnP相关功能。 -
LLVMFuzzerTestOneInput
函数:
这是模糊测试的核心函数,LLVMFuzzerTestOneInput
接受两个参数:buf
(模糊输入数据)和size
(数据大小)。该函数的作用是模拟HTTP请求的处理过程,以便测试是否存在漏洞。-
struct upnphttp *h = New_upnphttp(1);
创建一个upnphttp
结构体实例,初始化时传递参数1
,可能代表请求的版本或其他配置。 -
h->req_buf = (char *)malloc(size + 1);
为HTTP请求数据分配内存,大小为传入的size
加1字节,用于存储请求体。 -
memcpy(h->req_buf, buf, size);
将传入的模糊数据复制到req_buf
缓冲区。 -
strstr(h->req_buf, "\r\n\r\n");
查找HTTP请求头的结束位置,HTTP头通常以\r\n\r\n
作为分隔符。 -
h->req_contentoff = endheaders - h->req_buf + 4;
计算请求体的偏移量,这意味着HTTP请求头后的内容就是请求体。 -
ProcessHttpQuery_upnphttp(h);
调用函数处理HTTP请求。这是代码的关键部分,实际上会执行HTTP请求的解析和响应过程。 -
最后,
free
函数释放了分配的内存。
-
-
漏洞利用的背景:
经过几天的模糊测试和对框架的调整,我发现了几个可能导致崩溃的情况。其中有一个异常值得注意:由于Netgear糟糕的GPL代码发布实践,我发现实际测试的设备(RAX45)运行的版本比其在GPL包中提供的版本要新,并且是一个定制的分支,显然已经修复了这些漏洞。我通过反向分析从设备中提取的二进制文件确认了这一点。
更令人惊讶的是,Netgear在自己的内部分支中修复了这些漏洞,却没有将这些修复提供给GPL包,甚至没有向上游提交。这种做法令人非常震惊。发现这一点后,我决定转向MiniDLNA的最新代码库,并确认该版本也存在相同的漏洞。
根本原因分析
漏洞代码在处理包含Transfer-Encoding: chunked
HTTP 头部的有效请求时被触发,且必须满足以下条件:
-
正确终止HTTP头部:HTTP头部必须以
\r\n\r\n
序列结束,这是标准的HTTP请求格式。 -
请求体末尾包含终止块:请求体的末尾必须包含一个块大小为0的终止块。这是chunked编码中用来标识请求体结束的标准做法。
-
块大小后跟随终止序列:块大小的后面必须有一个正确的
\r\n
终止序列。
漏洞发生的函数调用链如下所示,从Proces
4A评测 - 免责申明
本站提供的一切软件、教程和内容信息仅限用于学习和研究目的。
不得将上述内容用于商业或者非法用途,否则一切后果请用户自负。
本站信息来自网络,版权争议与本站无关。您必须在下载后的24个小时之内,从您的电脑或手机中彻底删除上述内容。
如果您喜欢该程序,请支持正版,购买注册,得到更好的正版服务。如有侵权请邮件与我们联系处理。敬请谅解!
程序来源网络,不确保不包含木马病毒等危险内容,请在确保安全的情况下或使用虚拟机使用。
侵权违规投诉邮箱:4ablog168#gmail.com(#换成@)