安全路透社
当前位置:安全路透社 > 安全客 > 正文

【技术分享】一个针对TP-Link调试协议(TDDP)漏洞挖掘的故事

翻译:backahasten

前言


我写这篇文章原本是为了简化WiFi渗透测试研究工作。我们想使用去年由Core Security发布的WIWO,它可以在计算机网络接口和WiFi路由器之间建立一个透明的通道。

研究的第一步,就是选取一个合适的工具, 在此研究中,我会首先选择一个适当的路由器进行改造。

经过一段时间的考察,我选择了TP-Link的TL-WA5210g无线路由器,它允许安全自定义固件,(这样我就能安装之前提到的工具),同时,他也是一个室外路由器(最初的设计就是如此)。

http://p3.qhimg.com/t01a6b7504343c3d585.png

我首先发现了一个问题,我所购买的路由器的硬件版本(2.0版本)和可以自定义固件的版本不符,这样安装WIWO的时候可能就会有麻烦。我也读了一些博客,有时候虽然硬件版本不同,但是固件可以相同。不幸的是,我手里的设备不可以。设备的硬件和固件版本是配套的,我也没发现降级固件的办法,(TL-WA5210G_V2_140523)当我尝试使用Web界面降级的时候,发生了错误。

http://p5.qhimg.com/t0140a904e65b984c16.png


使用串口


我暂时放下了最开始的目标,转而,我试图发现一种方法来控制设备,安装我所需要的软件。我的第一个方法是使用UART串口通信的方法,我把UART的引脚焊接上排针,使用Bus Pirate(我的选择)或类似的东西把他连接到电脑,看看我能做什么。(这一步需要拆开外壳,在绿色的电路板上操作)

http://p6.qhimg.com/t0146e786617e1bc092.png

连接到UART串口之后,我发现了一个可以运行有限命令的控制台,它的一些选项被禁用了,比如第一个和第二个。

http://p7.qhimg.com/t019a379289aab63f25.png

source: https://forum.openwrt.org/viewtopic.php?id=17252&p=6

我尝试了其他不同的数字和字母,希望找到隐藏功能,虽然我发现了一些,但是没发现对我有用的。

第二种方法是分析web程序,我发现很多UART控制台也可以从web界面的菜单中进入,这在OpenWRT的wiki中有介绍。对于我手中的设备,我发现了隐藏的菜单:

http://192.168.1.254/userRpm/NatDebugRpm26525557.htm


发现漏洞


在上一步发现的隐藏菜单中,有很多按钮,他们显示了设备很多有趣的信息。有一个默认开启的UDP端口引起了我的注意。

http://p9.qhimg.com/t01e25703d7165d44a8.png

做了一些研究之后,我了解到了这个UDP端口上运行的协议:

https://www.google.com/patents/CN102096654A?cl=en

TDDP是一个用于调试的简单协议,这个协议使用一个数据包,在载荷中使用不同的消息类型来完成请求或者命令的传递。下面的图片是TDDP的数据包信息。

http://p3.qhimg.com/t019920edf0db56f0d9.png

我还发现文档中记录了其他一些消息类型。如果想从设备中获得一些有关设备状态的信息,调试信息是十分有用的。我想知道我到底可以做什么,于是我从TP-Link的官网下载了它的固件。

下载完成之后,我使用IDA 开始搜索有关于与实现协议的代码,来确定文档中的协议是如何在固件中实现的。我已经找到了第二版协议的说明,但是,通过逆向我发现他和第一版是有差别的。

http://p4.qhimg.com/t01c7010a4d1c3fbf9b.png

由于没有第一版协议的说明,我决定逆向协议处理部分的程序,了解二者的主要差异。虽然包的结构相似,但是,我还是发现了一些重要区别:第一版不支持身份验证和对数据包载荷的加密,而第二版要求身份验证和加密。

分析V1版的处理程序,我发现V2的一些处理程序也出现在V1里,他们是set_configuration, get_configuration 和 set_macaddr。

1.set_configuration用来设施设备配置

2.get_configuration用来获取设备配置

http://p5.qhimg.com/t0190c605291db8469f.png

基于我目前所知道的,我已经准备好写一个实现TDDP V1协议最小功能的Python脚本,我首先将注意力集中在get_configuration请求上,希望可以收集我所需要的信息。

使用脚本发送数据包之后,硬件的返回看似关键值配置文件。阅读这个文件,我们竟然发现了账号和密码。(我们在读取配置信息的时候没有要求身份验证)

http://p0.qhimg.com/t01a314a6a204e20142.png

虽然很有趣,但是我们还只是拿到了设备的配置文件。我们依然离我们的目标——安装一个自定义的固件相去甚远,而这才是我真正想做的。再次阅读文档,我想一个旨在调试的协议很可能有很多问题出现。我继续逆向处理程序的其他部分,几个小时后,当我在深入研究set_configuration的时候,我发现了一个类strcpy风格的函数,导致了一个简单的溢出漏洞。

http://p9.qhimg.com/t0140743fddb271e9a5.png

经过初步的分析,我发现利用这个漏洞的shellcoder有一些限制,例如,不能出现0或者空格。

通过这个漏洞,我可以劫持TDDP服务的执行流,指向自己的代码,然后在更新功能上打上补丁,允许安装我自己需要的固件(包括旧版本)。

影响工作的主要问题就是设备中没有调试器,由于某些奇怪的原因,UART引脚也不工作了,我也不知道是为什么>_>。我可以从设备中获得的唯一信息就是PC寄存器的值和SP寄存器的值,他们在之前发现的web隐藏功能中。

下面的图片显示了进程列表是什么样子的,可以清楚的看见PC和SP的值。

http://p3.qhimg.com/t01de45ef6dee91875d.png

我准备使用“跳转调试”的方法写一个漏洞利用脚本,执行成功或者失败,jmp指令就会使PC跳转到不同的位置。

下面的图片是使用这种方法操作pc指针的例子:

http://p4.qhimg.com/t01de91a85f4e2cbdff.png

在这个例子中,文件描述符(0xa)被载入了寄存器,之后jmp指令被执行,这证明了这个寄存器控制着我们所需要的值。


设计漏洞利用代码


几天之后,利用之前描述的细节,我已经可以使用ROP 技术通过gadgets控制PC寄存器了,但是,当我准备让他运行我自己的代码的时候(我考虑了前面提到的限制因素),却失败了。

我之前从来没有写过MIPS架构的漏洞利用代码,在读过一篇文章【1】之后,我知道了为什么失败了,原因是MIPS的cache没有被刷新(此处涉及MIPS架构中“缓存一致性”处理—译者注),所以,写入的shellcoder是不能使用的,博客中所介绍的,解决该问题的办法是调用sleep()函数清除cache,但是在我的情况下,固件没有符号,识别sleep函数很难,为此,我开始学习MIPS的cache是如何工作的,我怎样可以清除它。

阅读这篇文章【2】让我知道MIPS的cache是双重的。

一个是数据cache(D-cache),另外一个是指令cache(I-cache),清除这两个cache的过程是:首先,设置协处理器的TagLi和TagHi为0,之后调用指令 “cache   8, 0($a0)”(清除I-cache)和指令“cache   9, 0($a0)”(清除D-cache)。

查看整个固件,我发现了一个函数的作用正是我想要的。(可能是用来初始化)

http://p8.qhimg.com/t0107d496601e4fc19b.png

http://p3.qhimg.com/t01560967691d1bc631.png

我发现这个代码有一个小问题。

http://p8.qhimg.com/t01ed1749677fa62b42.png

像固件中其他函数返回时一样,这个函数使用了jr $ra.作为结尾,$ra寄存器中的值是在这个函数被jalr调用时设置的,例如,你可以这样调用foo函数:

$ra寄存器的作用是为了确定返回地址。在这个例子中,返回命令使用jr,因为,和jalr指令相反,jr指令不设置$ra寄存器。

通常的解决方法是使用ROP 链(或者对于MIPS架构来说,说成JOP更好),设置$ra寄存器的值(例如0x12345678),然后接下来调用函数跳转到0x8016B910(cache清除函数),这个函数会清除cache,接下来就可以调用自己的函数了(例如0x12345678).

问题是$ra寄存器只会在函数结尾被设置,就像这样。

http://p1.qhimg.com/t01b600235dc0c4ad8c.png

前面的图片就是函数结尾,在这里,你可以看到,从栈中获得数值后,$ra寄存器用来返回调用者,如果我把它设置为0x8016b910(清除cache),我会失去控制,因为这会形成一个无线循环。

那么,怎么办?

我想到当I-cache被清除之后,新的指令会立刻被设置,这意味着我可以修改清除函数 (0x8016B910) 的结尾,用一下指令代替 “jr $ra ”  “jr $fp” (或者相似的),使用这种方法,我可以清除cache并且跳转进我的shellcoder。

下面显示了函数指令在攻击前后的变化;

http://p4.qhimg.com/t0141e9fe659658366d.png

最后,shellcoder的作用是在代码中打上补丁,再调用指令激活补丁,关闭固件检查。

和@_topo谈话之后,他建议我读关于MIPS的段布局,我之后发现可以使用kseg1 ,不适用kseg0,这样的话,MIPS的cache就可以避开 (http://cdn.imgtec.com/mips-training/mips-basic-training-course/slides/Me…)。我没有尝试。


后记


首先是一个好消息,这个服务不可能从广域网连接到,实际上,连接到wifi都无法访问,所以需要使用有线连接。第二,这个固件版本是2014年的。然而,在那时,这个固件是这个设备的最新版本。可悲的是,使用这个设备的人很多都没有升级。

对于其他TP-Link设备有很多新的固件,我们安装了适配于TL-MR3020的最新固件(2015年发布),这个服务依然存在,默认在监听端口。第一版的协议代码依然存在于这个固件中,虽然第一版的部分指令被删除(例如,获取配置文件的代码被删除了),我们不能确定新版是否存在我们发现的漏洞。我们需要研究。虽然我们没有设备测试,但是,通过静态分析2016年的固件,相同的情况还是存在的。

根据攻击的方案和设备的配置(再说一遍,WiFi连接和公网上无法访问这个服务),这个漏洞的最大作用可能就是允许攻击者更改路由器固件(可能包含持久性的后门),使他 可以从网络上获取信息。


结论


人们关注嵌入式安全已经有一段时间了,我们通常不会花很多时间关注所有常见的问题。

在无需身份确认的条件下,就可以访问一个默认开启的调试协议,是很糟糕的事情,厂家应该注意到这一点。通过上述的研究,我们可以发现,内存溢出漏洞很常见。成熟的安全防护方法也应该被应用于嵌入式中。例如,现在MIPS设备支持XI(Execute Inhibit)技术,他就和英特尔的NX技术相似,作用是阻止执行用户输入的数据。实际上,开发中也应该采用正确的方法,例如源码审计和渗透测试。


参考链接


【1】http://www.devttys0.com/2012/10/exploiting-a-mips-stack-overflow/

【2】http://cdn.imgtec.com/mips-training/mips-basic-training-course/slides/Caches.pdf


原文链接:https://www.coresecurity.com/blog/story-about-tp-link-device-debug-protocol-tddp-research

未经允许不得转载:安全路透社 » 【技术分享】一个针对TP-Link调试协议(TDDP)漏洞挖掘的故事

赞 (0)
分享到:更多 ()

评论 0

评论前必须登录!

登陆 注册