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

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

## 128 statements

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#

17

18 import re

19 import ssl

20 import base64

21 import binascii

22

23 try:

24 from http.client import HTTPConnection, HTTPSConnection

25 except ImportError:

26 from httplib import HTTPConnection, HTTPSConnection

27

28 from impacket import ntlm, LOG

29

30# Auth types

31 AUTH_AUTO = 'Auto'

32 AUTH_BASIC = 'Basic'

33 AUTH_NTLM = 'NTLM'

34 AUTH_NEGOTIATE = 'Negotiate'

35 AUTH_BEARER = 'Bearer'

36 AUTH_DIGEST = 'Digest'

37

38################################################################################

39# CLASSES

40################################################################################

41 class 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

51

52 self.__auth_type = auth_type

53

54 self.__auth_types = []

55 self.__ntlmssp_info = None

56

57 def set_auth_type(self, auth_type):

58 self.__auth_type = auth_type

59

60 def get_auth_type(self):

61 return self.__auth_type

62

63 def get_auth_types(self):

64 return self.__auth_types

65

66 def get_ntlmssp_info(self):

67 return self.__ntlmssp_info

68

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

73

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

79

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

87

88 self.__aesKey = aesKey

89 self.__TGT = TGT

90 self.__TGS = TGS

91

92 def parse_www_authenticate(self, header):

93 ret = []

94

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)

105

106 return ret

107

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)

117

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)

125

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.')

131

132 if self.__domain == '':

133 auth_line = self.__username + ':' + self.__password

134 else:

135 auth_line = self.__domain + '\\' + self.__username + ':' + self.__password

136

137 auth_line_http = 'Basic %s' % base64.b64encode(auth_line.encode('UTF-8')).decode('ascii')

138

139 # Format: auth_headers, reserved, ...

140 return {'Authorization': auth_line_http}, None

141

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()

152

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 )

158

159 if res.getheader('WWW-Authenticate') is None:

160 raise Exception('No authentication requested by '

161 'the server for url %s' % path)

162

163 if self.__auth_types == []:

164 self.__auth_types = self.parse_www_authenticate(res.getheader('WWW-Authenticate'))

165

166 if AUTH_NTLM not in self.__auth_types:

167 # NTLM auth not supported for url

168 return None, None

169

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)

176

177 if not self.__ntlmssp_info:

178 challenge = ntlm.NTLMAuthChallenge(serverChallenge)

179 self.__ntlmssp_info = ntlm.AV_PAIRS(challenge['TargetInfoFields'])

180

181 # Format: serverChallenge, reserved, ...

182 return serverChallenge, None

183

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.')

188

189 auth = ntlm.getNTLMSSPType1(domain=self.__domain)

190 serverChallenge = self.send_ntlm_type1(http_obj, method, path, headers, auth.getData())[0]

191

192 if serverChallenge is not None:

193 self.__auth_type = AUTH_NTLM

194

195 type3, exportedSessionKey = ntlm.getNTLMSSPType3(auth, serverChallenge, self.__username,

196 self.__password, self.__domain,

197 self.__lmhash, self.__nthash)

198

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)

206

207 # Format: auth_headers, reserved, ...

208 return {'Authorization': auth_line_http}, None