DrayTek研究之CVE-2024-41592详细分析
cctrl IoT安全 770浏览 · 2025-04-09 12:44

说明

1,读者有责任遵守其所在国家或地区的所有法律,作者不对使用其文章中提到的任何内容所造成的任何损害负责。

2,本文内容关键词

(1)drayos固件解密

(2)drayos仿真

(3)符号恢复

(4)漏洞成因

(5)ext 文件系统

(6)IDA中如何查看连接ip:port?

(7)drayos网络架构

3,欢迎有问题交流

提问前请阅读:smart-questions



分析目标

测试目标版本2962_4326.all

溢出漏洞点程序:soho2962.bin

分析环境:kali或ubuntu

Decrypt firmware

固件下载:

https://fw.draytek.com.tw/Vigor2962/Firmware/

GPL源码下载:

https://gplsource.draytek.com/?dir=Vigor2962

1,binwalk -E 分析v2862_4326.all固件是加密的,首先寻找解密方法。

解密思路:利用未加密旧的版本固件中寻找解密程序。

定位到解密程序后两种解密思路:

(1)还原加密算法

(2)利用仿真环境运行解密程序

2,下载了GPL源码和未加密的3931版本的固件,都可以获取到文件系统内容,定位到解密代码:

./Vigor2962_431_GPL_release/drayrt-release-gpl/output/rootfs/draytek/drayapp/runcommand/fw_upload

3,上面脚本在xx版本环境中执行测试,获得了解密后的固件。

在kali中执行切换上下文环境,可以执行不同架构指令

下图是解密成功的截图:

image.png


解密后的v2962_4326固件:

image.png


分析一波加密特征

查看加密的v2962_4325固件,加密特征0204:

image.png


nonce(用于加密的随机数) :

enc_signature后面跟着的12位就是nonce

image.png


/draytek/drayapp/chacha20是解密程序,获得完整文件系统:

4,通过binwalk解压xx.all固件获取的文件系统可能不完整,ext未被识别解压,可通过mount挂载解决。

Q可能遇见的问题:提示挂载/ext-root空间不够如何解决?

A:利用cp命令,然后再次挂载即可。

符号恢复

两种思路:

(1)利用同架构的,可以是不同型号的存在符号的程序,提取符号信息,然后导入分析目标程序。

思路来源:HEXACON2022 - Emulate it until you make it! Pwning a DrayTek Router by Philippe Laulheret作者在视频同提到了此点。

优点:恢复的符号多

缺点:加载脚本时间慢

(2)符号表恢复部分

思路来源:Draytek3910 符号表修复

优点:加载脚本快

缺点:恢复的符号少

方法一

Vigorxx早期版本固件soho.bin有符号(具体版本留给读者发现了)

固件下载:https://fw.draytek.com.tw/

在Vigorxx早期版本固件中的soho.bin是携带符号的,我们可以借用早期版本的符号来进行符号恢复。符号恢复使用插件:使用IDA插件rizzo+bindiff将soho2962.bin的符号恢复大部分。

通过binwalk获取完整文件系统

直接用binwalk解压无法获得完整ext文件,可通过unblob工具解决此问题。



或者按如下方式修改binwalk支持解压格式,获取完整ext解压文件:

1,修改binwalk配置文件

正常binwalk -Me提取文件发现未加密,但无法获得文件系统,发现存在lzo格式压缩包,考虑LZ4方法压缩数据,设置配置文件,添加binwalk支持解压方式 和 安装LZ4压缩工具:



image.png


注意:不同安装binwalk方法,extract.conf 路径不同,可使用find查询

有可能显示几个路径,我环境下python路径下的才是binwalk运行时加载的路径。

扩展:确定一个文件读取哪个文件或在哪个路径下可使用trace工具追踪分析。

2,binwalk提取文件系统

3,查找xx中的有符号sohod64.bin

使用IDA加载有符号sohod64.bin

下面使用的方案是通过rizzo.py先导出符号,然后加载到无符号的程序中。其他bindiff等工具都可实现相识功能。

rizzo.py(支持到最新版本ida):

https://github.com/Reverier-Xu/IDA-Rizzo/tree/main

我使用的rizzo.py版本:

https://github.com/grayhatacademy/ida/blob/master/plugins/rizzo/rizzo.py(我使用的ida是7.x,提示了需要shims包,下载此仓库中的shims包即可解决)

1,使用rizzo导出vigorxx符号表,保存为xx.riz文件:

image.png


2,使用rizzo导入xx.riz符号表到soho2962.bin中,完成符号表加载:

image.png


方法二

参考:https://www.iotsec-zone.com/article/480

1,只能恢复部分符号方法:

2,IDA恢复函数名:

其他思路

文件系统中userspace/drayos/draycfg发现如下内容,像是下载源码文件这文件,但是服务器无法连接:

完整系统仿真

为了更好的分析,避免破坏测试环境,由于此设备可以通过qemu仿真,下面我们搭建仿真环境,然后保存分析点快照继续研究。

仿真思路:参考原本的qemu.sh修改qemu-system-aarch64参数。

1,高版本qemu.sh适配仿真:

2,网卡信息参考drayrt-release-gpl/output/rootfs/draytek/drayrc/rc.d/rc.41.setupif.sh可参看分析出如下net.sh脚本:

3,kali环境可能出现报错:

qemu-system-aarch64: -device virtio-net-pci,netdev=network-lan,mac=00:1d:aa:e9:25:69: failed to find romfile "efi-virtio.rom"

解决:

找到 efi-virtio.rom,拷贝下到当前目录:

4,然后分别执行上面net.sh和qemu.sh即可仿真运行:

image.png


5,开放端口:

漏洞分析

参考:

[Breaking lnto DrayTek RoutersBefore Threat Actors Do lt Again]https://www.forescout.com/resources/draybreak-draytek-research/

[CVE-2024-41592 vigor 栈溢出漏洞分析]https://bestwing.me/CVE-2024-41592-vigor-stack-overflow.html

信息简述:

(1)未检测&数量导致栈溢出:CVE-2024-41592

相关API:makework()

(2)OS Command Injection:CVE-2024-41585

2926漏洞版本确定。

修复版本: 4.3.2.8

溢出漏洞产生原因

1,漏洞产生原因,定位getcgi()函数中:

未检测&边界,循环读取内容

2,makeword功能是开辟空间放key1=value1的值,然后返回p_key;移动指针,开辟空间保存ptr_key和ptr_value:

3,溢出点:getcgi(a1,a2)其中a2是栈上数据,在getcgi(a1,a2)函数实现中,通过while循环读取&的数量,往a2上赋值,最终导致上一层栈帧内容被覆盖。

溢出补丁比对

Vigor2962_v4.3.2.8 V.S. Vigor2962_v4.3.2.6

1,Vigor2962_v4.3.2.6漏洞点,未检验&数量边界:

2,Vigor2962_v4.3.2.8修复,不再只是v25控制循环,对*(_DWORD *)(a1 + 0x10LL)做了长度校验,从而防止了&溢出:

命令执行漏洞

1,触发命令执行点:

image.png


2,支持的拼接命令:

image.png


3,示例:

Q:拼接中有个细节,这个细节就留给大家思考。

调试分析

Q:溢出点?溢出的栈帧是当前栈帧?

Q:利用的是哪个栈帧?

Q:如何解决破坏利用流程?

Q:寻找到xx.cgi?

Q:哪些未授权的xx.cgi?

下面文分析过程中给出上面答案。

解决溢出偏移问题

两种思路:

(1)静态分析计算

(2)动态测试计算

如不能一次计算准确的偏移值,可以根据动态测试微调。

gdb调试技巧:

1,以下是其中之一未授权接口点cgi测试poc示例:

192.168.1.1/cgi-bin/wlogin.cgi?&&&&&&&&&&&&&&&&&&&&&

测试结果:

image.png


2,分析溢出真正可控ret的栈帧位置:

image.png


v28是当前栈帧func1的局部变量,v28作为GetCGI的第二个参数传递,当发生溢出时,溢出的是foo1()的栈帧。

重点:由于arm的指令特性,往v28局部变量写,无法控制当前foo1()的ret1,想要控制pc寄存器,需要控制上层函数栈帧foo2()的ret2。(利用代码的本质就是控制pc)

3,验证可控内容覆盖栈帧:

image.png


4,freeCtrlName把输入数据给清空了

绿色部分:

image.png


5,如何找到真正cgi,绕过freebuf

溢出a2参数到ret,指令正常执行到ret需要绕过freeCtrlName();

FreeCtrlName处理了所有的POST/GET请求数据;

FreeCtrlName虽然只释放了低四字节指针(unsigned int),但影响了ret布局;

6,解决:

找特别的cgi,free没影响到ret。因为FreeCtrlName遇到栈上指针(v1 + 8 * i)=0时,终止循环。

We were fortunate to discover a CGI handler that: (1) processes the query string without authentication, and (2) sets the value of a specific local variable to zero after the query string overflow occurs.This local variable resides at a stack address lower than the return address but higher than the query string buffer’s start. This effectively places a zero on the stack and breaks the deallocation chain in “FreeCtrName()”, and preserves the overwritten return address.

Q:这个cgi是哪个?

A:思路如下:

(1)首先先将所有的 CGI 调用函数定义出来

string soho把所有cgi接口提取出来;

补充:后续发现通过IDA中字符定位,反查地址引用(alt+t)可以直接定位到一个表单,里面是cgi与cgi_hander映射表。

(2)过滤出不需要授权的 CGI 函数

burp测试status code;

image.png


(3)猜想哪些函数可以写 0 , 例如 atoi(query_string), query_string 是 HTTP 请求传入的参数

注意:(考虑可写0)不用授权、且参数可控可写 0 的CGI。

并且这个0的位置要加载buf-0-ret的中间:

image.png


组合利用

poc编写,下面给出一个shellcode示例:

1,利用unescape_url(),绕过坏字符;

可利用url编码 smuggle shellcode;

char2byte => %DE%AD%BE%EF = Þ­¾ï

2,利用rop

movk x30, #0xbaad, lsl #16指令解释:

movk: 表示“move with keep”。这条指令用于将一个立即数(即数字常量)移动到指定寄存器的特定位,同时保留寄存器中其他位的值。

x30: 这是目标寄存器,表示我们要操作的寄存器。在 ARM 架构中,x30 通常是链接寄存器(LR),用于存储返回地址。

#0xbaad: 这是要移动的立即数。0xbaad 是一个 16 位的十六进制数。

lsl #16: 这是一个位移操作,表示将立即数左移 16 位。lsl 是“逻辑左移”的缩写。

小结:该指令的作用是将 0xbaad 左移 16 位,然后将结果存储到 x30 寄存器的高 16 位,同时保留低 16 位的值。

x19是执行命令的函数:

(1)打印输出

printf打印验证

(2)命令执行

recvCmd字符长度限制<64字节(下面poc结合的是此方式)

绕过:通过分步执行命名,其他思路留给大家思考。

注意:但程序中调用了fork,因此可能命令会被多次执行。

image.png


(3)一个探讨,如何定位分析ip:port

实际测试不可行,tcp_socket_servr和recvCmd实现代码类似,但是端口不对。

思路:发现监听如下socket,然后反推分析。

Q:如何分析处端口不对?

A:这里可以自己写一个C/S通过调试观察ip和port传递的参数,然后分析实际的port。

关键是理解client,只要sockaddr_in结构体保存ip:port;

connect根据ip:port获取socket

send根据socket发送信息;

compile

通过此分析可以了解ip:port写死的情况下,在ida中如何确定(ida中用hex表示ip地址)。

Q:是否需要考虑坏字符,判断0是否会被截断?

A:不用,此处url编码可绕过。

drayos网络架构

此设备的网络架构不常见,比较有趣,给出如下信息供大家参考。

参考:

[Local Network Setup and Management]https://www.draytek.com/support/knowledge-base/5731

[Routing Fundamentals]

https://www.draytek.com/support/knowledge-base/5765

[LAN-to-LAN IPsec VPN Configuration Guide]

https://www.draytek.co.uk/support/guides/kb-lantolan-ipsec-3900to2860

1,相关网络设置文件:

2,draytek框架网络通信三层关系:

host(linuxOS)-guest(drayOS)-user



0 条评论
某人
表情
可输入 255