Coverage for /root/GitHubProjects/impacket/impacket/http.py : 22%

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 2020 SecureAuth Corporation. All rights reserved.
2#
3# This software is provided under a slightly modified version
4# of the Apache Software License. See the accompanying LICENSE file
5# for more information.
6#
7# Authors:
8# Arseniy Sharoglazov <mohemiv@gmail.com> / Positive Technologies (https://www.ptsecurity.com/)
9#
10# Description:
11# For MS-RPCH
12# Can be programmed to be used in relay attacks
13#
14# Probably for future MAPI
16import re
17import ssl
18import base64
19import binascii
21try:
22 from http.client import HTTPConnection, HTTPSConnection
23except ImportError:
24 from httplib import HTTPConnection, HTTPSConnection
26from impacket import ntlm, LOG
28# Auth types
29AUTH_AUTO = 'Auto'
30AUTH_BASIC = 'Basic'
31AUTH_NTLM = 'NTLM'
32AUTH_NEGOTIATE = 'Negotiate'
33AUTH_BEARER = 'Bearer'
34AUTH_DIGEST = 'Digest'
36################################################################################
37# CLASSES
38################################################################################
39class HTTPClientSecurityProvider:
40 def __init__(self, auth_type=AUTH_AUTO):
41 self.__username = None
42 self.__password = None
43 self.__domain = None
44 self.__lmhash = ''
45 self.__nthash = ''
46 self.__aesKey = ''
47 self.__TGT = None
48 self.__TGS = None
50 self.__auth_type = auth_type
52 self.__auth_types = []
53 self.__ntlmssp_info = None
55 def set_auth_type(self, auth_type):
56 self.__auth_type = auth_type
58 def get_auth_type(self):
59 return self.__auth_type
61 def get_auth_types(self):
62 return self.__auth_types
64 def get_ntlmssp_info(self):
65 return self.__ntlmssp_info
67 def set_credentials(self, username, password, domain='', lmhash='', nthash='', aesKey='', TGT=None, TGS=None):
68 self.__username = username
69 self.__password = password
70 self.__domain = domain
72 if lmhash != '' or nthash != '':
73 if len(lmhash) % 2:
74 lmhash = '0%s' % lmhash
75 if len(nthash) % 2:
76 nthash = '0%s' % nthash
78 try: # just in case they were converted already
79 self.__lmhash = binascii.unhexlify(lmhash)
80 self.__nthash = binascii.unhexlify(nthash)
81 except:
82 self.__lmhash = lmhash
83 self.__nthash = nthash
84 pass
86 self.__aesKey = aesKey
87 self.__TGT = TGT
88 self.__TGS = TGS
90 def parse_www_authenticate(self, header):
91 ret = []
93 if 'NTLM' in header:
94 ret.append(AUTH_NTLM)
95 if 'Basic' in header:
96 ret.append(AUTH_BASIC)
97 if 'Negotiate' in header:
98 ret.append(AUTH_NEGOTIATE)
99 if 'Bearer' in header:
100 ret.append(AUTH_BEARER)
101 if 'Digest' in header:
102 ret.append(AUTH_DIGEST)
104 return ret
106 def connect(self, protocol, host_L6):
107 if protocol == 'http':
108 return HTTPConnection(host_L6)
109 else:
110 try:
111 uv_context = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
112 return HTTPSConnection(host_L6, context=uv_context)
113 except AttributeError:
114 return HTTPSConnection(host_L6)
116 def get_auth_headers(self, http_obj, method, path, headers):
117 if self.__auth_type == AUTH_BASIC:
118 return self.get_auth_headers_basic(http_obj, method, path, headers)
119 elif self.__auth_type in [AUTH_AUTO, AUTH_NTLM]:
120 return self.get_auth_headers_auto(http_obj, method, path, headers)
121 else:
122 raise Exception('%s auth type not supported' % self.__auth_type)
124 def get_auth_headers_basic(self, http_obj, method, path, headers):
125 if self.__lmhash != '' or self.__nthash != '' or \
126 self.__aesKey != '' or self.__TGT != None or self.__TGS != None:
127 raise Exception('Basic authentication in HTTP connection used, '
128 'so set a plaintext credentials to connect.')
130 if self.__domain == '':
131 auth_line = self.__username + ':' + self.__password
132 else:
133 auth_line = self.__domain + '\\' + self.__username + ':' + self.__password
135 auth_line_http = 'Basic %s' % base64.b64encode(auth_line.encode('UTF-8')).decode('ascii')
137 # Format: auth_headers, reserved, ...
138 return {'Authorization': auth_line_http}, None
140 # It's important that the class contains the separate method that
141 # gets NTLM Type 1 value, as this way the class can be programmed to
142 # be used in relay attacks
143 def send_ntlm_type1(self, http_obj, method, path, headers, negotiateMessage):
144 auth_headers = headers.copy()
145 auth_headers['Content-Length'] = '0'
146 auth_headers['Authorization'] = 'NTLM %s' % base64.b64encode(negotiateMessage).decode('ascii')
147 http_obj.request(method, path, headers=auth_headers)
148 res = http_obj.getresponse()
149 res.read()
151 if res.status != 401:
152 raise Exception('Status code returned: %d. '
153 'Authentication does not seem required for url %s'
154 % (res.status, path)
155 )
157 if res.getheader('WWW-Authenticate') is None:
158 raise Exception('No authentication requested by '
159 'the server for url %s' % path)
161 if self.__auth_types == []:
162 self.__auth_types = self.parse_www_authenticate(res.getheader('WWW-Authenticate'))
164 if AUTH_NTLM not in self.__auth_types:
165 # NTLM auth not supported for url
166 return None, None
168 try:
169 serverChallengeBase64 = re.search('NTLM ([a-zA-Z0-9+/]+={0,2})',
170 res.getheader('WWW-Authenticate')).group(1)
171 serverChallenge = base64.b64decode(serverChallengeBase64)
172 except (IndexError, KeyError, AttributeError):
173 raise Exception('No NTLM challenge returned from server for url %s' % path)
175 if not self.__ntlmssp_info:
176 challenge = ntlm.NTLMAuthChallenge(serverChallenge)
177 self.__ntlmssp_info = ntlm.AV_PAIRS(challenge['TargetInfoFields'])
179 # Format: serverChallenge, reserved, ...
180 return serverChallenge, None
182 def get_auth_headers_auto(self, http_obj, method, path, headers):
183 if self.__aesKey != '' or self.__TGT != None or self.__TGS != None:
184 raise Exception('NTLM authentication in HTTP connection used, ' \
185 'cannot use Kerberos.')
187 auth = ntlm.getNTLMSSPType1(domain=self.__domain)
188 serverChallenge = self.send_ntlm_type1(http_obj, method, path, headers, auth.getData())[0]
190 if serverChallenge is not None:
191 self.__auth_type = AUTH_NTLM
193 type3, exportedSessionKey = ntlm.getNTLMSSPType3(auth, serverChallenge, self.__username,
194 self.__password, self.__domain,
195 self.__lmhash, self.__nthash)
197 auth_line_http = 'NTLM %s' % base64.b64encode(type3.getData()).decode('ascii')
198 else:
199 if self.__auth_type == AUTH_AUTO and AUTH_BASIC in self.__auth_types:
200 self.__auth_type = AUTH_BASIC
201 return self.get_auth_headers_basic(http_obj, method, path, headers)
202 else:
203 raise Exception('No supported auth offered by URL: %s' % self.__auth_types)
205 # Format: auth_headers, reserved, ...
206 return {'Authorization': auth_line_http}, None