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

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# For MS-RPCH
11# Can be programmed to be used in relay attacks
12# Probably for future MAPI
13#
14# Authors:
15# Arseniy Sharoglazov <mohemiv@gmail.com> / Positive Technologies (https://www.ptsecurity.com/)
16#
18import re
19import ssl
20import base64
21import binascii
23try:
24 from http.client import HTTPConnection, HTTPSConnection
25except ImportError:
26 from httplib import HTTPConnection, HTTPSConnection
28from impacket import ntlm, LOG
30# Auth types
31AUTH_AUTO = 'Auto'
32AUTH_BASIC = 'Basic'
33AUTH_NTLM = 'NTLM'
34AUTH_NEGOTIATE = 'Negotiate'
35AUTH_BEARER = 'Bearer'
36AUTH_DIGEST = 'Digest'
38################################################################################
39# CLASSES
40################################################################################
41class HTTPClientSecurityProvider:
42 def __init__(self, auth_type=AUTH_AUTO):
43 self.__username = None
44 self.__password = None
45 self.__domain = None
46 self.__lmhash = ''
47 self.__nthash = ''
48 self.__aesKey = ''
49 self.__TGT = None
50 self.__TGS = None
52 self.__auth_type = auth_type
54 self.__auth_types = []
55 self.__ntlmssp_info = None
57 def set_auth_type(self, auth_type):
58 self.__auth_type = auth_type
60 def get_auth_type(self):
61 return self.__auth_type
63 def get_auth_types(self):
64 return self.__auth_types
66 def get_ntlmssp_info(self):
67 return self.__ntlmssp_info
69 def set_credentials(self, username, password, domain='', lmhash='', nthash='', aesKey='', TGT=None, TGS=None):
70 self.__username = username
71 self.__password = password
72 self.__domain = domain
74 if lmhash != '' or nthash != '':
75 if len(lmhash) % 2:
76 lmhash = '0%s' % lmhash
77 if len(nthash) % 2:
78 nthash = '0%s' % nthash
80 try: # just in case they were converted already
81 self.__lmhash = binascii.unhexlify(lmhash)
82 self.__nthash = binascii.unhexlify(nthash)
83 except:
84 self.__lmhash = lmhash
85 self.__nthash = nthash
86 pass
88 self.__aesKey = aesKey
89 self.__TGT = TGT
90 self.__TGS = TGS
92 def parse_www_authenticate(self, header):
93 ret = []
95 if 'NTLM' in header:
96 ret.append(AUTH_NTLM)
97 if 'Basic' in header:
98 ret.append(AUTH_BASIC)
99 if 'Negotiate' in header:
100 ret.append(AUTH_NEGOTIATE)
101 if 'Bearer' in header:
102 ret.append(AUTH_BEARER)
103 if 'Digest' in header:
104 ret.append(AUTH_DIGEST)
106 return ret
108 def connect(self, protocol, host_L6):
109 if protocol == 'http':
110 return HTTPConnection(host_L6)
111 else:
112 try:
113 uv_context = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
114 return HTTPSConnection(host_L6, context=uv_context)
115 except AttributeError:
116 return HTTPSConnection(host_L6)
118 def get_auth_headers(self, http_obj, method, path, headers):
119 if self.__auth_type == AUTH_BASIC:
120 return self.get_auth_headers_basic(http_obj, method, path, headers)
121 elif self.__auth_type in [AUTH_AUTO, AUTH_NTLM]:
122 return self.get_auth_headers_auto(http_obj, method, path, headers)
123 else:
124 raise Exception('%s auth type not supported' % self.__auth_type)
126 def get_auth_headers_basic(self, http_obj, method, path, headers):
127 if self.__lmhash != '' or self.__nthash != '' or \
128 self.__aesKey != '' or self.__TGT != None or self.__TGS != None:
129 raise Exception('Basic authentication in HTTP connection used, '
130 'so set a plaintext credentials to connect.')
132 if self.__domain == '':
133 auth_line = self.__username + ':' + self.__password
134 else:
135 auth_line = self.__domain + '\\' + self.__username + ':' + self.__password
137 auth_line_http = 'Basic %s' % base64.b64encode(auth_line.encode('UTF-8')).decode('ascii')
139 # Format: auth_headers, reserved, ...
140 return {'Authorization': auth_line_http}, None
142 # It's important that the class contains the separate method that
143 # gets NTLM Type 1 value, as this way the class can be programmed to
144 # be used in relay attacks
145 def send_ntlm_type1(self, http_obj, method, path, headers, negotiateMessage):
146 auth_headers = headers.copy()
147 auth_headers['Content-Length'] = '0'
148 auth_headers['Authorization'] = 'NTLM %s' % base64.b64encode(negotiateMessage).decode('ascii')
149 http_obj.request(method, path, headers=auth_headers)
150 res = http_obj.getresponse()
151 res.read()
153 if res.status != 401:
154 raise Exception('Status code returned: %d. '
155 'Authentication does not seem required for url %s'
156 % (res.status, path)
157 )
159 if res.getheader('WWW-Authenticate') is None:
160 raise Exception('No authentication requested by '
161 'the server for url %s' % path)
163 if self.__auth_types == []:
164 self.__auth_types = self.parse_www_authenticate(res.getheader('WWW-Authenticate'))
166 if AUTH_NTLM not in self.__auth_types:
167 # NTLM auth not supported for url
168 return None, None
170 try:
171 serverChallengeBase64 = re.search('NTLM ([a-zA-Z0-9+/]+={0,2})',
172 res.getheader('WWW-Authenticate')).group(1)
173 serverChallenge = base64.b64decode(serverChallengeBase64)
174 except (IndexError, KeyError, AttributeError):
175 raise Exception('No NTLM challenge returned from server for url %s' % path)
177 if not self.__ntlmssp_info:
178 challenge = ntlm.NTLMAuthChallenge(serverChallenge)
179 self.__ntlmssp_info = ntlm.AV_PAIRS(challenge['TargetInfoFields'])
181 # Format: serverChallenge, reserved, ...
182 return serverChallenge, None
184 def get_auth_headers_auto(self, http_obj, method, path, headers):
185 if self.__aesKey != '' or self.__TGT != None or self.__TGS != None:
186 raise Exception('NTLM authentication in HTTP connection used, ' \
187 'cannot use Kerberos.')
189 auth = ntlm.getNTLMSSPType1(domain=self.__domain)
190 serverChallenge = self.send_ntlm_type1(http_obj, method, path, headers, auth.getData())[0]
192 if serverChallenge is not None:
193 self.__auth_type = AUTH_NTLM
195 type3, exportedSessionKey = ntlm.getNTLMSSPType3(auth, serverChallenge, self.__username,
196 self.__password, self.__domain,
197 self.__lmhash, self.__nthash)
199 auth_line_http = 'NTLM %s' % base64.b64encode(type3.getData()).decode('ascii')
200 else:
201 if self.__auth_type == AUTH_AUTO and AUTH_BASIC in self.__auth_types:
202 self.__auth_type = AUTH_BASIC
203 return self.get_auth_headers_basic(http_obj, method, path, headers)
204 else:
205 raise Exception('No supported auth offered by URL: %s' % self.__auth_types)
207 # Format: auth_headers, reserved, ...
208 return {'Authorization': auth_line_http}, None