0x00 前言

只是因为某宝上搜路由器跳出来的第一个设备是它,于是就选它了

型号是tplink-ax3000(TL-XDR3010易展版),固件版本是V8.0

搜了搜网上几乎一点分析资料没有,自己就试着写写看。

0x10 解包

0x11 获取固件

固件获取地址:https://resource.tp-link.com.cn/?filterClass=[23]&keyword=xdr3010

固件下载后用binwalk解开即可

我尝试过本地模拟,但是报错涉及的东西感觉实在太多,于是选择了购买真机,后续测试交互也是通过真机完成的,有师傅模拟成功的也欢迎来交流。

0x20 逆向分析

该路由器的大部分服务由dms程序提供,因此主要分析该程序。

0x21 服务注册

由于一般最开始想到还是http服务,且该程序中留有大部分符号,直接对http进行搜索看是否有相关函数。

可以发现有一个httpdServiceInit的函数,在函数末尾调用了registerAppModule函数且参数为uhttpdService结构体,看上去是用于注册http服务的。

registerAppModule函数中,传入的结构体参数先是被复制到ModulerRoot + 216的位置,然后获取该地址偏移+4的内容并作为函数去调用。

由此推断结构体的第二个成员为该服务的注册函数。

点进该uhttpdService结构体,可以看到其中四个函数,其中sub_62a040即为注册函数

为什么是注册函数而不是初始化函数,因为点进去可以发现上面的函数输出日志的时候写着uhttpdRegister(笑

而结构体第二个函数是做了一系列的初始化

(你可能会好奇httpServiceInit函数是在哪里被调用的,对该函数做个交叉引用,可以发现它在一堆函数里面夹着,往上翻翻其实就能发现这个函数表是初始函数表,给libc_csu_init用的,程序刚开始会把函数表里的函数都运行一遍)

0x22 数据模块注册

先看http的注册函数

开头调用了不少regOprInterface函数,并且传了个像路径一样的一参,二参则为函数数组

我这边已经重命名过了,实际上也是比较好分析出来的。

第一个函数在给参数传完值后就调用了第二个函数,而第二个函数很明显是增添一个json对象,并根据传入的参数向该对象中添加键值对。

第三个函数则是从json对象中根据键名提取值,并放入传入的a3缓冲区中

剩下的那些register函数暂时不用看

registerVerNotify:
二参为 1 时,为一参数据模块注册验证函数,是系统在配置修改前调用的函数,用于判断某个配置值是否有效或允许变更。
二参为 0 时,为一参数据模块注册通知函数,是系统在配置修改后调用的函数,用于通知模块配置已更新,可以执行相应动作。

0x23 http服务

进入到http的初始化函数,就是上面结构体的第二个函数sub_629F8C

重点看httpParserInit

该函数用httpMethodAdd注册了两种http方法的处理函数,具体过程是将如下结构体添加进一个全局数组中,后续通过在数组中的位置来(即传入的一参index)检索对应结构体

image-sfkl.png

底下的httpKeyParserAdd就是字面意义上的解析报文header了。

这边都只是初始化相关的处理函数,那真正从头开始的start函数在哪里呢?

进入uhttpdService结构体的第三个函数

日志直接告诉你是start函数了,我这里直接给出大概的执行流了,只是肉眼观察到的出现的顺序,不是执行的顺序(?

httpRemoteGroupListenPort -> httpHandle -> httpParser

进到httpParser函数。这个函数做了一堆操作,主要就是解析用户发送的报文,然后把对应的数据放到a1结构体的对应偏移上去

偏移  含义
+8      请求 URL host 起点
+12     请求来源?(2 表示特定来源)
+20     http错误状态码
+24     请求资源标识(文件后缀对应的序号)
+96     缓冲区总大小  int
+100    缓冲区起始地址?    char* / void*(或可能是 offset)
+104    当前写指针/有效数据末尾地址  unsigned int
+112    userAgent
+140    数据备份(保存 a1+100 的值)  int
+144    当前有效数据开始地址  _BYTE*
+148    url 存储请求路径
+232    URL 参数键值对
+312    error_code
+316    匹配模块 ID
+320    请求方法(GET=1, POST=2等)
+324    HTTP 版本(1=1.0, 2=1.1)
+356    http_password
+392    解码后的 stok
+524    recvBufCache

大概分析了上面这些,仅供参考,可能有误,就不一一细说了。

这个函数里面也做了一些关键数据的解析,像methodurltoken,还调用了各种header的处理函数

然后在这里,解析完成后,就要根据方法来调用对应的处理函数了,像GET,POST之类的

image-grzg.png

这些之前已经在httpParserInit函数中看过了,那里用httpMethodAdd注册了两个method的处理。如果对该函数进行交叉引用,可以看到更多method及其处理

image-osdl.png

可以发现upnp相关的一些服务也是通过httpd来进行分配处理的(深入分析的话可以知道端口其实是正常开放的,但是会把报文数据转交给http进行解析,然后再根据具体方法传给对应处理函数)

这里再写一下会遇到的modelwritemodelread函数,其实就是之前regOprInterface注册的数据模块的处理函数

modelwrite:调用指定数据模块的func[1]来修改json值
modelread: 调用指定数据模块的func[2]来获取json值

POST 方法

就只简单写一下post方法了

image-rres.png

函数在开始将解析后的url与一个字符串数组里的字符串作比较,如果有相同url则 会进入httpCodeHandle函数,如果没有则会进入底下的httpProcDataSrv函数image-ivhg.png

httpCodeHandle没什么好说的,看一下httpProcDataSrv

image-dmjs.png

再结合实际抓包得到的数据

image-eett.png

该函数处理POST数据时,会解析作为POST datajson对象。并且根据该对象中的method(此method非彼method,一个是httpmethod,一个是POST内部的method)确定处理函数,并且将上面的数据作为参数传入。

httpDoAuthorize

这里再说说这个验证函数,在各个处理函数中基本都能看到,想要做到部分未授权就需要对这个函数进行绕过。

在部分老版本中,在对GET方法进行权限认证时,如果url中含有loginLess,Login.htm,即可直接通过验证。所以可以通过在url的将loginLess添加在url的末尾,就像/admin/system/download_confloginLess,来绕过认证下载配置文件,然后从配置文件中提取管理员密码。

image-pbxv.png

而在新版中,修改了判断方式,使用了strncmp,所以/loginLess/就只能放在url的最开头,而且用,就不太好像上面一样拼接路径了。

image-osyn.png

httpMGetHandle

再再说一下这个函数,里面有个有意思(存疑),但没啥用的 目录穿越 + 部分文件读。

该函数将处理过后的url交给 miniFsReadFileDirect函数,最终是通过fopen来读本地文件,而没有对../进行过滤,存在个目录穿越。

图中可以看到我标了个调用链,其中*(a1+24)需要大于5才能触发读文件,那么这个成员代表的是什么呢?

如果你还记得我上面有张表写了一长串的结构体成员含义,就知道该到哪个函数里去看了。

回到httpParser函数

图片-lvka.png

就是在调用不同方法的处理函数之前,程序对url进行了一个后缀名的匹配,如果url的末尾是指定的后缀名,则会将a1[6]赋值,该值为后缀名列表的中每个后缀名后的index(别问为什么上面是*(a1+24),这里是a1[6],问就是记得把鼠标放a1上看看类型)

.binindex为1,.xmlindex为5。当这个值大于等于5时,才会跳过验证。

虽然可以任意目录泄露,但是后缀名卡在这了,所以大概是没啥用

0x30 最后

差不多就分析到这,只是把大致架构和http服务相关的东西写了写,后续漏洞挖掘请师傅们自行探索了。

浇浇我,我什么都会做的