ciscorv160 漏洞复现

IoT 1年前 (2023) admin
385 0 0

EDI

JOIN US ▶▶▶

招新


EDI安全的CTF战队经常参与各大CTF比赛,了解CTF赛事。

欢迎各位师傅加入EDI,大家一起打CTF,一起进步。(诚招web re crypto pwn misc方向的师傅)有意向的师傅请联系邮箱[email protected][email protected](带上自己的简历,简历内容包括但不限于就读学校、个人ID、擅长技术方向、历史参与比赛成绩等等。

点击蓝字 ·  关注我们

01

前言

继续上次rv340失败后,目标转换成rv160

02

漏洞信息

ciscorv160 漏洞复现

漏洞版本是低于1.0.01.04,所以我下载了1.0.01.03和1.0.01.04这俩个版本区间

03

环境搭建

依旧是选择unblob去对文件进行解压

查看路由设备系统架构

ciscorv160 漏洞复现

依旧是启动./start-armelhf.sh
#!/bin/bashsudo tunctl -t tap0 -u `whoami`sudo ifconfig tap0 192.168.2.1/24qemu-system-arm -M vexpress-a9 -kernel vmlinuz-3.2.0-4-vexpress -initrd initrd.img-3.2.0-4-vexpress -drive if=sd,file=debian_wheezy_armhf_standard.qcow2 -append "root=/dev/mmcblk0p2 console=tty0" -net nic -net tap,ifname=tap0,script=no,downscript=no -nographicz
在qemu中需要设置,
ifconfig eth0 192.168.2.2/24
然后测试是否能互相ping通
ciscorv160 漏洞复现

然后回到文件中
首先将文件系统压缩(避免scp传文件时扰乱文件中的软连接)tar zcvf 1.tar rootfs/然后将文件系统上传至qemusudo scp -r 1.tar root@192.168.2.2:/root/

rootfs是我改的名字,这个不需要在意

在qemu中

tar zxvf 1.tarchmod -R 777 rootfscd rootfsmount --bind /proc procmount --bind /dev devchroot . sh
ciscorv160 漏洞复现

这样的话我们久完成了基础的环境搭建,下面我们需要的时候运行起该款路由器的web服务

首先我们需要查看系统的web服务器

ciscorv160 漏洞复现

这里可以看到 该款路由器的web服务名称是mini_httpd,我们启动这个服务岂可成功启动web服务

首先我们先运行./etc/init.d/mini_httpd.init这个文件,因为etc/init.d/目录下存放的大多是开机自启动的服务,所以为了避免后面的错误,我们先运行这个文件

./etc/init.d/mini_httpd.init start

然后我们在去运行/usr/sbin/mini_httpd

然后看到报错

setsockopt SO_REUSEADDR: Protocol **not** availablesetsockopt SO_REUSEADDR: Protocol **not** available】**/**usr**/**sbin**/**mini_httpd: can't bind to any address
ciscorv160 漏洞复现
应该是setsockopt返回了负值导致的
ciscorv160 漏洞复现
如果要一个个ptach的可能会很麻烦,在这里我们使用LD_PRELOAD去使这个函数全返回1
/*arm-linux-gnueabi-gcc -shared -fPIC hook.c -o hook  */#include <stdio.h>#include <stdlib.h>#include<sys/socket.h> int setsockopt(int sockfd, int level, int optname,                      const void *optval, socklen_t optlen){ return 1;}

arm-linux-gnueabi-gcc这个交叉编译器的下载可以参考这个

https://www.cnblogs.com/jzcn/p/14889438.html

然后我们继续运行mini_httpd

LD_PRELOAD="/hook" ./usr/sbin/mini_httpd
但是我们访问页面,页面依旧是错误
ciscorv160 漏洞复现

这里我们根据错误去进行搜索,在ida中可以定位到如下情况

ciscorv160 漏洞复现

这里我们之间将报错显示后的bl跳转给nop掉

ciscorv160 漏洞复现

然后替换掉qemu中的mini_http程序

重新运行,访问页面

ciscorv160 漏洞复现

但是输入默认的账号密码 cisco后,不能成功登录,并且报错

Invalid Username or Password. Please try again

继续根据报错去查询

ciscorv160 漏洞复现

最后根据报错跟踪到了admin.cgi文件中

具体逻辑代码如下:

ciscorv160 漏洞复现

可以看到v9=-1的时候判断为报错,一开始我是直接想着将v9直接patch1,后来根据sub_1c900中的代码

ciscorv160 漏洞复现

猜测应该是缺少一些环境变量和配置文件,所以继续搜索/ect/init.sh目录查看是否有别的开启启动文件漏掉了

ciscorv160 漏洞复现

尝试运行config_update.sh

ciscorv160 漏洞复现

熟悉的错误,这个错误都快要刻在我脑子里了,这个错误一般是缺少/tmp/etc/config文件,所以我们去搜索mkdir -p /tmp/etc/config 字符串

ciscorv160 漏洞复现

熟悉的文件,在搭建rv34x的时候也碰到过这个文件,看来俩款设备有共通性,

那么该款路由器设备的完整搭建过程应该如下

/etc/init.d/boot boot   //初始化环境的创建generate_default_cert   //生成ssl证书文件/etc/init.d/confd start //启动confd服务/etc/init.d/mini_httpd.init start //初始化web服务的环境LD_PRELOAD="/hook" ./usr/sbin/mini_httpd 运行web服务
ciscorv160 漏洞复现
ciscorv160 漏洞复现
然后我们可以成功的登录进入设备的页面,但是不知道为什么google不能访问页面成功,火狐确可以
ciscorv160 漏洞复现

根据漏洞信息,漏洞是再web端传参,那么我们的中心就放在 admin.cgi mini_httpd rest.cgi当中

其中在mini_httpd中

void __noreturn sub_195F4(){  int v0[2]; // [sp+4h] [bp-30h] BYREF  int pipedes[2]; // [sp+Ch] [bp-28h] BYREF  __pid_t v2; // [sp+14h] [bp-20h]  __pid_t v3; // [sp+18h] [bp-1Ch]  char **argv; // [sp+1Ch] [bp-18h]  char **envp; // [sp+20h] [bp-14h]  int v6; // [sp+24h] [bp-10h]  char *path; // [sp+28h] [bp-Ch]  int v8; // [sp+2Ch] [bp-8h]
if ( dword_35394 != 1 && dword_35394 != 3 && !strncmp((const char *)arg, "dniapi/", 7u) ) sub_1BA24(501, "Not Implemented", byte_20108, "That method is not implemented for CGI."); if ( !dword_352FC || dword_352FC == 1 || dword_352FC == 2 ) { v6 = dup2(dword_352FC, 3); if ( v6 >= 0 ) dword_352FC = v6; } envp = (char **)sub_1A4B4(); argv = (char **)sub_1A2E8(); if ( dword_3538C > (unsigned int)dword_35390 || dword_342E8 ) { if ( pipe(pipedes) < 0 ) sub_1BA24(500, "Internal Error", byte_20108, "Something unexpected went wrong making a pipe."); v3 = fork(); if ( v3 < 0 ) sub_1BA24(500, "Internal Error", byte_20108, "Something unexpected went wrong forking an interposer."); if ( !v3 ) { close(pipedes[0]); sub_19A94(pipedes[1]); exit(0); } close(pipedes[1]); if ( pipedes[0] ) { dup2(pipedes[0], 0); close(pipedes[0]); } } else if ( dword_352FC ) { dup2(dword_352FC, 0); } v8 = strncmp(*argv, "nph-", 4u) != 0; if ( v8 || dword_342E8 ) { if ( pipe(v0) < 0 ) sub_1BA24(500, "Internal Error", byte_20108, "Something unexpected went wrong making a pipe."); v2 = fork(); if ( v2 < 0 ) sub_1BA24(500, "Internal Error", byte_20108, "Something unexpected went wrong forking an interposer."); if ( !v2 ) { close(v0[1]); sub_19CC4(v0[0], v8); exit(0); } close(v0[0]); if ( v0[1] != 1 ) dup2(v0[1], 1); if ( v0[1] != 2 ) dup2(v0[1], 2); if ( v0[1] != 1 && v0[1] != 2 ) close(v0[1]); } else { if ( dword_352FC != 1 ) dup2(dword_352FC, 1); if ( dword_352FC != 2 ) dup2(dword_352FC, 2); } if ( dword_342DC ) fclose((FILE *)dword_342DC); closelog(); nice(10); if ( dword_34258 ) path = "/usr/sbin/rest.cgi"; else path = "/usr/sbin/admin.cgi"; signal(13, 0); execve(path, argv, envp); sub_1BA24(500, "Internal Error", byte_20108, "Something unexpected went wrong running a CGI program.");

其中当get的请求投前六个参数为dniapi/时,mini_httpd会使用rest.cgi或者admin.cgi去进行处理,但是注意这个dword_34258判断,前面的代码我就不放了,所以admin.cgi为我们的主要web端处理函数

对mini_http进行diff

可以看到这么一处关键函数处理

_BYTE *__fastcall sub_1B034(_BYTE *result, _BYTE *a2, int a3){  _BYTE *v3; // r3  _BYTE *v4; // r3  _BYTE *v5; // r3  _BYTE *v6; // r3  _BYTE *v7; // r3  _BYTE *v8; // r2  int v9; // [sp+10h] [bp-14h]  _BYTE *v10; // [sp+14h] [bp-10h]  int v12; // [sp+1Ch] [bp-8h]  int v13; // [sp+1Ch] [bp-8h]  int v14; // [sp+1Ch] [bp-8h]
v12 = 0; v9 = a3 - 1; v10 = result; while ( *a2 ) { if ( *a2 == '~' || *a2 == '`' || *a2 == '#' || *a2 == '$' || *a2 == '&' || *a2 == '*' || *a2 == '(' || *a2 == ')' || *a2 == '|' || *a2 == '[' || *a2 == ']' || *a2 == '{' || *a2 == '}' || *a2 == ';' || *a2 == ''' || *a2 == '"' || *a2 == '<' || *a2 == '>' || *a2 == '/' || *a2 == '?' || *a2 == '!' || *a2 == ' ' || *a2 == '=' || *a2 == 't' ) { v3 = v10++; *v3 = '\'; if ( ++v12 >= v9 ) break; } else if ( *a2 == '\' ) { v4 = v10++; *v4 = '\'; v13 = v12 + 1; if ( v13 >= v9 ) break; v5 = v10++; *v5 = '\'; v14 = v13 + 1; if ( v14 >= v9 ) break; v6 = v10++; *v6 = '\'; v12 = v14 + 1; if ( v12 >= v9 ) break; } v7 = v10++; v8 = a2++; *v7 = *v8; if ( ++v12 >= v9 ) break; } *v10 = 0; return result;}

而新版本则是加上了’n‘字符的处理

那么此处函数应该是对输入的字符进行匹配,匹配到的字符在前面加上转义符,查看他的上层引用

int __fastcall vuln(const char *a1){  char v3[1024]; // [sp+Ch] [bp-3110h] BYREF  char v4[16]; // [sp+40Ch] [bp-2D10h] BYREF  char v5[16]; // [sp+2B1Ch] [bp-600h] BYREF  char v6[500]; // [sp+2F1Ch] [bp-200h] BYREF  FILE *stream; // [sp+3110h] [bp-Ch]  int v8; // [sp+3114h] [bp-8h]
memset(v3, 0, sizeof(v3)); sub_147A8("dirname:%s, %s", a1, (const char *)args); if ( *a1 && !strncmp(a1, "download/", 9u) ) { sub_147A8("hhhhhhhhhhhh"); if ( !args ) sub_1B83C(a1); if ( strncmp((const char *)args, "Basic ", 6u) ) sub_1B83C(a1); v8 = sub_1E5D0(args + 6, v6, 499); v6[v8] = 0; sub_1B034(v3, v6, 1024); v8 = snprintf( v5, 1024, "curl -v --stderr /tmp/b -v -u %s -X GET http://127.0.0.1:8008/api/running/gui/disable-startup-page > /tmp/d", v3); sub_147A8("cmd1:%s", v5); v5[v8] = 0; system(v5); stream = fopen("/tmp/b", "r"); if ( stream ) { do { if ( !fgets(v4, 10000, stream) ) goto LABEL_14; sub_147A8("line:%s", v4); if ( strstr(v4, "401 ") ) { sub_1AEFC(a1, 401); goto LABEL_14; } } while ( !strstr(v4, "403 ") ); sub_1AEFC(a1, 403);LABEL_14: fclose(stream); stream = 0; } v8 = snprintf( v5, 1024, "curl -i -v --stderr /tmp/b -v -u %s -X PUT http://127.0.0.1:8008/api/running/gui/disable-startup-page -T /tmp/d ", v3); sub_147A8("cmd2:%s ,l=%d", v5, v8); v5[v8] = 0; system(v5); stream = fopen("/tmp/b", "r"); if ( stream ) { do { if ( !fgets(v4, 10000, stream) ) goto LABEL_23; sub_147A8("line:%s", v4); if ( strstr(v4, "204 ") ) return 0; if ( strstr(v4, "401 ") ) { sub_1AEFC(a1, 401); goto LABEL_23; } } while ( !strstr(v4, "400 ") ); sub_1AEFC(a1, 400);LABEL_23: fclose(stream); stream = 0; } sub_147A8("cmd2 end"); sub_1B83C(a1); } return 0;}

其中a1为url的构成。args为Authorization: Basic参数后的匹配,

ciscorv160 漏洞复现

再由前面代码我们可以判断处url为/download/dniapi,构建数据包发送

ciscorv160 漏洞复现

虽然返回了401但是可以看到在设备的终端,命令被成功执行

ciscorv160 漏洞复现

但这并不是我们的目标,因为这个漏洞已经有详细信息,但是我们成功判断出1.03-1.04之间修复的主要漏洞逻辑为类似sub_1B034这样的函数判断,

版本更新公告也可以看出

ciscorv160 漏洞复现

此处漏洞为cve-2021-1602不是我们的目标,那么说明仍有漏网之鱼可以shell

将目光转向到rest.cgi和admin.cgi同时进行diff

rest.cgi

ciscorv160 漏洞复现

同样的问题,rest.cgi的主逻辑如下

ciscorv160 漏洞复现

经过代码分析,同样AUTHORIZATION:Basic这个参数存在问题,

但是要将请求头发送到rest.cgi去进行处理,需要让dword_34258为1

ciscorv160 漏洞复现

为1的条件如下

ciscorv160 漏洞复现

与进入cgi处理的前七个参数为/dniapi/相互冲突,尝试大半天仍然不能成功请求到rest.cgi

所以转换目标为admin.cgi

admin.cgi:

同样进行diff

ciscorv160 漏洞复现
ciscorv160 漏洞复现

向上层进行追溯

ciscorv160 漏洞复现

继续上层追溯

可以很清楚的进行代码审计

程序对authMethod usmUserAuthKey engineID privMethod usmUserPrivKey 这些参数进行处理

这些参数来源于上方的json定义

ciscorv160 漏洞复现

此处定义了数据包中的json结构,通过 usmUserPrivKey这个参数,有经验的师傅一定可以想起这这里的代码逻辑处理为cisco snmp页面的传参(根据我分析cisco rv34x 得来的经验)

所以进入snmp设置页面,抓包 修改参数,

ciscorv160 漏洞复现

成功shell

EDI安全

ciscorv160 漏洞复现

扫二维码|关注我们

一个专注渗透实战经验分享的公众号


原文始发于微信公众号(EDI安全):ciscorv160 漏洞复现

版权声明:admin 发表于 2023年4月11日 上午11:39。
转载请注明:ciscorv160 漏洞复现 | CTF导航

相关文章

暂无评论

您必须登录才能参与评论!
立即登录
暂无评论...