EDI
JOIN US ▶▶▶
EDI安全的CTF战队经常参与各大CTF比赛,了解CTF赛事。
欢迎各位师傅加入EDI,大家一起打CTF,一起进步。(诚招web re crypto pwn misc方向的师傅)有意向的师傅请联系邮箱root@edisec.net、shiyi@edisec.net(带上自己的简历,简历内容包括但不限于就读学校、个人ID、擅长技术方向、历史参与比赛成绩等等。
点击蓝字 · 关注我们
继续上次rv340失败后,目标转换成rv160
漏洞版本是低于1.0.01.04,所以我下载了1.0.01.03和1.0.01.04这俩个版本区间
依旧是选择unblob去对文件进行解压
查看路由设备系统架构
sudo tunctl -t tap0 -u `whoami`
sudo ifconfig tap0 192.168.2.1/24
qemu-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
ifconfig eth0 192.168.2.2/24
首先将文件系统压缩(避免scp传文件时扰乱文件中的软连接)
tar zcvf 1.tar rootfs/
然后将文件系统上传至qemu
sudo scp -r 1.tar root@192.168.2.2:/root/
rootfs是我改的名字,这个不需要在意
在qemu中
tar zxvf 1.tar
chmod -R 777 rootfs
cd rootfsmount --bind /proc proc
mount --bind /dev dev
chroot . sh
这样的话我们久完成了基础的环境搭建,下面我们需要的时候运行起该款路由器的web服务
首先我们需要查看系统的web服务器
这里可以看到 该款路由器的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** available
setsockopt SO_REUSEADDR: Protocol **not** available】
**/**usr**/**sbin**/**mini_httpd: can't bind to any address
/*arm-linux-gnueabi-gcc -shared -fPIC hook.c -o hook */
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
这里我们根据错误去进行搜索,在ida中可以定位到如下情况
这里我们之间将报错显示后的bl跳转给nop掉
然后替换掉qemu中的mini_http程序
重新运行,访问页面
但是输入默认的账号密码 cisco后,不能成功登录,并且报错
Invalid Username or Password. Please try again
继续根据报错去查询
最后根据报错跟踪到了admin.cgi文件中
具体逻辑代码如下:
可以看到v9=-1的时候判断为报错,一开始我是直接想着将v9直接patch1,后来根据sub_1c900中的代码
猜测应该是缺少一些环境变量和配置文件,所以继续搜索/ect/init.sh目录查看是否有别的开启启动文件漏掉了
尝试运行config_update.sh
熟悉的错误,这个错误都快要刻在我脑子里了,这个错误一般是缺少/tmp/etc/config文件,所以我们去搜索mkdir -p /tmp/etc/config 字符串
熟悉的文件,在搭建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服务
根据漏洞信息,漏洞是再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参数后的匹配,
再由前面代码我们可以判断处url为/download/dniapi,构建数据包发送
虽然返回了401但是可以看到在设备的终端,命令被成功执行
但这并不是我们的目标,因为这个漏洞已经有详细信息,但是我们成功判断出1.03-1.04之间修复的主要漏洞逻辑为类似sub_1B034这样的函数判断,
版本更新公告也可以看出
此处漏洞为cve-2021-1602不是我们的目标,那么说明仍有漏网之鱼可以shell
将目光转向到rest.cgi和admin.cgi同时进行diff
rest.cgi
同样的问题,rest.cgi的主逻辑如下
经过代码分析,同样AUTHORIZATION:Basic这个参数存在问题,
但是要将请求头发送到rest.cgi去进行处理,需要让dword_34258为1
为1的条件如下
与进入cgi处理的前七个参数为/dniapi/相互冲突,尝试大半天仍然不能成功请求到rest.cgi
所以转换目标为admin.cgi
admin.cgi:
同样进行diff
向上层进行追溯
继续上层追溯
可以很清楚的进行代码审计
程序对authMethod usmUserAuthKey engineID privMethod usmUserPrivKey 这些参数进行处理
这些参数来源于上方的json定义
此处定义了数据包中的json结构,通过 usmUserPrivKey这个参数,有经验的师傅一定可以想起这这里的代码逻辑处理为cisco snmp页面的传参(根据我分析cisco rv34x 得来的经验)
所以进入snmp设置页面,抓包 修改参数,
成功shell
EDI安全
扫二维码|关注我们
一个专注渗透实战经验分享的公众号
原文始发于微信公众号(EDI安全):ciscorv160 漏洞复现
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论