CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!

GitHub Repository: rapid7/metasploit-framework
Path: blob/master/lib/metasploit/framework/ntds/account.rb
Views: 1904
1
module Metasploit
2
module Framework
3
module NTDS
4
# This class represents an NTDS account structure as sent back by Meterpreter's
5
# priv extension.
6
class Account
7
8
# Size of an NTDS Account Struct on the Wire
9
ACCOUNT_SIZE = 3016
10
# Size of a Date or Time Format String on the Wire
11
DATE_TIME_STRING_SIZE = 30
12
# Size of the AccountDescription Field
13
DESCRIPTION_SIZE =1024
14
# Size of a Hash History Record
15
HASH_HISTORY_SIZE = 792
16
# Size of a Hash String
17
HASH_SIZE = 33
18
# Size of the samAccountName field
19
NAME_SIZE = 128
20
21
#@return [String] The AD Account Description
22
attr_accessor :description
23
#@return [Boolean] If the AD account is disabled
24
attr_accessor :disabled
25
#@return [Boolean] If the AD account password is expired
26
attr_accessor :expired
27
#@return [String] Human Readable Date for the account's password expiration
28
attr_accessor :expiry_date
29
#@return [String] The LM Hash of the current password
30
attr_accessor :lm_hash
31
#@return [Array<String>] The LM hashes for previous passwords, up to 24
32
attr_accessor :lm_history
33
#@return [Integer] The count of historical LM hashes
34
attr_accessor :lm_history_count
35
#@return [Boolean] If the AD account is locked
36
attr_accessor :locked
37
#@return [Integer] The number of times this account has logged in
38
attr_accessor :logon_count
39
#@return [String] Human Readable Date for the last time the account logged in
40
attr_accessor :logon_date
41
#@return [String] Human Readable Time for the last time the account logged in
42
attr_accessor :logon_time
43
#@return [String] The samAccountName of the account
44
attr_accessor :name
45
#@return [Boolean] If the AD account password does not expire
46
attr_accessor :no_expire
47
#@return [Boolean] If the AD account does not require a password
48
attr_accessor :no_pass
49
#@return [String] The NT Hash of the current password
50
attr_accessor :nt_hash
51
#@return [Array<String>] The NT hashes for previous passwords, up to 24
52
attr_accessor :nt_history
53
#@return [Integer] The count of historical NT hashes
54
attr_accessor :nt_history_count
55
#@return [String] Human Readable Date for the last password change
56
attr_accessor :pass_date
57
#@return [String] Human Readable Time for the last password change
58
attr_accessor :pass_time
59
#@return [Integer] The Relative ID of the account
60
attr_accessor :rid
61
#@return [String] Byte String for the Account's SID
62
attr_accessor :sid
63
64
# @param raw_data [String] the raw 3948 byte string from the wire
65
# @raise [ArgumentErrror] if a 3948 byte string is not supplied
66
def initialize(raw_data)
67
raise ArgumentError, "No Data Supplied" unless raw_data.present?
68
raise ArgumentError, "Invalid Data" unless raw_data.length == ACCOUNT_SIZE
69
data = raw_data.dup
70
@name = get_string(data,NAME_SIZE)
71
@description = get_string(data,DESCRIPTION_SIZE)
72
@rid = get_int(data)
73
@disabled = get_boolean(data)
74
@locked = get_boolean(data)
75
@no_pass = get_boolean(data)
76
@no_expire = get_boolean(data)
77
@expired = get_boolean(data)
78
@logon_count = get_int(data)
79
@nt_history_count = get_int(data)
80
@lm_history_count = get_int(data)
81
@expiry_date = get_string(data,DATE_TIME_STRING_SIZE)
82
@logon_date = get_string(data,DATE_TIME_STRING_SIZE)
83
@logon_time = get_string(data,DATE_TIME_STRING_SIZE)
84
@pass_date = get_string(data,DATE_TIME_STRING_SIZE)
85
@pass_time = get_string(data,DATE_TIME_STRING_SIZE)
86
@lm_hash = get_string(data,HASH_SIZE)
87
@nt_hash = get_string(data,HASH_SIZE)
88
@lm_history = get_hash_history(data)
89
@nt_history = get_hash_history(data)
90
@sid = data
91
end
92
93
# @return [String] String representation of the account data
94
def to_s
95
<<-EOS.strip_heredoc
96
#{@name} (#{@description})
97
#{@name}:#{@rid}:#{ntlm_hash}
98
Password Expires: #{@expiry_date}
99
Last Password Change: #{@pass_time} #{@pass_date}
100
Last Logon: #{@logon_time} #{@logon_date}
101
Logon Count: #{@logon_count}
102
#{uac_string}
103
Hash History:
104
#{hash_history}
105
EOS
106
end
107
108
# @return [String] the NTLM hash string for the current password
109
def ntlm_hash
110
"#{@lm_hash}:#{@nt_hash}"
111
end
112
113
# @return [String] Each historical NTLM Hash on a new line
114
def hash_history
115
history_string = ''
116
@lm_history.each_with_index do | lm_hash, index|
117
history_string << "#{@name}:#{@rid}:#{lm_hash}:#{@nt_history[index]}\n"
118
end
119
history_string
120
end
121
122
private
123
124
def get_boolean(data)
125
get_int(data) == 1
126
end
127
128
def get_hash_history(data)
129
raw_history = data.slice!(0,HASH_HISTORY_SIZE)
130
split_history = raw_history.scan(/.{1,33}/)
131
split_history.map!{ |hash| hash.gsub(/\x00/,'')}
132
split_history.reject!{ |hash| hash.blank? }
133
end
134
135
def get_int(data)
136
data.slice!(0,4).unpack('L').first
137
end
138
139
def get_string(data,length)
140
data.slice!(0,length).force_encoding("UTF-8").gsub(/\x00/,'')
141
end
142
143
def uac_string
144
status_string = ''
145
if @disabled
146
status_string << " - Account Disabled\n"
147
end
148
if @expired
149
status_string << " - Password Expired\n"
150
end
151
if @locked
152
status_string << " - Account Locked Out\n"
153
end
154
if @no_expire
155
status_string << " - Password Never Expires\n"
156
end
157
if @no_pass
158
status_string << " - No Password Required\n"
159
end
160
status_string
161
end
162
end
163
end
164
end
165
end
166
167