2020强网杯决赛Cisco路由器

2020强网杯决赛Cisco路由器

九月 18, 2020 阅读数

题目相关

题目描述:挖掘并利用CISCO RV110W-E-CN-K9(固件版本1.2.2.5)中的漏洞,获取路由器的Root Shell,实现DNS劫持。

靶机环境:CISCO RV110W-E-CN-K9(固件版本1.2.2.5),设置后台管理密码、WIFI密码,默认关闭telnet。

附件信息:附件中提供了路由器固件(固件版本1.2.2.5,与靶机版本一致),路由器设备后台管理账号为cisco,密码为qwb2020 ,WiFi密码未设置。

展示环境拓扑:展台路由器WAN口连接互联网,选手攻击机和操作员验证机通过网线连接路由器LAN口,IP地址设置自动获取。

展示目标:选手携带自己的攻击机上台,通过有线方式连接路由器设备。在规定的时间内攻击展台路由器,获取路由器的Root Shell执行系统命令,劫持http://www.baidu.com为如下页面:

<html><br><br><br><h1 style="font-size:100px;color:red;" align="center">Hacked by BOI</h1></html>

操作人员使用验证机中的Firefox浏览器访问http://www.baidu.com查看攻击效果,确认实现DNS劫持后判定成功。

注意事项:上台展示题解的时候注意关闭exp的调试信息。

展示时操作人员操作步骤:

1) 重启路由器设备;

2) 将互联网网线连接路由器WAN口;

3) 验证机通过有线方式连接路由器设备LAN口;

4) 设置验证机的DNS服务器IP地址为路由器IP;

5) 清除浏览器历史记录,清除本机的DNS缓存;

6) 等待选手连接路由器;

7) 等待选手攻击;

8) 在规定时间内可以配合选手重启路由器设备(每次重启首先要重复步骤4,5);

9) 选手攻击完毕后,操作人员使用验证机中的浏览器访问网页验证效果;

10)攻击成功或超时后:关闭路由器。

解题思路

根据题目要求,攻击机通过lan口连接,并没有提供用户名密码,登录不了管理界面,所以需要做到三点:前台身份验证绕过,后台命令执行或代码执行,DNS劫持。也可以改成两点:未授权命令执行或未授权代码执行,DNS劫持。

现有的?

先找找有无公布exp的前台代码执行和命令执行的CVE,链接

CVE-2019-1663(未授权,远程代码执行)< 1.2.2.1

CVE-2020-3323 (RCE,远程命令执行) < 1.2.2.8

CVE-2020-3331 (RCE,任意代码执行) < 1.2.2.8

CVE-2020-3330(默认系统用户凭证) < 1.2.2.8

CVE-2020-3144 (身份验证绕过,命令执行) < 1.2.2.8

只有CVE-2019-1663有exp,而且msf集成了该漏洞利用模块,直接用msf测试一波发现无法使用了,说明1.2.2.5版本补了这个漏洞。其他的CVE是2020年7月份Cisco刚公布的严重漏洞,网上还没有利用代码。

其实想想也可以知道,出题的师傅肯定做了充分的调研,不可能让大家这么简单找到exp直接使用的。

手工挖0day?

主办方提供了螺丝刀套装、焊台、万用表、TTL小板和飞线,很明显就是让你拆机了。

没有能直接使用的未授权RCE,那就只能代码审计补丁比对找0day了,代码审计也不是盲目的代码审计。结合补丁比对能够迅速定位厂商修补了哪些未公开的代码。

CVE-2019-1663的漏洞点是login.cgi的strcpy函数。GDB+IDA+burp吃饭三件套复现该漏洞,这里不详细描述啦!

代码审计

主要的思路是定位危险函数:system()、Popen()、strcpy()、strcat()、sprintf()等。

命令注入漏洞

命令注入漏洞是优先考虑的漏洞,这种漏洞在路由器等嵌入式设备中出现的可能性很高。

  1. 这些设备在配置和使用过程中与底层硬件的交互非常多,开发者为了方便通常直接使用system函数来执行配置命令。常见的命令注入点主要有:诊断页面中的ping输入框,开发者经常未对用户输入进行过滤,直接传递给system函数或popen函数,进而导致命令注入。
  2. 命令注入漏洞相较于缓冲区溢出漏洞而言,调试起来更简单,使用burpsuite进行调试即可。
  3. 很多命令注入漏洞无需认证,能够直接达到RCE的效果。

缓冲区溢出漏洞

主要是栈溢出,栈溢出漏洞在路由器等小型嵌入式IoT设备中的占比尤其高,主要是因为开发者直接使用危险函数处理用户输入,导致攻击者能够覆盖返回地址,劫持程序控制流,达到任意代码执行的目的。

  1. 定位strcpy()等危险函数。
  2. 判断输入对应的cgi时候是否有相应的URI,这样就能利用Burp构造相应数据包进行测试,进而了解如何触发该漏洞。
  3. 判断输入点距离ra的偏移量,一般情况下能够在IDA看出来,实在判断不了,只能通过上传相应的gdb,进行调试,同时判断出libc的基地址。
  4. 最后构造完整的rop链进行不断测试。

补丁比对

通过比对最新版本1.2.2.8与当前版本1.2.2.5版本能够发现厂商自行修补但未公布的漏洞。

这里主要使用bindiff工具,结合IDA pro进行补丁比对。找到其中相同函数名,但是相似度不高的函数进行分析。

本次的漏洞点是通过补丁比对找到的,能够清楚的找到在最新版本修复掉的危险函数。

在guest_logout_cgi函数中,1.2.2.8版本使用限定了拷贝大小的strncpy来替换危险函数sscanf。

漏洞定位与分析

binwalk解压后,针对web服务器二进制程序usr/sbin/httpd进行分析。

httpd中guest_lougou_cgi()函数的sscanf函数。

危险函数sscanf()将第一个参数的内容按照第二个参数的格式传递给后面的参数所对应的缓冲区中。而后两个缓冲区的大小都为0x40。

第二个参数所对应的正则表达式含义:%个数表示匹配参数的个数,第一个%[^;];表示取”;”之前的字符串,第二个%*[^=]=表示”=”之前的字符串被过滤。整个sscanf的含义为:将haystack中的内容按照”;”之前的内容给变量acStack108,”;”到”=”之间的内容忽略,”=”之后的内容到换行”\n”之前的内容给变量acStack172。这个时候往前回溯可以发现要执行这个sscanf(),需要在haystack中找到status_guestnet.asp这个字符串。

而haystack这个缓冲区中的内容是通过get_cgi函数获取submit_button后的内容,

并且通过fprintf打印的内容可以看出,submit_button对应的内容为_haystack,url对应的内容为acStack108,session_id的内容为acStack172。

通过fprintf打印的内容为submit_button对应的内容为_haystack,url对应的内容为acStack108,session_id的内容为acStack172。

同时要求_haystack中的内容包含status_guestnet.asp;第一个分号”;”之前的内容给acStack108,分号到等号之间的内容忽略,等号之后的内容给acStack172。所以可以这样构造数据包:

submit_button=status_guestnet.asp;seesion_id=xxxxxxxxxxxxxxxx。这样执行完sscanf之后acStack108中的内容为status_guestnet.asp,acStack172内容为xxxxxxxxxxxxxxxx。因为acStack172的大小为0x40而没有对submit_button之后的_haystack内容长度做限制,所以能够达到缓冲区溢出的目的。

漏洞利用

确定偏移

距离返回地址的偏移可以直接通过IDA中的汇编代码获得。

acStack172的内容是通过寄存器s5传递而来,acStack108的内容是通过寄存器fp中传递过来。

往上查找发现fp是$(sp+0x7c),所以acStack108的内容从$(sp+0x7c)开始。

s5是$(SP+0xe8+var_AC)=$(SP+0x3c),acStack172的内容从$(SP+0x3c),在acStack108下方0x40处,会被acStack172覆盖。

而返回地址ra保存在$(sp+0xe8+var_4)=$(sp+0xe4)

所以acStack172输入的位置距离ra的偏移量为0xac-4=0xa8。

构造ROP链

因00截断,使用libc库中的gadget。

libc.so.0中的system函数地址为0x4c7E0。

使用mipsrop插件寻找libc.so.0中gadget。因为寻找将栈地址拷贝到寄存器的gadget,应使用mipsrop.stackfinders()命令。0x257A0处的gadget作用是使a0指向当前栈指针SP+0x18的位置,然后跳转至s0处。

guest_logout_cgi()函数在返回时,栈上的值能够覆盖s0-s7,fp寄存器。所以s0也能被控制。

payload形式如下:

漏洞调试

拆开路由器,很容易发现uart串口。

可以看到路由器的启动过程。

接下来就是上传gdbserver调试或者直接上传gdb调试。

在jr ra的地址断下来,能够看到ra以及被覆盖。

调试过程中发现虽然地址随机化参数为1,但是libc的基地址一直保持不变为0x2af98000。

并且因为usr/sbin目录下存在utelnetd,所以构造的cmd命令可以为utelnetd -l /bin/sh -p 2222。并且每次栈溢出后都会将其中的https服务打down,需要重启httpd服务。

劫持DNS

在成功getshell之后,考虑如何劫持DNS,这里的方法有很多。

大体步骤是:

  1. 利用bind等类型软件搭建DNS服务器,并提供公网地址IP。

  2. 配置DNS服务器,将所有解析域名为www.baidu.com的请求,劫持为下列页面。

    <html><br><br><br><h1 style="font-size:100px;color:red;" align="center">Hacked by BOI</h1></html>
  3. getshell后,执行下列命令:

    echo "nameserver 搭建的DNS服务器公网IP" > /tmp/resolv.conf;
    killall dnsmasq;
    /usr/sbin/dnsmasq -C /tmp/dnsmasq.conf -r /tmp/resolv.conf

之后让工作人员的验证机通过lan口连接路由器, 设置验证机的DNS服务器IP地址为路由器IP,清除浏览器历史记录,清除本机的DNS缓存,使用Firefox浏览器访问www.baidu.com。