CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

GitHub Repository: rapid7/metasploit-framework
Path: blob/master/lib/rex/parser/unattend.rb
Views: 11780
1
# -*- coding: binary -*-
2
3
module Rex
4
module Parser
5
6
# This is a parser for the Windows Unattended Answer File
7
# format. It's used by modules/post/windows/gather/enum_unattend.rb
8
# and uses REXML (as opposed to Nokogiri) for its XML parsing.
9
# See: http://technet.microsoft.com/en-us/library/ff715801
10
# http://technet.microsoft.com/en-us/library/cc749415(v=ws.10).aspx
11
# Samples: http://technet.microsoft.com/en-us/library/cc732280%28v=ws.10%29.aspx
12
class Unattend
13
14
require 'rex/text'
15
16
def self.parse(xml)
17
return [] if xml.nil?
18
results = []
19
unattend = xml.elements['unattend']
20
return [] if unattend.nil?
21
unattend.each_element do |settings|
22
next if settings.class != REXML::Element
23
settings.get_elements('component').each do |c|
24
next if c.class != REXML::Element
25
results << extract_useraccounts(c.elements['UserAccounts'])
26
results << extract_autologon(c.elements['AutoLogon'])
27
results << extract_deployment(c.elements['WindowsDeploymentServices'])
28
results << extract_domain_join(c.elements['Identification/Credentials'])
29
end
30
end
31
return results.flatten
32
end
33
34
#
35
# Extract sensitive data from Deployment Services.
36
# We can only seem to add one <Login> with Windows System Image Manager, so
37
# we'll only enum one.
38
#
39
def self.extract_deployment(deployment)
40
return [] if deployment.nil?
41
domain = deployment.elements['Login/Credentials/Domain'].get_text.value rescue ''
42
username = deployment.elements['Login/Credentials/Username'].get_text.value rescue ''
43
password = deployment.elements['Login/Credentials/Password'].get_text.value rescue ''
44
plaintext = deployment.elements['Login/Credentials/Password/PlainText'].get_text.value rescue 'true'
45
46
if plaintext == 'false'
47
password = Rex::Text.decode_base64(password)
48
password = password.gsub(/#{Rex::Text.to_unicode('Password')}$/, '')
49
end
50
51
return {'type' => 'wds', 'domain' => domain, 'username' => username, 'password' => password }
52
end
53
54
#
55
# Extract sensitive data from 'Secure' Domain Join
56
#
57
def self.extract_domain_join(credentials)
58
return [] if credentials.nil?
59
domain = credentials.elements['Domain'].get_text.value rescue ''
60
username = credentials.elements['Username'].get_text.value rescue ''
61
password = credentials.elements['Password'].get_text.value rescue ''
62
63
return {'type' => 'domain_join', 'domain' => domain, 'username' => username, 'password' => password }
64
end
65
66
#
67
# Extract sensitive data from AutoLogon
68
#
69
def self.extract_autologon(auto_logon)
70
return [] if auto_logon.nil?
71
72
domain = auto_logon.elements['Domain'].get_text.value rescue ''
73
username = auto_logon.elements['Username'].get_text.value rescue ''
74
password = auto_logon.elements['Password/Value'].get_text.value rescue ''
75
plaintext = auto_logon.elements['Password/PlainText'].get_text.value rescue 'true'
76
77
if plaintext == 'false'
78
password = Rex::Text.decode_base64(password)
79
password = password.gsub(/#{Rex::Text.to_unicode('Password')}$/, '')
80
end
81
82
return {'type' => 'auto', 'domain' => domain, 'username' => username, 'password' => password }
83
end
84
85
#
86
# Extract sensitive data from UserAccounts
87
#
88
def self.extract_useraccounts(user_accounts)
89
return[] if user_accounts.nil?
90
91
results = []
92
account_types = ['AdministratorPassword', 'DomainAccounts', 'LocalAccounts']
93
account_types.each do |t|
94
element = user_accounts.elements[t]
95
next if element.nil?
96
97
case t
98
#
99
# Extract the password from AdministratorPasswords
100
#
101
when account_types[0]
102
password = element.elements['Value'].get_text.value rescue ''
103
plaintext = element.elements['PlainText'].get_text.value rescue 'true'
104
105
if plaintext == 'false'
106
password = Rex::Text.decode_base64(password)
107
password = password.gsub(/#{Rex::Text.to_unicode('AdministratorPassword')}$/, '')
108
end
109
110
unless password.empty?
111
results << {'type' => 'admin', 'username' => 'Administrator', 'password' => password}
112
end
113
114
#
115
# Extract the sensitive data from DomainAccounts.
116
# According to MSDN, unattend.xml doesn't seem to store passwords for domain accounts
117
#
118
when account_types[1] #DomainAccounts
119
element.elements.each do |account_list|
120
name = account_list.elements['DomainAccount/Name'].get_text.value rescue ''
121
group = account_list.elements['DomainAccount/Group'].get_text.value rescue 'true'
122
123
results << {'type' => 'domain', 'username' => name, 'group' => group}
124
end
125
#
126
# Extract the username/password from LocalAccounts
127
#
128
when account_types[2] #LocalAccounts
129
element.elements.each do |local|
130
password = local.elements['Password/Value'].get_text.value rescue ''
131
plaintext = local.elements['Password/PlainText'].get_text.value rescue 'true'
132
133
if plaintext == 'false'
134
password = Rex::Text.decode_base64(password)
135
password = password.gsub(/#{Rex::Text.to_unicode('Password')}$/, '')
136
end
137
138
username = local.elements['Name'].get_text.value rescue ''
139
results << {'type' => 'local', 'username' => username, 'password' => password}
140
end
141
end
142
end
143
144
return results
145
end
146
147
def self.create_table(results)
148
return nil if results.nil? or results.empty?
149
table = Rex::Text::Table.new({
150
'Header' => 'Unattend Credentials',
151
'Indent' => 1,
152
'Columns' => ['Type', 'Domain', 'Username', 'Password', 'Groups']
153
})
154
155
results.each do |result|
156
case result['type']
157
when 'wds', 'auto', 'domain_join'
158
table << [result['type'], result['domain'], result['username'], result['password'], ""]
159
when 'admin', 'local'
160
table << [result['type'], "", result['username'], result['password'], ""]
161
when 'domain'
162
table << [result['type'], "", result['username'], "", result['group']]
163
end
164
end
165
166
return table
167
end
168
end
169
end
170
end
171
172
173