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# A Socks Proxy for the MSSQL Protocol 

8# 

9# Author: 

10# Alberto Solino (@agsolino) 

11# 

12# Description: 

13# A simple SOCKS server that proxy connection to relayed connections 

14# 

15# ToDo: 

16# 

17 

18import struct 

19import random 

20 

21from impacket import LOG 

22from impacket.examples.ntlmrelayx.servers.socksserver import SocksRelay 

23from impacket.tds import TDSPacket, TDS_STATUS_NORMAL, TDS_STATUS_EOM, TDS_PRE_LOGIN, TDS_ENCRYPT_NOT_SUP, TDS_TABULAR, \ 

24 TDS_LOGIN, TDS_LOGIN7, TDS_PRELOGIN, TDS_INTEGRATED_SECURITY_ON 

25from impacket.ntlm import NTLMAuthChallengeResponse 

26try: 

27 from OpenSSL import SSL 

28except: 

29 LOG.critical("pyOpenSSL is not installed, can't continue") 

30 raise 

31 

32# Besides using this base class you need to define one global variable when 

33# writing a plugin: 

34PLUGIN_CLASS = "MSSQLSocksRelay" 

35 

36class MSSQLSocksRelay(SocksRelay): 

37 PLUGIN_NAME = 'MSSQL Socks Plugin' 

38 PLUGIN_SCHEME = 'MSSQL' 

39 

40 def __init__(self, targetHost, targetPort, socksSocket, activeRelays): 

41 SocksRelay.__init__(self, targetHost, targetPort, socksSocket, activeRelays) 

42 self.isSSL = False 

43 self.tlsSocket = None 

44 self.packetSize = 32763 

45 self.session = None 

46 

47 @staticmethod 

48 def getProtocolPort(): 

49 return 1433 

50 

51 def initConnection(self): 

52 pass 

53 

54 def skipAuthentication(self): 

55 

56 # 1. First packet should be a TDS_PRELOGIN() 

57 tds = self.recvTDS() 

58 if tds['Type'] != TDS_PRE_LOGIN: 

59 # Unexpected packet 

60 LOG.debug('Unexpected packet type %d instead of TDS_PRE_LOGIN' % tds['Type']) 

61 return False 

62 

63 prelogin = TDS_PRELOGIN() 

64 prelogin['Version'] = b"\x08\x00\x01\x55\x00\x00" 

65 prelogin['Encryption'] = TDS_ENCRYPT_NOT_SUP 

66 prelogin['ThreadID'] = struct.pack('<L',random.randint(0,65535)) 

67 prelogin['Instance'] = b'\x00' 

68 

69 # Answering who we are 

70 self.sendTDS(TDS_TABULAR, prelogin.getData(), 0) 

71 

72 # 2. Packet should be a TDS_LOGIN 

73 tds = self.recvTDS() 

74 

75 if tds['Type'] != TDS_LOGIN7: 

76 # Unexpected packet 

77 LOG.debug('Unexpected packet type %d instead of TDS_LOGIN' % tds['Type']) 

78 return False 

79 

80 login = TDS_LOGIN() 

81 login.fromString(tds['Data']) 

82 if login['OptionFlags2'] & TDS_INTEGRATED_SECURITY_ON: 

83 # Windows Authentication enabled 

84 # Send the resp we've got from the original relay 

85 TDSResponse = self.sessionData['NTLM_CHALLENGE'] 

86 self.sendTDS(TDSResponse['Type'], TDSResponse['Data'], 0) 

87 

88 # Here we should get the NTLM_AUTHENTICATE 

89 tds = self.recvTDS() 

90 authenticateMessage = NTLMAuthChallengeResponse() 

91 authenticateMessage.fromString(tds['Data']) 

92 self.username = authenticateMessage['user_name'] 

93 try: 

94 self.username = ('%s/%s' % (authenticateMessage['domain_name'].decode('utf-16le'), 

95 authenticateMessage['user_name'].decode('utf-16le'))).upper() 

96 except UnicodeDecodeError: 

97 # Not Unicode encoded? 

98 self.username = ('%s/%s' % (authenticateMessage['domain_name'], authenticateMessage['user_name'])).upper() 

99 

100 else: 

101 if login['UserName'].find('/') >=0: 

102 try: 

103 self.username = login['UserName'].upper().decode('utf-16le') 

104 except UnicodeDecodeError: 

105 # Not Unicode encoded? 

106 self.username = login['UserName'].upper() 

107 

108 else: 

109 try: 

110 self.username = ('/%s' % login['UserName'].decode('utf-16le')).upper() 

111 except UnicodeDecodeError: 

112 # Not Unicode encoded? 

113 self.username = ('/%s' % login['UserName']).upper() 

114 

115 # Check if we have a connection for the user 

116 if self.username in self.activeRelays: 

117 # Check the connection is not inUse 

118 if self.activeRelays[self.username]['inUse'] is True: 

119 LOG.error('MSSQL: Connection for %s@%s(%s) is being used at the moment!' % ( 

120 self.username, self.targetHost, self.targetPort)) 

121 return False 

122 else: 

123 LOG.info('MSSQL: Proxying client session for %s@%s(%s)' % ( 

124 self.username, self.targetHost, self.targetPort)) 

125 self.session = self.activeRelays[self.username]['protocolClient'].session 

126 else: 

127 LOG.error('MSSQL: No session for %s@%s(%s) available' % ( 

128 self.username, self.targetHost, self.targetPort)) 

129 return False 

130 

131 # We have a session relayed, let's answer back with the data 

132 if login['OptionFlags2'] & TDS_INTEGRATED_SECURITY_ON: 

133 TDSResponse = self.sessionData['AUTH_ANSWER'] 

134 self.sendTDS(TDSResponse['Type'], TDSResponse['Data'], 0) 

135 else: 

136 TDSResponse = self.sessionData['AUTH_ANSWER'] 

137 self.sendTDS(TDSResponse['Type'], TDSResponse['Data'], 0) 

138 

139 return True 

140 

141 def tunnelConnection(self): 

142 # For the rest of the remaining packets, we should just read and send. Except when trying to log out, 

143 # that's forbidden! ;) 

144 try: 

145 while True: 

146 # 1. Get Data from client 

147 tds = self.recvTDS() 

148 # 2. Send it to the relayed session 

149 self.session.sendTDS(tds['Type'], tds['Data'], 0) 

150 # 3. Get the target's answer 

151 tds = self.session.recvTDS() 

152 # 4. Send it back to the client 

153 self.sendTDS(tds['Type'], tds['Data'], 0) 

154 except Exception: 

155 # Probably an error here 

156 LOG.debug('Exception:', exc_info=True) 

157 

158 return True 

159 

160 def sendTDS(self, packetType, data, packetID = 1): 

161 if (len(data)-8) > self.packetSize: 

162 remaining = data[self.packetSize-8:] 

163 tds = TDSPacket() 

164 tds['Type'] = packetType 

165 tds['Status'] = TDS_STATUS_NORMAL 

166 tds['PacketID'] = packetID 

167 tds['Data'] = data[:self.packetSize-8] 

168 self.socketSendall(tds.getData()) 

169 

170 while len(remaining) > (self.packetSize-8): 

171 packetID += 1 

172 tds['PacketID'] = packetID 

173 tds['Data'] = remaining[:self.packetSize-8] 

174 self.socketSendall(tds.getData()) 

175 remaining = remaining[self.packetSize-8:] 

176 data = remaining 

177 packetID+=1 

178 

179 tds = TDSPacket() 

180 tds['Type'] = packetType 

181 tds['Status'] = TDS_STATUS_EOM 

182 tds['PacketID'] = packetID 

183 tds['Data'] = data 

184 self.socketSendall(tds.getData()) 

185 

186 def socketSendall(self,data): 

187 if self.tlsSocket is None: 

188 return self.socksSocket.sendall(data) 

189 else: 

190 self.tlsSocket.sendall(data) 

191 dd = self.tlsSocket.bio_read(self.packetSize) 

192 return self.socksSocket.sendall(dd) 

193 

194 def socketRecv(self, packetSize): 

195 data = self.socksSocket.recv(packetSize) 

196 if self.tlsSocket is not None: 

197 dd = b'' 

198 self.tlsSocket.bio_write(data) 

199 while True: 

200 try: 

201 dd += self.tlsSocket.read(packetSize) 

202 except SSL.WantReadError: 

203 data2 = self.socksSocket.recv(packetSize - len(data) ) 

204 self.tlsSocket.bio_write(data2) 

205 pass 

206 else: 

207 data = dd 

208 break 

209 return data 

210 

211 def recvTDS(self, packetSize=None): 

212 # Do reassembly here 

213 if packetSize is None: 

214 packetSize = self.packetSize 

215 packet = TDSPacket(self.socketRecv(packetSize)) 

216 status = packet['Status'] 

217 packetLen = packet['Length'] - 8 

218 while packetLen > len(packet['Data']): 

219 data = self.socketRecv(packetSize) 

220 packet['Data'] += data 

221 

222 remaining = None 

223 if packetLen < len(packet['Data']): 

224 remaining = packet['Data'][packetLen:] 

225 packet['Data'] = packet['Data'][:packetLen] 

226 

227 while status != TDS_STATUS_EOM: 

228 if remaining is not None: 

229 tmpPacket = TDSPacket(remaining) 

230 else: 

231 tmpPacket = TDSPacket(self.socketRecv(packetSize)) 

232 

233 packetLen = tmpPacket['Length'] - 8 

234 while packetLen > len(tmpPacket['Data']): 

235 data = self.socketRecv(packetSize) 

236 tmpPacket['Data'] += data 

237 

238 remaining = None 

239 if packetLen < len(tmpPacket['Data']): 

240 remaining = tmpPacket['Data'][packetLen:] 

241 tmpPacket['Data'] = tmpPacket['Data'][:packetLen] 

242 

243 status = tmpPacket['Status'] 

244 packet['Data'] += tmpPacket['Data'] 

245 packet['Length'] += tmpPacket['Length'] - 8 

246 

247 # print packet['Length'] 

248 return packet