
CVE-2025-0282 复现与分析

CVE-2025-0282 复现与分析
CVE-2025-0282 是发生在 ivanti 服务器中的一个栈溢出漏洞,CVSS 评分为 9.0,一如既往地,环境搭建还是那么的抽象,在搭建好调试环境之后,漏洞利用就很快了,同时感谢 @blonet 师傅(References[5])提供的帮助。
1. References
https://bestwing.me/CVE-2025-0282-Ivanti-Connect-Secure-VPN-stack-overflow.html
https://mp.weixin.qq.com/s/59TrRqK-Znk-CBTzL8db9Q:环境版本 Ivanti Connect Secure 22.7R2.3
2. Set env
测试环境为:Ivanti Connect Secure 22.7R2.3
虚拟机下载 : https://pulsezta.blob.core.windows.net/gateway/nsa/ISA-V-VMWARE-ICS-22.7R2.3-3431.1.zip
环境配置参考如下两篇文章,均有较详细介绍,同时在该节中也给出了所有可能用到的脚本。
https://mp.weixin.qq.com/s/e6X7GcKq1DaipmfsRqNq2w
https://mp.weixin.qq.com/s/59TrRqK-Znk-CBTzL8db9Q
在搭建好环境之后,可以直接访问如下url,看到登陆界面
2.1 文件互传脚本
下面两个脚本不能实现往ivanti传文件,原因是ivanti的端口被禁了。
receiver.py
不适用于ivanti服务器
1 | cat > receiver.py << 'EOF' |
sender.py
发送文件到目标机器,本地创建一个端口,然后指定发送的文件即可
1 | cat > sender.py << 'EOF' |
2.2 发送文件到ivanti服务器
在ubuntu上需要发送文件的目录下,运行该脚本
1 | #!/usr/bin/env python2 |
ivanti server下载文件
在下载文件之前,首先需要获取命令行shell,我们需要做的是挂起虚拟机(这一步需等待虚拟机出现Press <Enter> to view or update your appliance settings
时在挂起),然后到虚拟机对应的目录,找到如下图所示的内存文件,替换所有字符串/home/bin/dsconfig.pl
为 ///////////////bin/sh
替换如下所示,之后我们打开虚拟机,回车就可以正常拿到shell。
在ivanti服务器上执行如下脚本即可
1 | import urllib |
这里我们上传一个 busybox
到 ivanti 服务器(为方便后面调试可以同时上传一个gdbserver),然后执行下面命令,开启telnet服务
1 | ./busybox telnetd -l /bin/sh -b 0.0.0.0 -p 8009 |
然后ubuntu执行下面的命令
2.3 将ivanti服务器文件从端口上传
1 | cat > filehttp2.py << 'EOF' |
具体如下图所示
然后执行该脚本,从浏览器访问如下所示,这样就可以直接从ubuntu下载ivanti中想要的文件了
2.4 搭建调试环境
在 ivanti 的shell中执行如下命令
1 | ./gdbserver 0.0.0.0:8010 --attach $(netstat -anptl | grep 443 | awk '{print $7}' | cut -d'/' -f1 | grep -v "-") |
然后就可以尝试正常调试了
3. Vuln
3.1 static analysis
先来看看漏洞触发原因(文件位置位于/home/bin/web
):
对面上面的函数的部分关键内容解释如下:
1 | EPMessage::EPMessage((EPMessage *)v54, (DSUtilMemPool *)v56); |
这是 构造 EPMessage 类对象,构造函数的含义是:
v54
是新建的消息对象;v56
是一个内存池对象(DSUtilMemPool
)——这个机制可能用于减少内存分配带来的性能损耗,多个字段可能会使用同一个内存池来避免碎片化。
1 | sub_11D6B8((int)v54, "clientIp", *(DSUtilMemPool **)(a1 + 108)); |
三行都调用了同一个函数 sub_11D6B8(...)
,作用是:
给
EPMessage
消息添加一个 字符串类型字段,键为"clientIp"
、"clientHostName"
、"clientCapabilities"
,值是从对象a1
中获取的指针。
字段含义如下:
字段名 | 含义说明 |
---|---|
clientIp |
客户端的 IP 地址(字符串) |
clientHostName |
客户端的主机名 |
clientCapabilities |
客户端支持的功能列表(如支持哪些安全特性、模块) |
这些字段的值在结构体偏移:
a1 + 108
: 指向 IP 字符串a1 + 124
: 指向主机名字符串a1 + 140
: 指向支持能力字符串
需要注意的是 clientCapabilities
字段的大小是用户可控的,这里表示的是所拷贝的字符串的长度大小。
漏洞触发函数所在位置如下:
加上注释之后如下图所示
通过红框中的内容可以发现,v22
表示我们前面发送过去的clientCapabilities
字符串的长度,然后经过值的传递,最后v23 = *(_DWORD *)(a1 + 144) + 1 = v22 + 1
,紧接着下面的 strncpy
会进行拷贝,因此这里会触发栈溢出,由于是 32 位系统地址的特殊性(很少会发生0截断),使得我们可以几乎随意地设置 gadget。
3.1 debug
这里即是发生溢出的地方,从 src 可以看到拷贝的内容是由我们控制的
部分 payload 内容如下所示:
有了溢出自然想到的是要思考如何劫持控制流,一般的思路就是劫持函数指针或者函数返回地址,在该漏洞中就是通过劫持函数指针来实现控制流劫持,从下图可以关注到,在发生越界拷贝之后,紧接着下面存在一处函数指针调用
变量a1
实际就是该函数的 this
指针,该指针通常保存在栈帧的前面,因此我们可以考虑覆盖该指针,此时栈空间分布大致如下,其中a1
在 Return Address
的后面,
1 | +---------------------+ |
对应的汇编表示如下
动态调试如下所示:
这里的思路就是我们需要传递一个fake vtable
的地址给到eax
,使其可以执行我们的 ROP。
首先是gadget的选择,不难想到的是,由于此时esp
指向的区域为用户不可控区域,我们需要找到一个gadget,使其能够将esp
指向用户可控的部分(这个比较好找),但同时如果 gadget 还要能够设置ebx
寄存器就使得任务比较艰巨了(ebx
在后面call system
时会起作用),针对gadget的查找,Swing
的博客中也进行了详细说明(References[3]),这里就直接给出对应libdsplibs.so
中 0x093849C
偏移处的gadget内容如下:
注意整个过程中是没有泄露地址的,但由于目标系统为 32位,导致其地址爆破并不复杂,且每次程序打崩重启其地址不会被重新随机化,成功概率大概为 1/4096。
比较奇怪的是上面的gadget
我通过ROPgadget
没有找到,貌似只有采用 objdump 的方式才可以(swing 的做法)
1 | objdump --x86-asm-syntax=intel -D $(find . -name "libdsplibs.so") 2>&1 > libdsplibs.so.txt |
既然找到了 gadget,我们还需要找到一个引用该地址的虚表指针,才能完成正常跳转,原理如下:
比较一个简单的搜索办法就是直接在 ida 中 alt+b,输入要查找的十六进制数据,进行搜索
最后搜到的结果如下:
下图为动态调试验证,可以看到与上面 ida 中的结果是一致的,只不过由于随机化的原因,下面的地址都已经加好了程序的基地址。
之后我们只需要思考如何布置剩余的 gadget 即可
由于是 32 位系统,所以后面 ROP 的大致思路是,使得esp
中保存指向反弹shell的指针,这里采用的 ROP 如下:
最后效果如下图所示:
可以看到最终成功调用 system
函数,并让其参数指向执行的 shell
命令。
4. Final
- Title: CVE-2025-0282 复现与分析
- Author: henry
- Created at : 2025-05-19 10:39:41
- Updated at : 2025-05-19 11:03:02
- Link: https://henrymartin262.github.io/2025/05/19/CVE-2025-0282/
- License: This work is licensed under CC BY-NC-SA 4.0.