Coverage for /root/GitHubProjects/impacket/impacket/examples/ntlmrelayx/clients/mssqlrelayclient.py: 23%

# Coverage for /root/GitHubProjects/impacket/impacket/examples/ntlmrelayx/clients/mssqlrelayclient.py : 23%

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

20 import random

21 import string

22 from struct import unpack

23

24 from impacket import LOG

25 from impacket.examples.ntlmrelayx.clients import ProtocolClient

26 from 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

28 from impacket.ntlm import NTLMAuthChallenge

29 from impacket.nt_errors import STATUS_SUCCESS, STATUS_ACCESS_DENIED

30 from impacket.spnego import SPNEGO_NegTokenResp

31

32 try:

33 from OpenSSL import SSL

34 except Exception:

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

36

37 PROTOCOL_CLIENT_CLASS = "MSSQLRelayClient"

38

39 class 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 = {}

44

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

51

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

67

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

74

75 def sendNegotiate(self,negotiateMessage):

76 #Also partly copied from tds.py

77 login = TDS_LOGIN()

78

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

86

87 # NTLMSSP Negotiate

88 login['SSPI'] = negotiateMessage

89 login['Length'] = len(login.getData())

90

91 # Send the NTLMSSP Negotiate

92 self.sendTDS(TDS_LOGIN7, login.getData())

93

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

98

99 tds = self.recvTDS()

100 self.sessionData['NTLM_CHALLENGE'] = tds

101

102 challenge = NTLMAuthChallenge()

103 challenge.fromString(tds['Data'][3:])

104 #challenge.dump()

105

106 return challenge

107

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

114

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

126

127 def close(self):

128 return self.disconnect()

129

130

131 class MSSQLRelayClient(ProtocolClient):

132 PLUGIN_NAME = "MSSQL"

133

134 def __init__(self, serverConfig, targetHost, targetPort = 1433, extendedSecurity=True ):

135 ProtocolClient.__init__(self, serverConfig, targetHost, targetPort, extendedSecurity)

136 self.extendedSecurity = extendedSecurity

137

138 self.domainIp = None

139 self.machineAccount = None

140 self.machineHashes = None

141

142 def initConnection(self):

143 self.session = MYMSSQL(self.targetHost, self.targetPort)

144 self.session.initConnection()

145 return True

146

147 def keepAlive(self):

148 # Don't know yet what needs to be done for TDS

149 pass

150

151 def killConnection(self):

152 if self.session is not None:

153 self.session.disconnect()

154 self.session = None

155

156 def sendNegotiate(self, negotiateMessage):

157 return self.session.sendNegotiate(negotiateMessage)

158

159 def sendAuth(self, authenticateMessageBlob, serverChallenge=None):

160 self.sessionData = self.session.sessionData

161 return self.session.sendAuth(authenticateMessageBlob, serverChallenge)