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

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

## 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# 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

15

16 import re

17 import ssl

18 import base64

19 import binascii

20

21 try:

22 from http.client import HTTPConnection, HTTPSConnection

23 except ImportError:

24 from httplib import HTTPConnection, HTTPSConnection

25

26 from impacket import ntlm, LOG

27

28# Auth types

29 AUTH_AUTO = 'Auto'

30 AUTH_BASIC = 'Basic'

31 AUTH_NTLM = 'NTLM'

32 AUTH_NEGOTIATE = 'Negotiate'

33 AUTH_BEARER = 'Bearer'

34 AUTH_DIGEST = 'Digest'

35

36################################################################################

37# CLASSES

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

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

49

50 self.__auth_type = auth_type

51

52 self.__auth_types = []

53 self.__ntlmssp_info = None

54

55 def set_auth_type(self, auth_type):

56 self.__auth_type = auth_type

57

58 def get_auth_type(self):

59 return self.__auth_type

60

61 def get_auth_types(self):

62 return self.__auth_types

63

64 def get_ntlmssp_info(self):

65 return self.__ntlmssp_info

66

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

71

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

77

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

85

86 self.__aesKey = aesKey

87 self.__TGT = TGT

88 self.__TGS = TGS

89

90 def parse_www_authenticate(self, header):

91 ret = []

92

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)

103

104 return ret

105

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)

115

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)

123

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

129

130 if self.__domain == '':

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

132 else:

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

134

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

136

137 # Format: auth_headers, reserved, ...

138 return {'Authorization': auth_line_http}, None

139

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

150

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 )

156

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

158 raise Exception('No authentication requested by '

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

160

161 if self.__auth_types == []:

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

163

164 if AUTH_NTLM not in self.__auth_types:

165 # NTLM auth not supported for url

166 return None, None

167

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)

174

175 if not self.__ntlmssp_info:

176 challenge = ntlm.NTLMAuthChallenge(serverChallenge)

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

178

179 # Format: serverChallenge, reserved, ...

180 return serverChallenge, None

181

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

186

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

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

189

190 if serverChallenge is not None:

191 self.__auth_type = AUTH_NTLM

192

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

194 self.__password, self.__domain,

195 self.__lmhash, self.__nthash)

196

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)

204

205 # Format: auth_headers, reserved, ...

206 return {'Authorization': auth_line_http}, None