Fix bug in SMB message signing due to missing NTLM_NegotiateSign flag in the NTLM session negotiation message.
The missing flag causes subsequent SMB operation requests with Windows to pass, but fails with Samba services.
Michael Teo
3 years ago
375 | 375 | self.log.info('Performing NTLMv1 authentication (on SMB2) with server challenge "%s"', binascii.hexlify(server_challenge)) |
376 | 376 | nt_challenge_response, lm_challenge_response, session_key = ntlm.generateChallengeResponseV1(self.password, server_challenge, True) |
377 | 377 | |
378 | ntlm_data = ntlm.generateAuthenticateMessage(server_flags, | |
379 | nt_challenge_response, | |
380 | lm_challenge_response, | |
381 | session_key, | |
382 | self.username, | |
383 | self.domain, | |
384 | self.my_name) | |
378 | ntlm_data, session_signing_key = ntlm.generateAuthenticateMessage(server_flags, | |
379 | nt_challenge_response, | |
380 | lm_challenge_response, | |
381 | session_key, | |
382 | self.username, | |
383 | self.domain, | |
384 | self.my_name) | |
385 | 385 | |
386 | 386 | if self.log.isEnabledFor(logging.DEBUG): |
387 | 387 | self.log.debug('NT challenge response is "%s" (%d bytes)', binascii.hexlify(nt_challenge_response), len(nt_challenge_response)) |
401 | 401 | |
402 | 402 | if self.is_signing_active: |
403 | 403 | self.log.info("SMB signing activated. All SMB messages will be signed.") |
404 | self.signing_session_key = session_key | |
404 | self.signing_session_key = session_signing_key | |
405 | 405 | if self.log.isEnabledFor(logging.DEBUG): |
406 | 406 | self.log.info("SMB signing key is %s", binascii.hexlify(self.signing_session_key)) |
407 | 407 | |
1907 | 1907 | self.log.info('Performing NTLMv1 authentication (with extended security) with server challenge "%s"', binascii.hexlify(server_challenge)) |
1908 | 1908 | nt_challenge_response, lm_challenge_response, session_key = ntlm.generateChallengeResponseV1(self.password, server_challenge, True) |
1909 | 1909 | |
1910 | ntlm_data = ntlm.generateAuthenticateMessage(server_flags, | |
1911 | nt_challenge_response, | |
1912 | lm_challenge_response, | |
1913 | session_key, | |
1914 | self.username, | |
1915 | self.domain, | |
1916 | self.my_name) | |
1910 | ntlm_data, session_signing_key = ntlm.generateAuthenticateMessage(server_flags, | |
1911 | nt_challenge_response, | |
1912 | lm_challenge_response, | |
1913 | session_key, | |
1914 | self.username, | |
1915 | self.domain, | |
1916 | self.my_name) | |
1917 | 1917 | |
1918 | 1918 | if self.log.isEnabledFor(logging.DEBUG): |
1919 | 1919 | self.log.debug('NT challenge response is "%s" (%d bytes)', binascii.hexlify(nt_challenge_response), len(nt_challenge_response)) |
1933 | 1933 | |
1934 | 1934 | if self.is_signing_active: |
1935 | 1935 | self.log.info("SMB signing activated. All SMB messages will be signed.") |
1936 | self.signing_session_key = session_key | |
1936 | self.signing_session_key = session_signing_key | |
1937 | 1937 | if self.capabilities & CAP_EXTENDED_SECURITY: |
1938 | 1938 | self.signing_challenge_response = None |
1939 | 1939 | else: |
58 | 58 | |
59 | 59 | NTLM_FLAGS = NTLM_NegotiateUnicode | \ |
60 | 60 | NTLM_RequestTarget | \ |
61 | NTLM_NegotiateSign | \ | |
61 | 62 | NTLM_NegotiateNTLM | \ |
62 | 63 | NTLM_NegotiateAlwaysSign | \ |
63 | 64 | NTLM_NegotiateExtendedSecurity | \ |
64 | 65 | NTLM_NegotiateTargetInfo | \ |
65 | 66 | NTLM_NegotiateVersion | \ |
66 | 67 | NTLM_Negotiate128 | \ |
67 | NTLM_NegotiateKeyExchange | \ | |
68 | NTLM_Negotiate56 | |
68 | NTLM_NegotiateKeyExchange | |
69 | 69 | |
70 | 70 | def generateNegotiateMessage(): |
71 | 71 | """ |
92 | 92 | |
93 | 93 | # [MS-NLMP]: 3.1.5.1.2 |
94 | 94 | # http://grutz.jingojango.net/exploits/davenport-ntlm.html |
95 | session_key = request_session_key | |
95 | session_key = session_signing_key = request_session_key | |
96 | 96 | if challenge_flags & NTLM_NegotiateKeyExchange: |
97 | 97 | cipher = ARC4.new(request_session_key) |
98 | session_key = cipher.encrypt("".join([ random.choice(string.digits+string.ascii_letters) for _ in range(16) ]).encode('ascii')) | |
98 | session_signing_key = "".join([ random.choice(string.digits+string.ascii_letters) for _ in range(16) ]).encode('ascii') | |
99 | session_key = cipher.encrypt(session_signing_key) | |
99 | 100 | |
100 | 101 | lm_response_length = len(lm_response) |
101 | 102 | lm_response_offset = FORMAT_SIZE |
132 | 133 | session_key_length, session_key_length, session_key_offset, |
133 | 134 | auth_flags) |
134 | 135 | |
135 | return s + lm_response + nt_response + padding + domain_unicode + user_unicode + workstation_unicode + session_key | |
136 | return s + lm_response + nt_response + padding + domain_unicode + user_unicode + workstation_unicode + session_key, session_signing_key | |
136 | 137 | |
137 | 138 | |
138 | 139 | def decodeChallengeMessage(ntlm_data): |
370 | 370 | self.log.info('Performing NTLMv1 authentication (on SMB2) with server challenge "%s"', binascii.hexlify(server_challenge)) |
371 | 371 | nt_challenge_response, lm_challenge_response, session_key = ntlm.generateChallengeResponseV1(self.password, server_challenge, True) |
372 | 372 | |
373 | ntlm_data = ntlm.generateAuthenticateMessage(server_flags, | |
374 | nt_challenge_response, | |
375 | lm_challenge_response, | |
376 | session_key, | |
377 | self.username, | |
378 | self.domain, | |
379 | self.my_name) | |
373 | ntlm_data, session_signing_key = ntlm.generateAuthenticateMessage(server_flags, | |
374 | nt_challenge_response, | |
375 | lm_challenge_response, | |
376 | session_key, | |
377 | self.username, | |
378 | self.domain, | |
379 | self.my_name) | |
380 | 380 | |
381 | 381 | if self.log.isEnabledFor(logging.DEBUG): |
382 | 382 | self.log.debug('NT challenge response is "%s" (%d bytes)', binascii.hexlify(nt_challenge_response), len(nt_challenge_response)) |
396 | 396 | |
397 | 397 | if self.is_signing_active: |
398 | 398 | self.log.info("SMB signing activated. All SMB messages will be signed.") |
399 | self.signing_session_key = session_key | |
399 | self.signing_session_key = session_signing_key | |
400 | 400 | if self.log.isEnabledFor(logging.DEBUG): |
401 | 401 | self.log.info("SMB signing key is %s", binascii.hexlify(self.signing_session_key)) |
402 | 402 | |
1904 | 1904 | self.log.info('Performing NTLMv1 authentication (with extended security) with server challenge "%s"', binascii.hexlify(server_challenge)) |
1905 | 1905 | nt_challenge_response, lm_challenge_response, session_key = ntlm.generateChallengeResponseV1(self.password, server_challenge, True) |
1906 | 1906 | |
1907 | ntlm_data = ntlm.generateAuthenticateMessage(server_flags, | |
1908 | nt_challenge_response, | |
1909 | lm_challenge_response, | |
1910 | session_key, | |
1911 | self.username, | |
1912 | self.domain, | |
1913 | self.my_name) | |
1907 | ntlm_data, signing_session_key = ntlm.generateAuthenticateMessage(server_flags, | |
1908 | nt_challenge_response, | |
1909 | lm_challenge_response, | |
1910 | session_key, | |
1911 | self.username, | |
1912 | self.domain, | |
1913 | self.my_name) | |
1914 | 1914 | |
1915 | 1915 | if self.log.isEnabledFor(logging.DEBUG): |
1916 | 1916 | self.log.debug('NT challenge response is "%s" (%d bytes)', binascii.hexlify(nt_challenge_response), len(nt_challenge_response)) |
1930 | 1930 | |
1931 | 1931 | if self.is_signing_active: |
1932 | 1932 | self.log.info("SMB signing activated. All SMB messages will be signed.") |
1933 | self.signing_session_key = session_key | |
1933 | self.signing_session_key = signing_session_key | |
1934 | 1934 | if self.capabilities & CAP_EXTENDED_SECURITY: |
1935 | 1935 | self.signing_challenge_response = None |
1936 | 1936 | else: |
58 | 58 | |
59 | 59 | NTLM_FLAGS = NTLM_NegotiateUnicode | \ |
60 | 60 | NTLM_RequestTarget | \ |
61 | NTLM_NegotiateSign | \ | |
61 | 62 | NTLM_NegotiateNTLM | \ |
62 | 63 | NTLM_NegotiateAlwaysSign | \ |
63 | 64 | NTLM_NegotiateExtendedSecurity | \ |
64 | 65 | NTLM_NegotiateTargetInfo | \ |
65 | 66 | NTLM_NegotiateVersion | \ |
66 | 67 | NTLM_Negotiate128 | \ |
67 | NTLM_NegotiateKeyExchange | \ | |
68 | NTLM_Negotiate56 | |
68 | NTLM_NegotiateKeyExchange | |
69 | 69 | |
70 | 70 | def generateNegotiateMessage(): |
71 | 71 | """ |
92 | 92 | |
93 | 93 | # [MS-NLMP]: 3.1.5.1.2 |
94 | 94 | # http://grutz.jingojango.net/exploits/davenport-ntlm.html |
95 | session_key = request_session_key | |
95 | session_key = session_signing_key = request_session_key | |
96 | 96 | if challenge_flags & NTLM_NegotiateKeyExchange: |
97 | 97 | cipher = ARC4.new(request_session_key) |
98 | session_key = cipher.encrypt("".join([ random.choice(string.digits+string.ascii_letters) for _ in range(16) ]).encode('ascii')) | |
98 | session_signing_key = "".join([ random.choice(string.digits+string.ascii_letters) for _ in range(16) ]).encode('ascii') | |
99 | session_key = cipher.encrypt(session_signing_key) | |
99 | 100 | |
100 | 101 | lm_response_length = len(lm_response) |
101 | 102 | lm_response_offset = FORMAT_SIZE |
132 | 133 | session_key_length, session_key_length, session_key_offset, |
133 | 134 | auth_flags) |
134 | 135 | |
135 | return s + lm_response + nt_response + padding + domain_unicode + user_unicode + workstation_unicode + session_key | |
136 | return s + lm_response + nt_response + padding + domain_unicode + user_unicode + workstation_unicode + session_key, session_signing_key | |
136 | 137 | |
137 | 138 | |
138 | 139 | def decodeChallengeMessage(ntlm_data): |