我的翻译--一个针对TP-Link调试协议(TDDP)漏洞挖掘的故事
前言
我写这篇文章原本是为了简化WiFi渗透测试研究工作。我们想使用去年由Core Security发布的WIWO,它可以在计算机网络接口和WiFi路由器之间建立一个透明的通道。
研究的第一步,就是选取一个合适的工具, 在此研究中,我会首先选择一个适当的路由器进行改造。
经过一段时间的考察,我选择了TP-Link的TL-WA5210g无线路由器,它允许安全自定义固件,(这样我就能安装之前提到的工具),同时,他也是一个室外路由器(最初的设计就是如此)。
我首先发现了一个问题,我所购买的路由器的硬件版本(2.0版本)和可以自定义固件的版本不符,这样安装WIWO的时候可能就会有麻烦。我也读了一些博客,有时候虽然硬件版本不同,但是固件可以相同。不幸的是,我手里的设备不可以。设备的硬件和固件版本是配套的,我也没发现降级固件的办法,(TL-WA5210G_V2_140523)当我尝试使用Web界面降级的时候,发生了错误。
使用串口
我暂时放下了最开始的目标,转而,我试图发现一种方法来控制设备,安装我所需要的软件。我的第一个方法是使用UART串口通信的方法,我把UART的引脚焊接上排针,使用Bus Pirate(我的选择)或类似的东西把他连接到电脑,看看我能做什么。(这一步需要拆开外壳,在绿色的电路板上操作)
连接到UART串口之后,我发现了一个可以运行有限命令的控制台,它的一些选项被禁用了,比如第一个和第二个。
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端口引起了我的注意。
做了一些研究之后,我了解到了这个UDP端口上运行的协议:
https://www.google.com/patents/CN102096654A?cl=en
TDDP是一个用于调试的简单协议,这个协议使用一个数据包,在载荷中使用不同的消息类型来完成请求或者命令的传递。下面的图片是TDDP的数据包信息。
我还发现文档中记录了其他一些消息类型。如果想从设备中获得一些有关设备状态的信息,调试信息是十分有用的。我想知道我到底可以做什么,于是我从TP-Link的官网下载了它的固件。
下载完成之后,我使用IDA 开始搜索有关于与实现协议的代码,来确定文档中的协议是如何在固件中实现的。我已经找到了第二版协议的说明,但是,通过逆向我发现他和第一版是有差别的。
由于没有第一版协议的说明,我决定逆向协议处理部分的程序,了解二者的主要差异。虽然包的结构相似,但是,我还是发现了一些重要区别:第一版不支持身份验证和对数据包载荷的加密,而第二版要求身份验证和加密。
分析V1版的处理程序,我发现V2的一些处理程序也出现在V1里,他们是set_configuration, get_configuration 和 set_macaddr。
1.set_configuration用来设施设备配置
2.get_configuration用来获取设备配置
基于我目前所知道的,我已经准备好写一个实现TDDP V1协议最小功能的Python脚本,我首先将注意力集中在get_configuration请求上,希望可以收集我所需要的信息。
使用脚本发送数据包之后,硬件的返回看似关键值配置文件。阅读这个文件,我们竟然发现了账号和密码。(我们在读取配置信息的时候没有要求身份验证)
虽然很有趣,但是我们还只是拿到了设备的配置文件。我们依然离我们的目标——安装一个自定义的固件相去甚远,而这才是我真正想做的。再次阅读文档,我想一个旨在调试的协议很可能有很多问题出现。我继续逆向处理程序的其他部分,几个小时后,当我在深入研究set_configuration的时候,我发现了一个类strcpy风格的函数,导致了一个简单的溢出漏洞。
经过初步的分析,我发现利用这个漏洞的shellcoder有一些限制,例如,不能出现0或者空格。
通过这个漏洞,我可以劫持TDDP服务的执行流,指向自己的代码,然后在更新功能上打上补丁,允许安装我自己需要的固件(包括旧版本)。
影响工作的主要问题就是设备中没有调试器,由于某些奇怪的原因,UART引脚也不工作了,我也不知道是为什么>_>。我可以从设备中获得的唯一信息就是PC寄存器的值和SP寄存器的值,他们在之前发现的web隐藏功能中。
下面的图片显示了进程列表是什么样子的,可以清楚的看见PC和SP的值。
我准备使用“跳转调试”的方法写一个漏洞利用脚本,执行成功或者失败,jmp指令就会使PC跳转到不同的位置。
下面的图片是使用这种方法操作pc指针的例子:
在这个例子中,文件描述符(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)。
查看整个固件,我发现了一个函数的作用正是我想要的。(可能是用来初始化)
我发现这个代码有一个小问题。
像固件中其他函数返回时一样,这个函数使用了jr $ra.作为结尾,$ra寄存器中的值是在这个函数被jalr调用时设置的,例如,你可以这样调用foo函数:
$ra寄存器的作用是为了确定返回地址。在这个例子中,返回命令使用jr,因为,和jalr指令相反,jr指令不设置$ra寄存器。
通常的解决方法是使用ROP 链(或者对于MIPS架构来说,说成JOP更好),设置$ra寄存器的值(例如0x12345678),然后接下来调用函数跳转到0x8016B910(cache清除函数),这个函数会清除cache,接下来就可以调用自己的函数了(例如0x12345678).
问题是$ra寄存器只会在函数结尾被设置,就像这样。
前面的图片就是函数结尾,在这里,你可以看到,从栈中获得数值后,$ra寄存器用来返回调用者,如果我把它设置为0x8016b910(清除cache),我会失去控制,因为这会形成一个无线循环。
那么,怎么办?
我想到当I-cache被清除之后,新的指令会立刻被设置,这意味着我可以修改清除函数 (0x8016B910) 的结尾,用一下指令代替 “jr $ra ” “jr $fp” (或者相似的),使用这种方法,我可以清除cache并且跳转进我的shellcoder。
下面显示了函数指令在攻击前后的变化;
最后,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
(发表于360安全客 http://bobao.360.cn/learning/detail/3221.html)
我的翻译--一个针对TP-Link调试协议(TDDP)漏洞挖掘的故事的更多相关文章
- WinDbg调试流程的学习及对TP反调试的探索
基础知识推荐阅读<软件调试>的第十八章 内核调试引擎 我在里直接总结一下内核调试引擎的几个关键标志位,也是TP进行反调试检测的关键位. KdPitchDebugger : Boolean ...
- Win7 x86内核调试与TP反调试的研究
参考 这两天对某P双机调试的学习及成果 ,非常好的一篇分析贴. 本文在Win7 x86下的分析,在虚拟机中以/DEBUG模式启动TP游戏,系统会自动重启. 0x01 内核调试全局变量 根据软件调试 ...
- [老文章搬家] [翻译] 深入解析win32 crt 调试堆
09 年翻译的东西. 原文见: http://www.nobugs.org/developer/win32/debug_crt_heap.html 在DeviceStudio的Debug编译模式下, ...
- 使用CLRMD编写一个自己的C#调试器
博客搬到了fresky.github.io - Dawei XU,请各位看官挪步.最新的一篇是:使用CLRMD编写一个自己的C#调试器.
- 写一个针对IQueryable<T>的扩展方法支持动态排序
所谓的动态排序是指支持任意字段.任意升序降序的排序.我们希望在客户端按如下格式写: localhost:8000/api/items?sort=titlelocalhost:8000/api/item ...
- 单片机裸机下写一个自己的shell调试器(转)
源: 单片机裸机下写一个自己的shell调试器
- 用.netcore写一个简单redis驱动,调试windows版本的redis.平且给set和get命令添加参数.
1. 下载windows版本的redis 2.开发环境vs2017 新建一个 .net core控制台. private static Socket socket = new Socket(Addr ...
- html5shiv 是一个针对 IE 浏览器的 HTML5 JavaScript 补丁,目的是让 IE 识别并支持 HTML5 元素。
html5shiv 是一个针对 IE 浏览器的 HTML5 JavaScript 补丁,目的是让 IE 识别并支持 HTML5 元素. 各版本html5shiv.js CDN网址:https://ww ...
- Android系统移植与调试之------->如何添加一个adb wifi无线调试的功能【开发者选项】-【Wifi调试】
首先弄懂怎么设置adb wifi无线调试的功能,如下所示. 1. 手机端开启adb tcp连接端口 :/$setprop service.adb.tcp.port :/$stop adbd :/$st ...
随机推荐
- ab使用详解—如何使用apache性能测试工具进行压力测试
作为后端工程师,除了实现业务需求之外,需要考虑的就是自己写的服务,在大并发下是否能正常运行了.但是,在一般开发情况下,没那么多大并发情况让你测试,那该怎么办呢? 这时候,我们就可以用到apache的压 ...
- RPC(简单实现)
笔者之前仅看过RPC这个单词,完全没有了解过,不想终于还是碰上了.起因:这边想提高并发量而去看kafka(最后折中使用了redis),其中kafka需要安装ZooKeeper,而ZooKeeper又与 ...
- StarUML之七、StarUML的Class Diagram(类图)示例
UML 类图中的概念 类图关系:泛化(继承).实现.聚合.组合.关联.依赖 类图的详解可在网上查询(推荐https://zhuanlan.zhihu.com/p/24576502) 它描述了在一个系统 ...
- Mysql 字符串转数字类型
使用场景: 在数据库中进行数字比较,但是数字的存储格式是varchar的时候可以使用以下方法进行转换,然后进行比较 方法一:SELECT CAST('123' AS SIGNED); 方法二:SELE ...
- Apache Solr JMX服务远程代码执行漏洞复现
0x00 漏洞介绍 该漏洞源于默认配置文件solr.in.sh中的ENABLE_REMOTE_JMX_OPTS配置选项存在安全风险. Apache Solr的8.1.1和8.2.0版本的自带配置文件s ...
- Python 编程入门(2):复杂数据类型(列表,字典)
以下所有例子都基于最新版本的 Python,为了便于消化,每一篇都尽量短小精悍,希望你能尽力去掌握 Python 编程的「概念」,可以的话去动手试一下这些例子(就算目前还没完全搞懂),加深理解. 在 ...
- clr via c# 定制特性
1,特性的应用范围:特性可应用于程序集,模块,类型,字段,方法,方法参数,方法返回值,属性,参数,泛型参数 2,利用前缀告诉编译器表明意图---下面的倾斜是必须的表明了我们的目标元素: [assemb ...
- Oracle查询如何才能行转列?-sunziren
原创文章,转载务必注明出处. 今天工作的时候,碰到一个问题,涉及oracle行转列,用了半小时解决,因此在这里写个博客记录一下解决办法. 原数据库表的数据是: 想要达到的效果是: 经过思考,这是一个o ...
- warning: LF will be replaced by CRLF in
warning: LF will be replaced by CRLF in analysis/Result.csv. The file will have its original line en ...
- Jupyter Notebook 常用快捷键 (转)
Jupyter Notebook 有两种键盘输入模式. 编辑模式,允许你往单元中键入代码或文本:这时的单元框线是绿色的. 命令模式,键盘输入运行程序命令:这时的单元框线为蓝色. 命令模式 (按键 Es ...