前言
继上次添加SSH后,现在有个淘汰的京东云一代,想要发挥余热,于是乎又去下载了一份高恪S2A版本(因为其不包含MT7615N驱动,故无法使用5G WiFi频段),由于我只是想使用其并发多拨的功能,因此关系不大。
按照前文的流程,卡卡一顿改完后,刷入设备,成功取得ssh,但是升级后就没了,但是官方上没有找到升级包的下载地址,于是乎有了本文的后续分析。
同时在分析中发现,有疑似后门的程序。
升级流程
/usr/lib/lua/luci/controller/admin/system.lua
网页上的检查升级按钮,最终会调用到这个lua中,但是这并不是明文,而是已经编译成luac的字节码。
反编译流程在这里不在展开,网上有许多现成的资料,需要留意的是openwrt中使用了魔改的lua。
关于升级检查的功能,主要位于prepare_onlineupgrade_inifile方法内。
/sbin/onlineupgrade.sh
通过对反编译后的system.lua分析,可知更新流程,会请求http://update.go-cloud.cn/RN1200/GOCLOUD-S2A_WL.ini检查是否存在新版本(URL后半段与具体型号有关)。
请求流程最终调用/sbin/onlineupgrade.sh脚本完成。
手动下载该配置文件后,发现配置文件非明文,采用Rijndael算法进行加密。再次返回system.lua分析,查找解密的密钥,通过在设备上运行以下lua脚本获得密钥:
require("luci") require("luci.sys") print("luci.sys.get_crypto_key:" .. luci.sys.get_crypto_key()) print("luci.sys.get_crypto_old_key(这是用于解密升级包的密钥):" .. luci.sys.get_crypto_old_key())
获得密钥后,简单的通过命令行ccrypt -d GOCLOUD-S2A_WL.ini -K <PASSWORD> -c即可得到明文的配置文件,如下:
version=5.2.2.22550 date=2023-06-26 size=12058628 url=http://update.gocloud.cn/GOCLOUD-S2A_WL-5.2.2.22550.bin.web name=GOCLOUD-S2A_WL-5.2.2.22550.bin.web md5=af4daf2bcc2521b3f4ddd5afe25125c3 standard=S2A_WL log=5.2.2.22550(2023.06.26)主要更新:<br />1. 特征库合并到最新<br />2. 修正一个交换机管理相关的配置兼容问题<br /><br />5.2.2.22540(2023.06.24)主要更新:<br />2. 优化一个AC相关的问题<br /><br />5.2.2.22513(2023.06.07)主要更新:<br />1. SDWAN自组网代理模式优化<br />3. POE网关桌面版支持网段冲突自动规避;<br /><br />5.2.2.22479(2023.05.30)主要更新:<br />1. 部分型号优化SDWAN二层自组网<br />2. 部分型号优化SDWAN三层自组网,支持对等组网<br />3. 软路由接口概览显示协商速率;<br />4. 软路由网吧版加强游戏更新服务器上传流量的控制,大幅降低P2P上传导致宽带重拨的概率<br />5. 软路由网吧版解决部分机器不能安装的问题<br />6. 加快付费产品加入云管平台的速度;优化长Ping快捷配置,不再使用114<br /><br />5.2.2.22360(2023.04.18)主要更新:<br />1. 部分型号支持SDWAN二层自组网(公测)<br />2. 部分型号比如POE网关桌面版、弱电箱专用路由、WiFi6路由支持手机首次连接路由弹portal引导到快速向导,方便施工开局<br />3. POE网关桌面版支持一机一码认证;<br /><br />5.2.2.21901(2022.10.26)主要更新:<br />1. 修正一个潜在的安全问题,请务必及时更新<br /><br />5.2.2.21650(2022.06.23)主要更新:<br />1. 完善SDWAN自组网私有部署, 部分官方路由支持服务端【支持创建组网】, 部分官方路由仅仅支持分支节点【允许加入组网】<br />2. 完善I225网卡驱动<br />3. 完善软路由控制台,在识别不到网卡时做恰当的提示<br />4. 默认关闭dns重定向保护;<br /><br />5.2.2.21466(2022.04.28)主要更新:<br />1. 支持SDWAN私有部署, 部分官方路由支持服务端【支持创建组网】, 部分官方路由仅仅支持分支节点【允许加入组网】<br />2. AP管理支持显示上联接口的协商状态<br />3. 流控例外支持配置目的IP组例外<br /><br />
其中载明了升级包的地址,但是直接访问,却发现返回404,将主机名修改为update.go-cloud.cn即可正常访问(完整地址为http://update.go-cloud.cn/RN1200/GOCLOUD-S2A_WL-5.2.2.22550.bin.web)
*.bin.web文件下载后,依葫芦画瓢使用ccrypt命令解密即可。
解密后是一个标准的tar格式文件,继续tar xf <FILENAME>。
解包后包含两个文件:
firmware.bin
firmware.ini
通过查看,firmware.bin文件只是简单描述设备型号,不需要处理。firmware.bin是一个uImage文件,那么就回到了最开始的工作了。
$ binwalk firmware.bin DECIMAL HEXADECIMAL DESCRIPTION -------------------------------------------------------------------------------- 0 0x0 uImage header, header size: 64 bytes, header CRC: 0x159706CA, created: 2023-06-26 07:34:15, image size: 1363048 bytes, Data Address: 0x81001000, Entry Point: 0x81001000, data CRC: 0x4166CAA3, OS: Linux, CPU: MIPS, image type: OS Kernel Image, compression type: lzma, image name: "OpenWrt Linux-3.10.14-169495" 64 0x40 LZMA compressed data, properties: 0x6D, dictionary size: 8388608 bytes, uncompressed size: 4143424 bytes 1363112 0x14CCA8 Squashfs filesystem, little endian, version 4.0, compression:xz, size: 10582460 bytes, 4019 inodes, blocksize: 262144 bytes, created: 2023-06-26 07:34:13
开启SSH
这一部分其实是老活了。
经过检查,固件中仍然包含dropbear,那么只需要简单的开启即可。修改/etc/rc.local
( sleep 5 chmod 755 /etc/init.d/dropbear /etc/init.d/dropbear enable /etc/init.d/dropbear restart ) & exit 0
同时修改ssh端口为22,这一步可选,不修改,默认为22222,/etc/config/dropbear
config dropbear option PasswordAuth 'on' option RootPasswordAuth 'on' option Port '22'
打包、升级
其实打包后的固件可直接通过breed刷机,但是这样可能配置信息就没了,而要通过系统自己的升级流程,就需要反过来操作一遍解包的流程。
# 将固件与ini文件重新打包成tar tar -cvf output.bin.web firmware.bin firmware.ini # 对tar进行加密 ccrypt -e output.bin.web -K <PASSWORD>
随后即可通过web的升级功能,选择本地升级即可。
意外的发现
因为在系统里发现了一些有趣的东西,也一起分享一下。
截止目前为止,高恪依旧是基于OpenWRT 14.07进行魔改的,内核版本为3.10.14,相对版本较为陈旧是否与长期不支持IPv6有关?
以下内容涉及到lua及其反编译,本人并不擅长,结论仅供参考,请各位自行分析。
以下所有内容都可能与云管有关,但部分组件为开机自启动,即使未开启云管功能依旧运行。
n2n
这一部分猜测是与云管有关系,但是因为n2n是二层的网络架构,同时设备上也有dropbear可以开启SSH(虽然官方固件并不开启,也不允许用户自行开启,却保留了dropbear),在技术上可以遥控路由器做任何事情。
当然n2n组件并未在开机时自动启动。
/etc/init.d/n2n
下面是最新版本5.2.2.22550的内容,对比官方发布的底包,是有所不同的,在底包中,开启n2n的同时,会开启dropbear,并监听在n2n ip的22222端口,供远程访问。
#!/bin/sh /etc/rc.common # Copyright (C) 2008 OpenWrt.org START=99 start() { enable=$(uci -q get n2n.global.enable) if [ "$enable" == "0" ]; then pidfile="/tmp/n2n-edge.pid" if test -f "$pidfile" ; then kill -9 `cat $pidfile` rm -f "$pidfile" fi exit fi sysctl -w net.core.rmem_max=10485760 sysctl -w "net.ipv4.udp_mem=3000 12000 48000" sysctl -w net.ipv4.udp_rmem_min=180224 ipaddr=$(uci -q get n2n.global.ipaddr) community=$(uci -q get n2n.global.community) token=$(uci -q get n2n.global.token) sdwan_ip=$(uci -q get n2n.global.sdwan_ip) serial=$(leval luci.sys.get_serial_number) wan=$(uci -q get n2n.global.wan) if [ -z "$wan" ]; then wan='auto' fi if [ -z "$sdwan_ip" ]; then speedlimit=$(uci -q get n2n.global.speedlimit) if [ -z "$speedlimit" ]; then speedlimit=250 fi flags=$(uci -q get n2n.global.flags) if [ -z "$flags" ]; then flags=0 fi master=$(uci -q get n2n.global.master) if [ -z "$master" ]; then master="n2n.gocloud.cn" fi edge -r -l $master -a static:$ipaddr -c $community -S $serial -x $speedlimit -T $token -d n2n -F $flags -w "$wan" else edge -r -P $sdwan_ip -a static:$ipaddr -c $community -S $serial -T $token -d n2n -F 0 -w "$wan" fi } stop() { /usr/lib/lua/spyder/tools/fw_n2n stop 1>/dev/null 2>&1 pidfile=/tmp/n2n-edge.pid if test -r $pidfile; then kill `cat $pidfile` rm -f $pidfile fi if pgrep edge; then killall -9 edge fi }
/usr/lib/lua/luci/model/cbi/admin_system/remotedb.lua
虽然新版中n2n开启时,不再直接开启dropbear,但是新版本多了一个remotedb的组件,位于/usr/lib/lua/luci/model/cbi/admin_system/remotedb.lua,其反编译后的代码大概长这样
db.parse = function(l_1_0, l_1_1) -- function num : 0_0 do if not l_1_0:formvalue(l_1_1) then local l_1_2, l_1_3, l_1_4, l_1_5, l_1_6, l_1_7, l_1_8 = l_1_0.default end -- DECOMPILER ERROR at PC6: Confused about usage of register: R2 in 'UnsetPending' -- DECOMPILER ERROR at PC8: Confused about usage of register: R2 in 'UnsetPending' if l_1_2 and l_1_2 == "1" then (os.execute)("chmod 755 /etc/init.d/dropbear") ; (os.execute)("/etc/init.d/dropbear restart") else ; (os.execute)("killall dropbear") ; (os.execute)("chmod 644 /etc/init.d/dropbear") end -- DECOMPILER ERROR at PC29: Confused about usage of register: R2 in 'UnsetPending' l_1_0:write(l_1_1, l_1_2) end end
可以看到其可以接受两个参数,决定开启或者关闭dropbear,那么再深入一步,看看是否有地方用到了这个模块呢。
发现在/usr/lib/lua/luci/controller/admin/system.lua中有使用,但是因为反编译的一些原因,无法具体的深入分析。猜测可能与授权有关。
/usr/sbin/realtimed
设备启动后,存在一个进程realtimed,启动参数为/usr/bin/lua /usr/sbin/realtimed -p 65527,大抵是在本地开启一个http服务(端口65527),具体用途未深入分析。另外还会监听一个端口65528,从下面节选的一段反编译代码来看,也与spyder组件相关
local verify_spydersign = function() -- function num : 0_20 if (require("app")).DEBUG then return true end local buf = tmpbuf:rewind() local key = (buf:putw(Unknown_Type_Error, Unknown_Type_Error, Unknown_Type_Error, Unknown_Type_Error, Unknown_Type_Error)):str() ; (buf:rewind()):putstr("spyder.sign") local fnames = (fs.listdir)("/usr/lib/lua/spyder") ; (table.sort)(fnames) local loadfile = function(fpath) -- function num : 0_20_0 , upvalues : buf buf:putc((fs.access)(fpath, fs.X_OK) == Unknown_Type_Error and Unknown_Type_Error or Unknown_Type_Error) file_get_content(fpath, buf) end for _,fname in ipairs(fnames) do if fname:match("%.lua$") then loadfile("/usr/lib/lua/spyder/" .. fname) end end loadfile("/usr/sbin/spyder") loadfile("/etc/init.d/spyder") aes_pencrypt(buf, md5(key)) do local fdata = file_get_content("/etc/spyder.sign") do return not fdata or md5(buf) == fdata end -- DECOMPILER ERROR: 2 unprocessed JMP targets end end
/usr/sbin/spyder
设备启动后同样存在spyder的服务,从名字看,与spider非常像,于是我也反编译粗略看了一眼,基本上是与spyder.gocloud.cn/115.28.230.105进行某些交互(实际运行时输出信息显示,连接的IP有所不同)。
$ tail -f /proc/15093/fd/3 01-11 16:50:50 info *** a.luyou.gocloud.cn resolved as 119.45.50.138 01-01 01:00:51 info *** worker connected 01-01 01:00:51 info *** SEND-REQ: create /login2 1 01-01 01:00:51 info *** RECV-RESP: 0 /login2 1 01-01 01:00:51 info *** set forbid_reset=0 01-01 01:00:51 info *** sync date to 2023-01-01 01:00:51 01-01 01:00:52 info *** SEND-REQ: read /data/app 2 01-01 01:00:52 info *** RECV-RESP: 0 /data/app 2 01-01 01:00:53 info *** scheduled next iplearning 46163 seconds later 01-01 01:00:52 info *** 60 /data/null requests done.
那么交互可以产生哪些效果呢,于是我注意到了一个文件/usr/lib/lua/spyder/cmd.lua,反编译后的代码大概长这样
routes["/cmd/execsh"] = function(l_1_0) -- function num : 0_0 do if l_1_0.body then local l_1_1, l_1_2 = (l_1_0.body):str() end local l_1_3 = nil ; (log.info)("execsh: ", l_1_3) if type(l_1_3) == "string" then if l_1_3 == "reboot" then reply(l_1_0, Unknown_Type_Error) ; (tasklet.sleep)(Unknown_Type_Error) end ; (os.execute)(l_1_3 .. " &") else -- DECOMPILER ERROR at PC35: Overwrote pending register: R2 in 'AssignReg' end return Unknown_Type_Error end end
routes["/cmd/execlua"] = function(l_3_0) -- function num : 0_2 do if l_3_0.body then local l_3_1, l_3_2 = (l_3_0.body):str() end -- DECOMPILER ERROR at PC7: Confused about usage of register: R1 in 'UnsetPending' local l_3_3 = nil if not loadstring(l_3_1) then (log.error)("execlua syntax error") else local l_3_4, l_3_5 = , pcall(loadstring(l_3_1)) if not l_3_5 then (log.error)("execlua runtime error: ", R7_PC25) end end do return Unknown_Type_Error end end end
大概意思就是可以执行任意shell以及任意lua脚本。
最后
我个人觉得realtimed和spyder两个组件还是有点可疑的,一为开机自启动,二为这两个组件停止后,会自动重启。
但是相关内容实在太多了,本人才疏学浅,看不完了。如果有其他感兴趣的大佬,可以接着分析一波,甚至中间人后tcpdump实锤一波。
附本次分析的镜像文件(已开启SSH),可breed直接刷入MT7621设备:点击下载(也可使用SX系列在x86虚拟机内分析,大部分内容都是相似的)
评论