fortra / impacket

Impacket is a collection of Python classes for working with network protocols.
https://www.coresecurity.com
Other
13.49k stars 3.57k forks source link

Kerberos: no support for referrals (KDC_ERR_WRONG_REALM) #315

Open machosec opened 7 years ago

machosec commented 7 years ago

Hola amigo! While playing with Impacket during an engagement with multi-domain forest I've encountered some Kerberos referrals issues. When a client requests a TGT from a domain controller for a domain to which the client does not belong the KDC_ERR_WRONG_REALM error is returned. This error refers the client to the correct domain and does not indicate a problem. More info here: https://msdn.microsoft.com/en-us/library/Cc212351.aspx . Impacket doesn't handle this error and throws it as an exception. Supporting this capability is essential for multi-domain forest communications.

Your help will be appreciated!

asolino commented 7 years ago

Hola Matan!

Many thanks for your input.

I'd like to understand that scenario better. You said:

When a client requests a TGT from a domain controller for a domain to which the client does not belong the KDC_ERR_WRONG_REALM error is returned.

How did you do that? In theory the library will locate the DC based on the domain FQDN specified in the connection string, unless you force it with -dc-ip switch. Is that what you did? (If I use that switch and specify the wrong domain I indeed get that error)

Regardless, what do you think an acceptable solution for this should be? Returning the next REALM that should be tried? Try it automatically?

I'd love to reproduce this scenario in my lab, any help would be appreciated.

thanks!

machosec commented 7 years ago

Based on your answer I have better understanding of what happened. I have modified the GetADUsers.py to query each domain in the forest. When I started the query, the getMachineName function failed to retrieve a DC for one of the domains. Then, I manually changed the kdcHost parameter to a valid DC FQDN and it worked. If I remember correctly, the DC that answers to the domain FQDN is the the primary Domain Controller (PDC). So I suspect that the PDC was down for that particular domain. After this change I tried to connect to a different domain while keeping the kdcHost with the DC from the previous domain. That raised the KDC_ERR_WRONG_REALM exception. However, this scenario led me think about few possible upgrades to Impacket:

  1. Is there a way to pass a TGT to a connection (SMBConnection, LDAPConnection, etc.) instead of passing any sort of credentials for authentication (to acquire a new TGT)? I know Impacket supports reuse of credentials from ccache, but what about Windows? It should be possible to authenticate to one domain and reuse the TGT to access cross-domain services (using inter-realm tickets). This will prevent the necessity of authenticating against each PDC (or service) in the first place.
  2. This scenario proves that you shouldn't rely on the domain FQDN to point to a running DC. There are elegant ways (such as DNS) to locate DCs in a domain.
  3. IMO the best way to handle the KDC_ERR_WRONG_REALM error is to recursively authenticate to the next REALM.

Regradless, Impacket is great. Keep it up! :)

asolino commented 7 years ago

Hey @machosec

Then, I manually changed the kdcHost parameter to a valid DC FQDN and it worked.

Don't know exactly what you changed.. but have in mind you have the -dc-ip option to specify the DC ip instead of the library resolving it based on the domain FQDN.

  1. Is there a way to pass a TGT to a connection (SMBConnection, LDAPConnection, etc.) instead of passing any sort of credentials for authentication (to acquire a new TGT)? I know Impacket supports reuse of credentials from ccache, but what about Windows? It should be possible to authenticate to one domain and reuse the TGT to access cross-domain services (using inter-realm tickets). This will prevent the necessity of authenticating against each PDC (or service) in the first place.

Programmatically yes... SMBConnection.kerberosLogin(), LDAPConnection.kerberosLogin and others (e.g. TDS, DCOM) support sending TGTs/TGSs as a way to authenticate against the target systems. You will need somehow to get the TGTs/TGSs from the OS stack and go from there. CCache should work on Windows as well, assuming you set the KRB5CCNAME environment variable and somehow convert the Windows TGTs into Ccache format (there are utilities that do that... can't remember if mimikatz now supports it out of the box). Indeed it would be neat to grab the tickets from Windows directly but that would require interacting with the APIs (more packages required). Ideas (and PRs) are welcomed :)

  1. This scenario proves that you shouldn't rely on the domain FQDN to point to a running DC. There are elegant ways (such as DNS) to locate DCs in a domain.

Based on the different ways to locate a DC and your hypothesis that PDC was down, I don't know what would've changed with a not-so-different DNS approach (as described in the link). Maybe I'm missing something. That being said.. there are still some more robust techniques to locate DCs (similar to what Windows does) but that requires extra coding.

IMO the best way to handle the KDC_ERR_WRONG_REALM error is to recursively authenticate to the next REALM.

Point taken.. I'll see what I can do.. I need to reproduce it in a way that makes sense testing first.

Thanks for the feedback.

the-useless-one commented 7 years ago

Btw, my PR is supposed to address this.