Skip to content

Commit 066a5e2

Browse files
committed
Land rapid7#3377, GPP gathering module fixes
2 parents 942112d + 8fe5082 commit 066a5e2

File tree

4 files changed

+355
-152
lines changed

4 files changed

+355
-152
lines changed

lib/msf/core/post/windows/netapi.rb

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,11 @@ def read_server_structs(start_ptr, count, domain, server_type)
6868
base = 0
6969
struct_size = 8
7070
hosts = []
71+
72+
if count == 0
73+
return hosts
74+
end
75+
7176
mem = client.railgun.memread(start_ptr, struct_size*count)
7277

7378
count.times do
Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
# -*- coding: binary -*-
2+
#
3+
4+
module Rex
5+
module Parser
6+
7+
# This is a parser for the Windows Group Policy Preferences file
8+
# format. It's used by modules/post/windows/gather/credentials/gpp.rb
9+
# and uses REXML (as opposed to Nokogiri) for its XML parsing.
10+
# See: http://msdn.microsoft.com/en-gb/library/cc232587.aspx
11+
class GPP
12+
require 'rex'
13+
require 'rexml/document'
14+
15+
def self.parse(data)
16+
if data.nil?
17+
return []
18+
end
19+
20+
xml = REXML::Document.new(data).root
21+
results = []
22+
23+
unless xml and xml.elements and xml.elements.to_a("//Properties")
24+
return []
25+
end
26+
27+
xml.elements.to_a("//Properties").each do |node|
28+
epassword = node.attributes['cpassword']
29+
next if epassword.to_s.empty?
30+
password = self.decrypt(epassword)
31+
32+
user = node.attributes['runAs'] if node.attributes['runAs']
33+
user = node.attributes['accountName'] if node.attributes['accountName']
34+
user = node.attributes['username'] if node.attributes['username']
35+
user = node.attributes['userName'] if node.attributes['userName']
36+
user = node.attributes['newName'] unless node.attributes['newName'].nil? || node.attributes['newName'].empty?
37+
changed = node.parent.attributes['changed']
38+
39+
# Printers and Shares
40+
path = node.attributes['path']
41+
42+
# Datasources
43+
dsn = node.attributes['dsn']
44+
driver = node.attributes['driver']
45+
46+
# Tasks
47+
app_name = node.attributes['appName']
48+
49+
# Services
50+
service = node.attributes['serviceName']
51+
52+
# Groups
53+
expires = node.attributes['expires']
54+
never_expires = node.attributes['neverExpires']
55+
disabled = node.attributes['acctDisabled']
56+
57+
result = {
58+
:USER => user,
59+
:PASS => password,
60+
:CHANGED => changed
61+
}
62+
63+
result.merge!({ :EXPIRES => expires }) unless expires.nil? || expires.empty?
64+
result.merge!({ :NEVER_EXPIRES => never_expires.to_i }) unless never_expires.nil? || never_expires.empty?
65+
result.merge!({ :DISABLED => disabled.to_i }) unless disabled.nil? || disabled.empty?
66+
result.merge!({ :PATH => path }) unless path.nil? || path.empty?
67+
result.merge!({ :DATASOURCE => dsn }) unless dsn.nil? || dsn.empty?
68+
result.merge!({ :DRIVER => driver }) unless driver.nil? || driver.empty?
69+
result.merge!({ :TASK => app_name }) unless app_name.nil? || app_name.empty?
70+
result.merge!({ :SERVICE => service }) unless service.nil? || service.empty?
71+
72+
attributes = []
73+
node.elements.each('//Attributes//Attribute') do |dsn_attribute|
74+
attributes << {
75+
:A_NAME => dsn_attribute.attributes['name'],
76+
:A_VALUE => dsn_attribute.attributes['value']
77+
}
78+
end
79+
80+
result.merge!({ :ATTRIBUTES => attributes }) unless attributes.empty?
81+
82+
results << result
83+
end
84+
85+
results
86+
end
87+
88+
def self.create_tables(results, filetype, domain=nil, dc=nil)
89+
tables = []
90+
results.each do |result|
91+
table = Rex::Ui::Text::Table.new(
92+
'Header' => 'Group Policy Credential Info',
93+
'Indent' => 1,
94+
'SortIndex' => -1,
95+
'Columns' =>
96+
[
97+
'Name',
98+
'Value',
99+
]
100+
)
101+
102+
table << ["TYPE", filetype]
103+
table << ["USERNAME", result[:USER]]
104+
table << ["PASSWORD", result[:PASS]]
105+
table << ["DOMAIN CONTROLLER", dc] unless dc.nil? || dc.empty?
106+
table << ["DOMAIN", domain] unless domain.nil? || domain.empty?
107+
table << ["CHANGED", result[:CHANGED]]
108+
table << ["EXPIRES", result[:EXPIRES]] unless result[:EXPIRES].nil? || result[:EXPIRES].empty?
109+
table << ["NEVER_EXPIRES?", result[:NEVER_EXPIRES]] unless result[:NEVER_EXPIRES].nil?
110+
table << ["DISABLED", result[:DISABLED]] unless result[:DISABLED].nil?
111+
table << ["PATH", result[:PATH]] unless result[:PATH].nil? || result[:PATH].empty?
112+
table << ["DATASOURCE", result[:DSN]] unless result[:DSN].nil? || result[:DSN].empty?
113+
table << ["DRIVER", result[:DRIVER]] unless result[:DRIVER].nil? || result[:DRIVER].empty?
114+
table << ["TASK", result[:TASK]] unless result[:TASK].nil? || result[:TASK].empty?
115+
table << ["SERVICE", result[:SERVICE]] unless result[:SERVICE].nil? || result[:SERVICE].empty?
116+
117+
unless result[:ATTRIBUTES].nil? || result[:ATTRIBUTES].empty?
118+
result[:ATTRIBUTES].each do |dsn_attribute|
119+
table << ["ATTRIBUTE", "#{dsn_attribute[:A_NAME]} - #{dsn_attribute[:A_VALUE]}"]
120+
end
121+
end
122+
123+
tables << table
124+
end
125+
126+
tables
127+
end
128+
129+
# Decrypts passwords using Microsoft's published key:
130+
# http://msdn.microsoft.com/en-us/library/cc422924.aspx
131+
def self.decrypt(encrypted_data)
132+
unless encrypted_data
133+
return ""
134+
end
135+
136+
password = ""
137+
padding = "=" * (4 - (encrypted_data.length % 4))
138+
epassword = "#{encrypted_data}#{padding}"
139+
decoded = Rex::Text.decode_base64(epassword)
140+
141+
key = "\x4e\x99\x06\xe8\xfc\xb6\x6c\xc9\xfa\xf4\x93\x10\x62\x0f\xfe\xe8\xf4\x96\xe8\x06\xcc\x05\x79\x90\x20\x9b\x09\xa4\x33\xb6\x6c\x1b"
142+
aes = OpenSSL::Cipher::Cipher.new("AES-256-CBC")
143+
begin
144+
aes.decrypt
145+
aes.key = key
146+
plaintext = aes.update(decoded)
147+
plaintext << aes.final
148+
password = plaintext.unpack('v*').pack('C*') # UNICODE conversion
149+
rescue OpenSSL::Cipher::CipherError => e
150+
puts "Unable to decode: \"#{encrypted_data}\" Exception: #{e}"
151+
end
152+
153+
password
154+
end
155+
156+
end
157+
end
158+
end
159+

0 commit comments

Comments
 (0)