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# SECUREAUTH LABS. Copyright 2018 SecureAuth Corporation. All rights reserved. 

2# 

3# This software is provided under under a slightly modified version 

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

5# for more information. 

6# 

7# HTTP Protocol Client 

8# 

9# Author: 

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

11# Alberto Solino (@agsolino) 

12# 

13# Description: 

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

15# 

16import re 

17import ssl 

18try: 

19 from http.client import HTTPConnection, HTTPSConnection, ResponseNotReady 

20except ImportError: 

21 from httplib import HTTPConnection, HTTPSConnection, ResponseNotReady 

22import base64 

23 

24from struct import unpack 

25from impacket import LOG 

26from impacket.examples.ntlmrelayx.clients import ProtocolClient 

27from impacket.nt_errors import STATUS_SUCCESS, STATUS_ACCESS_DENIED 

28from impacket.ntlm import NTLMAuthChallenge 

29from impacket.spnego import SPNEGO_NegTokenResp 

30 

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

32 

33class HTTPRelayClient(ProtocolClient): 

34 PLUGIN_NAME = "HTTP" 

35 

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

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

38 self.extendedSecurity = extendedSecurity 

39 self.negotiateMessage = None 

40 self.authenticateMessageBlob = None 

41 self.server = None 

42 

43 def initConnection(self): 

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

45 self.lastresult = None 

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

47 self.path = '/' 

48 else: 

49 self.path = self.target.path 

50 return True 

51 

52 def sendNegotiate(self,negotiateMessage): 

53 #Check if server wants auth 

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

55 res = self.session.getresponse() 

56 res.read() 

57 if res.status != 401: 

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

59 try: 

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

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

62 return False 

63 except (KeyError, TypeError): 

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

65 return False 

66 

67 #Negotiate auth 

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

69 headers = {'Authorization':'NTLM %s' % negotiate} 

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

71 res = self.session.getresponse() 

72 res.read() 

73 try: 

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

75 serverChallenge = base64.b64decode(serverChallengeBase64) 

76 challenge = NTLMAuthChallenge() 

77 challenge.fromString(serverChallenge) 

78 return challenge 

79 except (IndexError, KeyError, AttributeError): 

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

81 

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

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

84 respToken2 = SPNEGO_NegTokenResp(authenticateMessageBlob) 

85 token = respToken2['ResponseToken'] 

86 else: 

87 token = authenticateMessageBlob 

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

89 headers = {'Authorization':'NTLM %s' % auth} 

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

91 res = self.session.getresponse() 

92 if res.status == 401: 

93 return None, STATUS_ACCESS_DENIED 

94 else: 

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

96 #Cache this 

97 self.lastresult = res.read() 

98 return None, STATUS_SUCCESS 

99 

100 def killConnection(self): 

101 if self.session is not None: 

102 self.session.close() 

103 self.session = None 

104 

105 def keepAlive(self): 

106 # Do a HEAD for favicon.ico 

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

108 self.session.getresponse() 

109 

110class HTTPSRelayClient(HTTPRelayClient): 

111 PLUGIN_NAME = "HTTPS" 

112 

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

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

115 

116 def initConnection(self): 

117 self.lastresult = None 

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

119 self.path = '/' 

120 else: 

121 self.path = self.target.path 

122 try: 

123 uv_context = ssl.SSLContext(ssl.PROTOCOL_SSLv23) 

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

125 except AttributeError: 

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

127 return True