前言


我写这篇文章原本是为了简化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)漏洞挖掘的故事的更多相关文章

  1. WinDbg调试流程的学习及对TP反调试的探索

    基础知识推荐阅读<软件调试>的第十八章 内核调试引擎 我在里直接总结一下内核调试引擎的几个关键标志位,也是TP进行反调试检测的关键位. KdPitchDebugger : Boolean ...

  2. Win7 x86内核调试与TP反调试的研究

    参考  这两天对某P双机调试的学习及成果 ,非常好的一篇分析贴. 本文在Win7 x86下的分析,在虚拟机中以/DEBUG模式启动TP游戏,系统会自动重启. 0x01 内核调试全局变量  根据软件调试 ...

  3. [老文章搬家] [翻译] 深入解析win32 crt 调试堆

    09 年翻译的东西. 原文见:  http://www.nobugs.org/developer/win32/debug_crt_heap.html 在DeviceStudio的Debug编译模式下, ...

  4. 使用CLRMD编写一个自己的C#调试器

    博客搬到了fresky.github.io - Dawei XU,请各位看官挪步.最新的一篇是:使用CLRMD编写一个自己的C#调试器.

  5. 写一个针对IQueryable<T>的扩展方法支持动态排序

    所谓的动态排序是指支持任意字段.任意升序降序的排序.我们希望在客户端按如下格式写: localhost:8000/api/items?sort=titlelocalhost:8000/api/item ...

  6. 单片机裸机下写一个自己的shell调试器(转)

    源: 单片机裸机下写一个自己的shell调试器

  7. 用.netcore写一个简单redis驱动,调试windows版本的redis.平且给set和get命令添加参数.

    1. 下载windows版本的redis 2.开发环境vs2017  新建一个 .net core控制台. private static Socket socket = new Socket(Addr ...

  8. html5shiv 是一个针对 IE 浏览器的 HTML5 JavaScript 补丁,目的是让 IE 识别并支持 HTML5 元素。

    html5shiv 是一个针对 IE 浏览器的 HTML5 JavaScript 补丁,目的是让 IE 识别并支持 HTML5 元素. 各版本html5shiv.js CDN网址:https://ww ...

  9. Android系统移植与调试之------->如何添加一个adb wifi无线调试的功能【开发者选项】-【Wifi调试】

    首先弄懂怎么设置adb wifi无线调试的功能,如下所示. 1. 手机端开启adb tcp连接端口 :/$setprop service.adb.tcp.port :/$stop adbd :/$st ...

随机推荐

  1. ab使用详解—如何使用apache性能测试工具进行压力测试

    作为后端工程师,除了实现业务需求之外,需要考虑的就是自己写的服务,在大并发下是否能正常运行了.但是,在一般开发情况下,没那么多大并发情况让你测试,那该怎么办呢? 这时候,我们就可以用到apache的压 ...

  2. RPC(简单实现)

    笔者之前仅看过RPC这个单词,完全没有了解过,不想终于还是碰上了.起因:这边想提高并发量而去看kafka(最后折中使用了redis),其中kafka需要安装ZooKeeper,而ZooKeeper又与 ...

  3. StarUML之七、StarUML的Class Diagram(类图)示例

    UML 类图中的概念 类图关系:泛化(继承).实现.聚合.组合.关联.依赖 类图的详解可在网上查询(推荐https://zhuanlan.zhihu.com/p/24576502) 它描述了在一个系统 ...

  4. Mysql 字符串转数字类型

    使用场景: 在数据库中进行数字比较,但是数字的存储格式是varchar的时候可以使用以下方法进行转换,然后进行比较 方法一:SELECT CAST('123' AS SIGNED); 方法二:SELE ...

  5. Apache Solr JMX服务远程代码执行漏洞复现

    0x00 漏洞介绍 该漏洞源于默认配置文件solr.in.sh中的ENABLE_REMOTE_JMX_OPTS配置选项存在安全风险. Apache Solr的8.1.1和8.2.0版本的自带配置文件s ...

  6. Python 编程入门(2):复杂数据类型(列表,字典)

    以下所有例子都基于最新版本的 Python,为了便于消化,每一篇都尽量短小精悍,希望你能尽力去掌握 Python 编程的「概念」,可以的话去动手试一下这些例子(就算目前还没完全搞懂),加深理解. 在 ...

  7. clr via c# 定制特性

    1,特性的应用范围:特性可应用于程序集,模块,类型,字段,方法,方法参数,方法返回值,属性,参数,泛型参数 2,利用前缀告诉编译器表明意图---下面的倾斜是必须的表明了我们的目标元素: [assemb ...

  8. Oracle查询如何才能行转列?-sunziren

    原创文章,转载务必注明出处. 今天工作的时候,碰到一个问题,涉及oracle行转列,用了半小时解决,因此在这里写个博客记录一下解决办法. 原数据库表的数据是: 想要达到的效果是: 经过思考,这是一个o ...

  9. 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 ...

  10. Jupyter Notebook 常用快捷键 (转)

    Jupyter Notebook 有两种键盘输入模式. 编辑模式,允许你往单元中键入代码或文本:这时的单元框线是绿色的. 命令模式,键盘输入运行程序命令:这时的单元框线为蓝色. 命令模式 (按键 Es ...