Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 51 additions & 42 deletions documentation/modules/auxiliary/gather/kerberos_enumusers.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,21 @@
## Vulnerable Application

The kerberos_enumusers module is used to enumerate valid Domain Users
via Kerberos from a wholly unauthenticated perspective. It utilises the
different responses returned by the service to identify users that exist
within the target domain. It is also able to identify whether user
accounts are enabled or disabled/locked out.

## Verification Steps

When verifying the module in the listed examples, it is recommended to test the following accounts:

- Valid account
- Invalid account
- Locked/Disabled account
- Account with spaces
- AS-REP Roastable accounts

## Target

To use kerberos_enumusers, make sure you are able to connect to the
Expand All @@ -15,54 +27,51 @@ The following demonstrates basic usage, using a custom wordlist,
targeting a single Domain Controller to identify valid domain user
accounts.

Create a new `./users.txt` file, then run the module:

```
msf > use auxiliary/gather/kerberos_enumusers
msf auxiliary(kerberos_enumusers) > set DOMAIN MYDOMAIN
DOMAIN => MYDOMAIN
msf auxiliary(kerberos_enumusers) > set RHOST 192.168.5.1
RHOST => 192.168.5.1
msf auxiliary(kerberos_enumusers) > set USER_FILE /job/users.txt
USER_FILE => /job/users.txt
msf auxiliary(kerberos_enumusers) > run

[*] Validating options...
[*] Using domain: MYDOMAIN...
[*] 192.168.5.1:88 - Testing User: "bob"...
[*] 192.168.5.1:88 - KDC_ERR_PREAUTH_REQUIRED - Additional
pre-authentication required
[+] 192.168.5.1:88 - User: "bob" is present
[*] 192.168.5.1:88 - Testing User: "alice"...
[*] 192.168.5.1:88 - KDC_ERR_PREAUTH_REQUIRED - Additional
pre-authentication required
[+] 192.168.5.1:88 - User: "alice" is present
[*] 192.168.5.1:88 - Testing User: "matt"...
[*] 192.168.5.1:88 - KDC_ERR_PREAUTH_REQUIRED - Additional
pre-authentication required
[+] 192.168.5.1:88 - User: "matt" is present
[*] 192.168.5.1:88 - Testing User: "guest"...
[*] 192.168.5.1:88 - KDC_ERR_CLIENT_REVOKED - Clients credentials have
been revoked
[-] 192.168.5.1:88 - User: "guest" account disabled or locked out
[*] 192.168.5.1:88 - Testing User: "admint"...
[*] 192.168.5.1:88 - KDC_ERR_C_PRINCIPAL_UNKNOWN - Client not found in
Kerberos database
[*] 192.168.5.1:88 - User: "admint" does not exist
[*] 192.168.5.1:88 - Testing User: "admin"...
[*] 192.168.5.1:88 - KDC_ERR_C_PRINCIPAL_UNKNOWN - Client not found in
Kerberos database
[*] 192.168.5.1:88 - User: "admin" does not exist
[*] 192.168.5.1:88 - Testing User: "administrator"...
[*] 192.168.5.1:88 - KDC_ERR_C_PRINCIPAL_UNKNOWN - Client not found in
Kerberos database
[*] 192.168.5.1:88 - User: "administrator" does not exist
msf6 auxiliary(gather/kerberos_enumusers) > run rhost=192.168.123.228 domain=domain.local user_file=./users.txt verbose=true
[*] Running module against 192.168.123.228

[*] Using domain: ADF3.LOCAL - 192.168.123.228:88...
[*] 192.168.123.228:88 - User: "missing123" user not found
[+] 192.168.123.228:88 - User: "administrator" is present
[+] 192.168.123.228:88 - User: "account with spaces" is present
[-] 192.168.123.228:88 - User: "locked_account" account disabled or locked out
[+] 192.168.123.228:88 - User: "no_pre_auth" does not require preauthentication. Hash: $krb5asrep$23$no_pre_auth@DOMAIN.LOCAL:bdb54b9e...etc..etc...
[+] 192.168.123.228:88 - User: "fake_mysql" is present
[*] 192.168.123.228:88 - User: "missing1234" user not found
[*] Auxiliary module execution completed
msf auxiliary(kerberos_enumusers) >
msf6 auxiliary(gather/kerberos_enumusers) >
```

### ASREPRoast Cracking

Accounts that have `Do not require Kerberos preauthentication` enabled, will receive an ASREP response with a ticket present.
The technique of cracking this token offline is called ASREPRoasting.

Cracking ASREP response with John:

```
john ./hashes.txt --wordlist=./wordlist.txt --format:krb5asrep
```

Cracking ASREP response with Hashcat:

```
hashcat -m 18200 -a 0 ./hashes.txt ./wordlist.txt
```

You can see previously creds with:

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
You can see previously creds with:
You can see previously stored creds with:


```
creds -v
```

## Options

The kerberos_enumusers module only requires the RHOST, DOMAIN and
USER_FILE options to run.
The `kerberos_enumusers` module only requires the `RHOST`, `DOMAIN` and
`USER_FILE` options to run.

**The DOMAIN option**

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,8 @@ def hash_to_hashcat(cred)
# https://hashcat.net/forum/thread-8833.html
# while we can do the transformation, we'd have to throw extra flags at hashcat which aren't currently written into the lib for automation
nil
when /^krb5$/
return cred.private.data.to_s
end
end
nil
Expand Down
8 changes: 4 additions & 4 deletions lib/msf/core/exploit/remote/kerberos/client/tgs_request.rb
Original file line number Diff line number Diff line change
Expand Up @@ -87,15 +87,15 @@ def build_tgs_request(opts = {})
# @option opts [Rex::Proto::Kerberos::Model::AuthorizationData] :auth_data
# @option opts [Rex::Proto::Kerberos::Model::EncryptionKey] :subkey
# @return [Rex::Proto::Kerberos::Model::EncryptedData]
# @raise [RuntimeError] if auth_data option isn't provided
# @raise [Rex::Proto::Kerberos::Model::Error::KerberosError] if auth_data option isn't provided
# @see Rex::Proto::Kerberos::Model::AuthorizationData
# @see Rex::Proto::Kerberos::Model::EncryptionKey
# @see Rex::Proto::Kerberos::Model::EncryptedData
def build_enc_auth_data(opts = {})
auth_data = opts[:auth_data]

if auth_data.nil?
raise ::RuntimeError, 'auth_data option required on #build_enc_auth_data'
raise ::Rex::Proto::Kerberos::Model::Error::KerberosError, 'auth_data option required on #build_enc_auth_data'
end

subkey = opts[:subkey] || build_subkey(opts)
Expand All @@ -120,7 +120,7 @@ def build_enc_auth_data(opts = {})
# @option opts [Rex::Proto::Kerberos::Model::EncryptedData] :authenticator
# @option opts [Rex::Proto::Kerberos::Model::EncryptionKey] :session_key
# @return [Rex::Proto::Kerberos::Model::EncryptionKey]
# @raise [RuntimeError] if ticket option isn't provided
# @raise [Rex::Proto::Kerberos::Model::Error::KerberosError] if ticket option isn't provided
# @see Rex::Proto::Kerberos::Model::Ticket
# @see Rex::Proto::Kerberos::Model::EncryptedData
# @see Rex::Proto::Kerberos::Model::EncryptionKey
Expand All @@ -133,7 +133,7 @@ def build_ap_req(opts = {})
session_key = opts[:session_key] || build_subkey(opts)

if ticket.nil?
raise ::RuntimeError, 'Building a AP-REQ without ticket not supported'
raise ::Rex::Proto::Kerberos::Model::Error::KerberosError, 'Building a AP-REQ without ticket not supported'
end

enc_authenticator = Rex::Proto::Kerberos::Model::EncryptedData.new(
Expand Down
10 changes: 5 additions & 5 deletions lib/rex/proto/kerberos/client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -157,18 +157,18 @@ def send_request_udp(req)
# Receives a Kerberos Response over a tcp connection
#
# @return [<Rex::Proto::Kerberos::Model::KrbError, Rex::Proto::Kerberos::Model::KdcResponse>] the kerberos message response
# @raise [RuntimeError] if the response can't be processed
# @raise [Rex::Proto::Kerberos::Model::Error::KerberosDecodingError] if the response can't be processed
# @raise [EOFError] if expected data can't be read
def recv_response_tcp
length_raw = connection.get_once(4, timeout)
unless length_raw && length_raw.length == 4
raise ::RuntimeError, 'Kerberos Client: failed to read response'
raise ::EOFError, 'Kerberos Client: failed to read response'
end
length = length_raw.unpack('N')[0]

data = connection.get_once(length, timeout)
unless data && data.length == length
raise ::RuntimeError, 'Kerberos Client: failed to read response'
raise ::EOFError, 'Kerberos Client: failed to read response'
end

res = decode_kerb_response(data)
Expand All @@ -189,7 +189,7 @@ def recv_response_udp
#
# @param data [String] the raw response message
# @return [<Rex::Proto::Kerberos::Model::KrbError, Rex::Proto::Kerberos::Model::KdcResponse>] the kerberos message response
# @raise [RuntimeError] if the response can't be processed
# @raise [Rex::Proto::Kerberos::Model::Error::KerberosDecodingError] if the response can't be processed
def decode_kerb_response(data)
asn1 = OpenSSL::ASN1.decode(data)
msg_type = asn1.value[0].value[1].value[0].value
Expand All @@ -202,7 +202,7 @@ def decode_kerb_response(data)
when Rex::Proto::Kerberos::Model::TGS_REP
res = Rex::Proto::Kerberos::Model::KdcResponse.decode(asn1)
else
raise ::RuntimeError, 'Kerberos Client: Unknown response'
raise ::Rex::Proto::Kerberos::Model::Error::KerberosDecodingError, 'Kerberos Client: Unknown response'
end

res
Expand Down
6 changes: 3 additions & 3 deletions lib/rex/proto/kerberos/crypto/rc4_hmac.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ module Rc4Hmac
# @param key [String] the key to decrypt
# @param msg_type [Integer] the message type
# @return [String] the decrypted cipher
# @raise [RuntimeError] if decryption doesn't succeed
# @raise [Rex::Proto::Kerberos::Model::Error::KerberosError] if decryption doesn't succeed
def decrypt_rc4_hmac(cipher, key, msg_type)
unless cipher && cipher.length > 16
raise ::RuntimeError, 'RC4-HMAC decryption failed'
Expand All @@ -29,7 +29,7 @@ def decrypt_rc4_hmac(cipher, key, msg_type)
decrypted = cipher.update(data) + cipher.final

if OpenSSL::HMAC.digest('MD5', k1, decrypted) != checksum
raise ::RuntimeError, 'RC4-HMAC decryption failed, incorrect checksum verification'
raise ::Rex::Proto::Kerberos::Model::Error::KerberosError, 'RC4-HMAC decryption failed, incorrect checksum verification'
end

decrypted
Expand Down Expand Up @@ -62,4 +62,4 @@ def encrypt_rc4_hmac(data, key, msg_type)
end
end
end
end
end
51 changes: 0 additions & 51 deletions lib/rex/proto/kerberos/model.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,57 +17,6 @@ module Model
AUTHENTICATOR = 2
AP_REQ = 14

# Kerberos error codes
ERROR_CODES = {
0 => ['KDC_ERR_NONE', 'No error'],
1 => ['KDC_ERR_NAME_EXP', 'Client\'s entry in database has expired'],
2 => ['KDC_ERR_SERVICE_EXP', 'Server\'s entry in database has expired'],
3 => ['KDC_ERR_BAD_PVNO', 'Requested protocol version number not supported'],
4 => ['KDC_ERR_C_OLD_MAST_KVNO', 'Client\'s key encrypted in old master key'],
5 => ['KDC_ERR_S_OLD_MAST_KVNO', 'Server\'s key encrypted in old master key'],
6 => ['KDC_ERR_C_PRINCIPAL_UNKNOWN', 'Client not found in Kerberos database'],
7 => ['KDC_ERR_S_PRINCIPAL_UNKNOWN', 'Server not found in Kerberos database'],
8 => ['KDC_ERR_PRINCIPAL_NOT_UNIQUE', 'Multiple principal entries in database'],
9 => ['KDC_ERR_NULL_KEY', 'The client or server has a null key'],
10 => ['KDC_ERR_CANNOT_POSTDATE', 'Ticket not eligible for postdating'],
11 => ['KDC_ERR_NEVER_VALID', 'Requested start time is later than end time'],
12 => ['KDC_ERR_POLICY', 'KDC policy rejects request'],
13 => ['KDC_ERR_BADOPTION', 'KDC cannot accommodate requested option'],
14 => ['KDC_ERR_ETYPE_NOSUPP', 'KDC has no support for encryption type'],
15 => ['KDC_ERR_SUMTYPE_NOSUPP', 'KDC has no support for checksum type'],
16 => ['KDC_ERR_PADATA_TYPE_NOSUPP', 'KDC has no support for padata type'],
17 => ['KDC_ERR_TRTYPE_NOSUPP', 'KDC has no support for transited type'],
18 => ['KDC_ERR_CLIENT_REVOKED', 'Clients credentials have been revoked'],
19 => ['KDC_ERR_SERVICE_REVOKED', 'Credentials for server have been revoked'],
20 => ['KDC_ERR_TGT_REVOKED', 'TGT has been revoked'],
21 => ['KDC_ERR_CLIENT_NOTYET', 'Client not yet valid - try again later'],
22 => ['KDC_ERR_SERVICE_NOTYET', 'Server not yet valid - try again later'],
23 => ['KDC_ERR_KEY_EXPIRED', 'Password has expired - change password to reset'],
24 => ['KDC_ERR_PREAUTH_FAILED', 'Pre-authentication information was invalid'],
25 => ['KDC_ERR_PREAUTH_REQUIRED', 'Additional pre-authentication required'],
31 => ['KRB_AP_ERR_BAD_INTEGRITY', 'Integrity check on decrypted field failed'],
32 => ['KRB_AP_ERR_TKT_EXPIRED', 'Ticket expired'],
33 => ['KRB_AP_ERR_TKT_NYV', 'Ticket not yet valid'],
34 => ['KRB_AP_ERR_REPEAT', 'Request is a replay'],
35 => ['KRB_AP_ERR_NOT_US', 'The ticket isn\'t for us'],
36 => ['KRB_AP_ERR_BADMATCH', 'Ticket and authenticator don\'t match'],
37 => ['KRB_AP_ERR_SKEW', 'Clock skew too great'],
38 => ['KRB_AP_ERR_BADADDR', 'Incorrect net address'],
39 => ['KRB_AP_ERR_BADVERSION', 'Protocol version mismatch'],
40 => ['KRB_AP_ERR_MSG_TYPE', 'Invalid msg type'],
41 => ['KRB_AP_ERR_MODIFIED', 'Message stream modified'],
42 => ['KRB_AP_ERR_BADORDER', 'Message out of order'],
44 => ['KRB_AP_ERR_BADKEYVER', 'Specified version of key is not available'],
45 => ['KRB_AP_ERR_NOKEY', 'Service key not available'],
46 => ['KRB_AP_ERR_MUT_FAIL', 'Mutual authentication failed'],
47 => ['KRB_AP_ERR_BADDIRECTION', 'Incorrect message direction'],
48 => ['KRB_AP_ERR_METHOD', 'Alternative authentication method required'],
49 => ['KRB_AP_ERR_BADSEQ', 'Incorrect sequence number in message'],
50 => ['KRB_AP_ERR_INAPP_CKSUM', 'Inappropriate type of checksum in message'],
60 => ['KRB_ERR_GENERIC', 'Generic error'],
61 => ['KRB_ERR_FIELD_TOOLONG', 'Field is too long for this implementation']
}

KDC_OPTION_RESERVED = 0
KDC_OPTION_FORWARDABLE = 1
KDC_OPTION_FORWARDED = 2
Expand Down
10 changes: 5 additions & 5 deletions lib/rex/proto/kerberos/model/enc_kdc_response.rb
Original file line number Diff line number Diff line change
Expand Up @@ -49,15 +49,15 @@ class EncKdcResponse < Element
#
# @param input [String, OpenSSL::ASN1::ASN1Data] the input to decode from
# @return [self] if decoding succeeds
# @raise [RuntimeError] if decoding doesn't succeed
# @raise [Rex::Proto::Kerberos::Model::Error::KerberosDecodingError] if decoding doesn't succeed
def decode(input)
case input
when String
decode_string(input)
when OpenSSL::ASN1::ASN1Data
decode_asn1(input)
else
raise ::RuntimeError, 'Failed to decode EncKdcResponse, invalid input'
raise ::Rex::Proto::Kerberos::Model::Error::KerberosDecodingError, 'Failed to decode EncKdcResponse, invalid input'
end

self
Expand All @@ -84,7 +84,7 @@ def decode_string(input)
# Decodes a Rex::Proto::Kerberos::Model::EncKdcResponse
#
# @param input [OpenSSL::ASN1::ASN1Data] the input to decode from
# @raise [RuntimeError] if decoding doesn't succeed
# @raise [Rex::Proto::Kerberos::Model::Error::KerberosDecodingError] if decoding doesn't succeed
def decode_asn1(input)
input.value[0].value.each do |val|
case val.tag
Expand All @@ -111,7 +111,7 @@ def decode_asn1(input)
when 10
self.sname = decode_sname(val)
else
raise ::RuntimeError, 'Failed to decode ENC-KDC-RESPONSE SEQUENCE'
raise ::Rex::Proto::Kerberos::Model::Error::KerberosDecodingError, 'Failed to decode ENC-KDC-RESPONSE SEQUENCE'
end
end
end
Expand Down Expand Up @@ -212,4 +212,4 @@ def decode_sname(input)
end
end
end
end
end
Loading