提要
该文档会一直处于更新当中,当状态为完毕后,才是更新完成。由于网络上关于该漏洞原理的分析文档和资源实在是太少,而本人关于该方向也才是刚入门,能力有限,所以复现需要的时间较长,需要补充和学习的东西较多,若大家有什么建议,欢迎评论区一起讨论进步。
漏洞简介
OpenWrt是一套针对嵌入式设备的Linux操作系统,libubox是其中的一个提供事件循环、二进制格式处理、Linux链表实现和JSON辅助处理的基础库。
该漏洞与标记的二进制数据的JSON转换部分(blob)有关。Blob属性有足够大的double类型数值,由blobmsg_format_json处理,会溢出为堆栈上分配的JSON输出指定的缓冲区数组。攻击者可以向blobmsg_format_json提供特制的二进制blob或JSON输入,从而在double值序列到JSON缓冲区时造成堆栈溢出。
libubox库是OpenWrt项目的核心组件,并在项目的其他部分使用。通过在项目的LXR[1]中查找上述易受攻击的blobmsg_format_json函数,可以看到这些相互依赖关系,该函数揭示了netifd、procd、ubus、rpcd、uhttpd中的引用。也就是说,依赖调用到blobmsg_format_json的有很多其它的部分,我们本次复现主要针对rpcd对这个漏洞的利用。
简单介绍一下rpcb
参考:
[OpenWrt Wiki] Security Advisory 2020-01-31-2 - libubox tagged binary data JSON serialization vulnerability (CVE-2020-7248)
论文:A Grounded Theory Based Approach to Characterize So! ware" Attack Surfaces
受影响的漏洞版本:
OpenWrt 18.06.0 - 18.06.6
OpenWrt 19.07.0-rc1 - 19.07.0
漏洞原理分析
本次我们选取的漏洞分析环境的版本是Openwrt18.06.5,具体的漏洞的源代码等内容我们在另一篇博客,关于CVE-2020-7982的漏洞复现中,已经提到了代码的位置,如下地址参考:
CVE-2020-7982 OpenWrt 远程命令执行漏洞学习(完结)_openwrt 漏洞-CSDN博客
本次实验要分析的源代码的参考地址如下,后续我们不再赘述
https://git.openwrt.org/?p=project/libubox.git;a=blob;f=blobmsg_json.c;h=ec8b482c30c96a355aca58651632bc509a16bedf;hb=HEAD
根据漏洞的介绍,我们需要先找到libubox的源代码
该项目的地址如下
https://git.openwrt.org/?a=project_list;pf=project
之后我们在blobmsg_json.h中可以找到我们想要的函数blobmsg_format_json
根据函数的内容声明,其会先调用blobmsg_format_json_with_cb这个函数,我们可以在blobmsg_json.c文件中找到这个函数
首先定义了一个strbuf的结构体s,并且初始化内容为0。strbuf
结构体通常用于在C语言中表示可变长度的字符串缓冲区。它通常包含有关缓冲区内容和大小的信息,以便可以方便地进行字符串操作。
一般而言,strbuf
结构体可能包含以下成员:
char *buf
:指向存储字符串数据的内存缓冲区的指针。size_t len
:当前字符串的长度。size_t alloc
:分配给缓冲区的内存空间大小。
之后其调用函数setup_strbuf 具体如下图
我们首先先找到blob_attr这个数据结构的结构样式,我们可以找到blob.h这个文件,我们在其中可以看到blob_attr这个结构,如下图所示
其中uint32_t是一个32位,也就是4字节的数据类型,而data是一个可变长度的数据类型,所以一般来说,原始的blob_attr结构的长度应该是32位。
那么首先setup_strbuf函数会调用blob_len函数来进行处理,我们同样可以在blob.h文件中找到函数blob_len,如下图
我们可以根据最开始的引用,
在文件utils.h中找到函数be32_to_cpu的结构,最终我们发现了两个
和
总的来说,其作用就是定义了一个内联函数 blob_len
,用于计算属性的有效载荷长度。通过 be32_to_cpu
宏将 attr->id_len
中的大端字节序转换为CPU本地字节序。CPU本地字节序则表示CPU处理器默认采用的字节序方式。因为不同的CPU架构可能采用不同的字节序,所以需要在不同字节序之间进行转换,以确保数据在不同平台上的正确解释和处理。这具体可以参考计组相关的知识。然后使用按位与操作符和 BLOB_ATTR_LEN_MASK
来提取有效载荷长度,并减去 struct blob_attr
的大小。其中我们在blob.h中看到了
这样一个定义,BLOB_ATTR_ID_MASK被定义为了0x7f000000,其二进制的形式是1111111000000000000000000000000,根据按位与的运算逻辑,本质就是把有效载荷数据的前7位进行按位与操作,而后续多出来的字节数据都会被置为0,最终其有效长度也是8x4bit=32bit,也就是4B的长度大小。
最终我们获得了attr的长度,将其赋值给s的len这样一个成员变量。
和其相关的漏洞原理我们也参考了论文中的一些内容
我们在数据包auc.c中可以看到如下的代码:
这段代码是一段 C 语言代码,看起来是基于 libubox 库进行的一些操作。根据代码的上下文,它似乎是在处理 HTTP 请求和响应的元数据信息。第 679 行通过调用 DPRINTF 函数打印出了一个变量 cl 的 status_code 值。第 680 行通过调用 blobmsg_format_json_indent 函数将 cl->meta 中的头部信息格式化为 JSON 并打印出来。第 681 行使用 blobmsg_parse 函数解析了 cl->meta 中的头部信息,并将解析结果存储在 tb 变量中。
我们可以结合libuboxblobmsg_json.h的具体内容来判断。
这个函数blobmsg_format_json_with_cb我们前面已经提到了,所以这里不赘述其内容。
这里除了用到了这个函数,过程中还会用到blobmsg_puts函数
其中对于第147行代码而言,这行代码使用了C语言中的memcpy
函数,其目的是将内存区域c的前len个字节复制到s->buf + s->pos所指向的内存区域中。在这里,s是一个结构体或者对象,buf和pos可能是该结构体中的成员变量,用来表示缓冲区和偏移量。那么漏洞就在这里,我们可以构造特殊的JSON数据传入,使其在将double数据序列化到JSON缓冲区的时候,导致缓冲区的溢出。
漏洞环境搭建
漏洞复现
payload
$ ubus call luci getFeatures '{ "banik": 00192200197600198000198100200400.1922 }'