fortra / impacket

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

NTLM hash compromise security issue #102

Closed scriptjunkie closed 9 years ago

scriptjunkie commented 9 years ago

Impacket will gladly negotiate NTLMv1, from which the hash can be recovered with 100% certainty from network traffic alone in < 48 hours via chapcrack (NTLMv1) (see: http://markgamache.blogspot.com/2013/01/ntlm-challenge-response-is-100-broken.html) This would be very bad, as any system you authenticated to with Impacket, if it was compromised, could have simply sent an NTLMv1 challenge, sniffed traffic while you authenticated, then could recover the hash used, and likely take over the rest of the network, regardless of how strong the account’s password is. Impacket needs to either disable NTLMv1 by default, or support a minimum NTLM version configuration setting or parameter allowing the user to block NTLMv1. I verified testing some of the standard scripts against earlier versions of Windows, and it gladly complied.

It's worth noting that "NTLM 2 has been available for Windows NT 4.0 since Service Pack 4 (SP4) was released" https://support.microsoft.com/en-us/kb/239869 which is so far back, much of impacket won't work anyway. It's just incredibly unlikely that a user of this library will ever need NTLMv1, and it poses a major risk if enabled by default.

asolino commented 9 years ago

Thanks for the report Matt.

Impacket will gladly negotiate NTLMv1, from which the hash can be recovered with 100% certainty from network traffic alone

The use of NTLMv1 is restricted to SMB version 1 only if NTLMv2 authentication failed or SMB Extended Security is not supported. This is aiming at maximizing the possibilities for establishing a successful connection against a target SMB Server (e.g. old Samba servers). You can see the code here.

Also, when establishing DCERPC connections using the ncacn_ip_tcp transport, NTLMv1 will not be used at all.

Impacket needs to either disable NTLMv1 by default, or support a minimum NTLM version configuration setting or parameter allowing the user to block NTLMv1.

If you want to avoid using NTLMv1 the easiest thing to do is avoiding using SMBv1 when connecting against target systems. By default the library will use the highest SMB version available, but you can force it to use a particular one by means of the SMBConnection object's preferredDialect parameter. If you specify, for example, smb3structs.SMB2_DIALECT_002 as the preferredDialect the library will only try to connect using the SMB 2.002 protocol. As I mentioned before, all SMB versions > 1 will only use NTLMv2 (since it doesn't make any sense to try NTLMv1).

Now, if you want to establish a SMBv1 connection without NTLMv1, you will need to manually override or change the SMB.login() behaviour. I can certainly add a parameter to SMBConnection.login() (let's say use_ntlmv2={True,False}) that will be populated to down to SMB.login()

It's worth noting that "NTLM 2 has been available for Windows NT 4.0 since Service Pack 4 (SP4) was released" https://support.microsoft.com/en-us/kb/239869 which is so far back,

Just for clarifications I'm assuming you're talking about NTLMv2 right? (Since NTLM2 is also known as NTLMv1 with extended session security). As you can see here I assumed NTLMv1 was needed for Windows 2000 base install. Based on your link looks like that's not the case. I'm also wondering how old Samba versions (still found when performing PTs) behave. If you have info about that I'd appreciate it.

The only picky point here is whether or not to set these values by default to NTLMv2 (and I think it has to do with the universe of possible servers left behind). If there are honeypots in the target network (or you own a box), I could argue also we should set SMB signing to True all the time (to avoid being SMB relayed), or even not using NTLM if targeting a domain because even with SMB signing the connection could be relayed and just connect using Kerberos, plus many other constraints. Opinions are welcomed tho :)

scriptjunkie commented 9 years ago

Thanks for your reply. Maybe something like this?

index 8decee5..03a13a9 100644
--- a/impacket/smb.py
+++ b/impacket/smb.py
@@ -3300,7 +3300,7 @@ class SMB:
             self.__TGT,
             self.__TGS)

-    def login(self, user, password, domain = '', lmhash = '', nthash = ''):
+    def login(self, user, password, domain = '', lmhash = '', nthash = '', ntlm_fallback = True):

         # If we have hashes, normalize them
         if lmhash != '' or nthash != '':
@@ -3326,12 +3326,12 @@ class SMB:
                 self.login_extended(user, password, domain, lmhash, nthash, use_ntlmv2 = True)
             except:
                 # If the target OS is Windows 5.0 or Samba, let's try using NTLMv1
-                if (self.get_server_lanman().find('Windows 2000') != -1) or (self.get_server_lanman().find('Samba') != -1):
+                if ntlm_fallback and ((self.get_server_lanman().find('Windows 2000') != -1) or (self.get_server_lanman().find('Samba') != -1)):
                     self.login_extended(user, password, domain, lmhash, nthash, use_ntlmv2 = False)
                     self.__isNTLMv2 = False
                 else:
                     raise
-        else:
+        elif ntlm_fallback:
             self.login_standard(user, password, domain, lmhash, nthash)
             self.__isNTLMv2 = False

And yes, relay still remains an issue, so kerberos is always the best choice for domains. But I think this would be a nice option.

asolino commented 9 years ago

Yes, something around those lines, but also populating the ntlm_fallback up to the SMBConnection.login() method.

As you can see in the examples, SMB or SMB3 objects are not instantiated directly anymore, but through SMBConnection in order to have a common API regardless of the underlying SMB version.

I'll use your pull request as the base for these changes.

asolino commented 9 years ago

Just merged your PR plus added some stuff. Please recheck the original issue and close this ticket if solved.

mubix commented 9 years ago

I know I am late to the party on this, this is just for Impacket talking out right? Because I would hate the chance to loose capturing NTLMv1 hashes

asolino commented 9 years ago

Just when talking out, and the default behavior remains the same as before. It just adds the possibility to deny using NTLMv1 for SMBv1 if the developer consuming the library wants that through the ntlmFallback parameter when login in. For SMB version > 1, NTLMv1 is never used.

scriptjunkie commented 9 years ago

Looks good!

asolino commented 9 years ago

Great. Thanks again Matt