1# Copyright (c) 2003-2016 CORE Security Technologies 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# Author: Alberto Solino (@agsolino) 8# 9# Description: 10# Transport implementations for the DCE/RPC protocol. 11# 12 13import re 14import socket 15import binascii 16import os 17 18from impacket.smbconnection import smb, SMBConnection 19from impacket import nmb 20from impacket import ntlm 21from impacket.dcerpc.v5.rpcrt import DCERPCException, DCERPC_v5, DCERPC_v4 22 23 24class DCERPCStringBinding: 25 parser = re.compile(r'(?:([a-fA-F0-9-]{8}(?:-[a-fA-F0-9-]{4}){3}-[a-fA-F0-9-]{12})@)?' # UUID (opt.) 26 +'([_a-zA-Z0-9]*):' # Protocol Sequence 27 +'([^\[]*)' # Network Address (opt.) 28 +'(?:\[([^\]]*)\])?') # Endpoint and options (opt.) 29 30 def __init__(self, stringbinding): 31 match = DCERPCStringBinding.parser.match(stringbinding) 32 self.__uuid = match.group(1) 33 self.__ps = match.group(2) 34 self.__na = match.group(3) 35 options = match.group(4) 36 if options: 37 options = options.split(',') 38 self.__endpoint = options[0] 39 try: 40 self.__endpoint.index('endpoint=') 41 self.__endpoint = self.__endpoint[len('endpoint='):] 42 except: 43 pass 44 self.__options = options[1:] 45 else: 46 self.__endpoint = '' 47 self.__options = [] 48 49 def get_uuid(self): 50 return self.__uuid 51 52 def get_protocol_sequence(self): 53 return self.__ps 54 55 def get_network_address(self): 56 return self.__na 57 58 def get_endpoint(self): 59 return self.__endpoint 60 61 def get_options(self): 62 return self.__options 63 64 def __str__(self): 65 return DCERPCStringBindingCompose(self.__uuid, self.__ps, self.__na, self.__endpoint, self.__options) 66 67def DCERPCStringBindingCompose(uuid=None, protocol_sequence='', network_address='', endpoint='', options=[]): 68 s = '' 69 if uuid: s += uuid + '@' 70 s += protocol_sequence + ':' 71 if network_address: s += network_address 72 if endpoint or options: 73 s += '[' + endpoint 74 if options: s += ',' + ','.join(options) 75 s += ']' 76 77 return s 78 79def DCERPCTransportFactory(stringbinding): 80 sb = DCERPCStringBinding(stringbinding) 81 82 na = sb.get_network_address() 83 ps = sb.get_protocol_sequence() 84 if 'ncadg_ip_udp' == ps: 85 port = sb.get_endpoint() 86 if port: 87 return UDPTransport(na, int(port)) 88 else: 89 return UDPTransport(na) 90 elif 'ncacn_ip_tcp' == ps: 91 port = sb.get_endpoint() 92 if port: 93 return TCPTransport(na, int(port)) 94 else: 95 return TCPTransport(na) 96 elif 'ncacn_http' == ps: 97 port = sb.get_endpoint() 98 if port: 99 return HTTPTransport(na, int(port)) 100 else: 101 return HTTPTransport(na) 102 elif 'ncacn_np' == ps: 103 named_pipe = sb.get_endpoint() 104 if named_pipe: 105 named_pipe = named_pipe[len(r'\pipe'):] 106 return SMBTransport(na, filename = named_pipe) 107 else: 108 return SMBTransport(na) 109 elif 'ncalocal' == ps: 110 named_pipe = sb.get_endpoint() 111 return LOCALTransport(filename = named_pipe) 112 else: 113 raise DCERPCException("Unknown protocol sequence.") 114 115 116class DCERPCTransport: 117 118 DCERPC_class = DCERPC_v5 119 120 def __init__(self, remoteName, dstport): 121 self.__remoteName = remoteName 122 self.__remoteHost = remoteName 123 self.__dstport = dstport 124 self._max_send_frag = None 125 self._max_recv_frag = None 126 self._domain = '' 127 self._lmhash = '' 128 self._nthash = '' 129 self.__connect_timeout = None 130 self._doKerberos = False 131 self._username = '' 132 self._password = '' 133 self._domain = '' 134 self._aesKey = None 135 self._TGT = None 136 self._TGS = None 137 self._kdcHost = None 138 self.set_credentials('','') 139 140 def connect(self): 141 raise RuntimeError, 'virtual function' 142 def send(self,data=0, forceWriteAndx = 0, forceRecv = 0): 143 raise RuntimeError, 'virtual function' 144 def recv(self, forceRecv = 0, count = 0): 145 raise RuntimeError, 'virtual function' 146 def disconnect(self): 147 raise RuntimeError, 'virtual function' 148 def get_socket(self): 149 raise RuntimeError, 'virtual function' 150 151 def get_connect_timeout(self): 152 return self.__connect_timeout 153 def set_connect_timeout(self, timeout): 154 self.__connect_timeout = timeout 155 156 def getRemoteName(self): 157 return self.__remoteName 158 159 def setRemoteName(self, remoteName): 160 """This method only makes sense before connection for most protocols.""" 161 self.__remoteName = remoteName 162 163 def getRemoteHost(self): 164 return self.__remoteHost 165 166 def setRemoteHost(self, remoteHost): 167 """This method only makes sense before connection for most protocols.""" 168 self.__remoteHost = remoteHost 169 170 def get_dport(self): 171 return self.__dstport 172 def set_dport(self, dport): 173 """This method only makes sense before connection for most protocols.""" 174 self.__dstport = dport 175 176 def get_addr(self): 177 return self.getRemoteHost(), self.get_dport() 178 def set_addr(self, addr): 179 """This method only makes sense before connection for most protocols.""" 180 self.setRemoteHost(addr[0]) 181 self.set_dport(addr[1]) 182 183 def set_kerberos(self, flag, kdcHost = None): 184 self._doKerberos = flag 185 self._kdcHost = kdcHost 186 187 def get_kerberos(self): 188 return self._doKerberos 189 190 def get_kdcHost(self): 191 return self._kdcHost 192 193 def set_max_fragment_size(self, send_fragment_size): 194 # -1 is default fragment size: 0 (don't fragment) 195 # 0 is don't fragment 196 # other values are max fragment size 197 if send_fragment_size == -1: 198 self.set_default_max_fragment_size() 199 else: 200 self._max_send_frag = send_fragment_size 201 202 def set_default_max_fragment_size(self): 203 # default is 0: don't fragment. 204 # subclasses may override this method 205 self._max_send_frag = 0 206 207 def get_credentials(self): 208 return ( 209 self._username, 210 self._password, 211 self._domain, 212 self._lmhash, 213 self._nthash, 214 self._aesKey, 215 self._TGT, 216 self._TGS) 217 218 def set_credentials(self, username, password, domain='', lmhash='', nthash='', aesKey='', TGT=None, TGS=None): 219 self._username = username 220 self._password = password 221 self._domain = domain 222 self._aesKey = aesKey 223 self._TGT = TGT 224 self._TGS = TGS 225 if lmhash != '' or nthash != '': 226 if len(lmhash) % 2: lmhash = '0%s' % lmhash 227 if len(nthash) % 2: nthash = '0%s' % nthash 228 try: # just in case they were converted already 229 self._lmhash = binascii.unhexlify(lmhash) 230 self._nthash = binascii.unhexlify(nthash) 231 except: 232 self._lmhash = lmhash 233 self._nthash = nthash 234 pass 235 236 def doesSupportNTLMv2(self): 237 # By default we'll be returning the library's default. Only on SMB Transports we might be able to know it beforehand 238 return ntlm.USE_NTLMv2 239 240 def get_dce_rpc(self): 241 return DCERPC_v5(self) 242 243class UDPTransport(DCERPCTransport): 244 "Implementation of ncadg_ip_udp protocol sequence" 245 246 DCERPC_class = DCERPC_v4 247 248 def __init__(self, remoteName, dstport = 135): 249 DCERPCTransport.__init__(self, remoteName, dstport) 250 self.__socket = 0 251 self.set_connect_timeout(30) 252 self.__recv_addr = '' 253 254 def connect(self): 255 try: 256 af, socktype, proto, canonname, sa = socket.getaddrinfo(self.getRemoteHost(), self.get_dport(), 0, socket.SOCK_DGRAM)[0] 257 self.__socket = socket.socket(af, socktype, proto) 258 self.__socket.settimeout(self.get_connect_timeout()) 259 except socket.error, msg: 260 self.__socket = None 261 raise DCERPCException("Could not connect: %s" % msg) 262 263 return 1 264 265 def disconnect(self): 266 try: 267 self.__socket.close() 268 except socket.error: 269 self.__socket = None 270 return 0 271 return 1 272 273 def send(self,data, forceWriteAndx = 0, forceRecv = 0): 274 self.__socket.sendto(data, (self.getRemoteHost(), self.get_dport())) 275 276 def recv(self, forceRecv = 0, count = 0): 277 buffer, self.__recv_addr = self.__socket.recvfrom(8192) 278 return buffer 279 280 def get_recv_addr(self): 281 return self.__recv_addr 282 283 def get_socket(self): 284 return self.__socket 285 286class TCPTransport(DCERPCTransport): 287 """Implementation of ncacn_ip_tcp protocol sequence""" 288 289 def __init__(self, remoteName, dstport = 135): 290 DCERPCTransport.__init__(self, remoteName, dstport) 291 self.__socket = 0 292 self.set_connect_timeout(30) 293 294 def connect(self): 295 af, socktype, proto, canonname, sa = socket.getaddrinfo(self.getRemoteHost(), self.get_dport(), 0, socket.SOCK_STREAM)[0] 296 self.__socket = socket.socket(af, socktype, proto) 297 try: 298 self.__socket.settimeout(self.get_connect_timeout()) 299 self.__socket.connect(sa) 300 except socket.error, msg: 301 self.__socket.close() 302 raise DCERPCException("Could not connect: %s" % msg) 303 return 1 304 305 def disconnect(self): 306 try: 307 self.__socket.close() 308 except socket.error, msg: 309 self.__socket = None 310 return 0 311 return 1 312 313 def send(self,data, forceWriteAndx = 0, forceRecv = 0): 314 if self._max_send_frag: 315 offset = 0 316 while 1: 317 toSend = data[offset:offset+self._max_send_frag] 318 if not toSend: 319 break 320 self.__socket.send(toSend) 321 offset += len(toSend) 322 else: 323 self.__socket.send(data) 324 325 def recv(self, forceRecv = 0, count = 0): 326 if count: 327 buffer = '' 328 while len(buffer) < count: 329 buffer += self.__socket.recv(count-len(buffer)) 330 else: 331 buffer = self.__socket.recv(8192) 332 return buffer 333 334 def get_socket(self): 335 return self.__socket 336 337class HTTPTransport(TCPTransport): 338 """Implementation of ncacn_http protocol sequence""" 339 340 def connect(self): 341 TCPTransport.connect(self) 342 343 self.get_socket().send('RPC_CONNECT ' + self.getRemoteHost() + ':593 HTTP/1.0\r\n\r\n') 344 data = self.get_socket().recv(8192) 345 if data[10:13] != '200': 346 raise DCERPCException("Service not supported.") 347 348class SMBTransport(DCERPCTransport): 349 """Implementation of ncacn_np protocol sequence""" 350 351 def __init__(self, remoteName, dstport=445, filename='', username='', password='', domain='', lmhash='', nthash='', 352 aesKey='', TGT=None, TGS=None, remote_host='', smb_connection=0, doKerberos=False, kdcHost=None): 353 DCERPCTransport.__init__(self, remoteName, dstport) 354 self.__socket = None 355 self.__tid = 0 356 self.__filename = filename 357 self.__handle = 0 358 self.__pending_recv = 0 359 self.set_credentials(username, password, domain, lmhash, nthash, aesKey, TGT, TGS) 360 self._doKerberos = doKerberos 361 self._kdcHost = kdcHost 362 363 if remote_host != '': 364 self.setRemoteHost(remote_host) 365 366 if smb_connection == 0: 367 self.__existing_smb = False 368 else: 369 self.__existing_smb = True 370 self.set_credentials(*smb_connection.getCredentials()) 371 372 self.__prefDialect = None 373 self.__smb_connection = smb_connection 374 375 def preferred_dialect(self, dialect): 376 self.__prefDialect = dialect 377 378 def setup_smb_connection(self): 379 if not self.__smb_connection: 380 self.__smb_connection = SMBConnection(self.getRemoteName(), self.getRemoteHost(), sess_port=self.get_dport(), 381 preferredDialect=self.__prefDialect) 382 383 def connect(self): 384 # Check if we have a smb connection already setup 385 if self.__smb_connection == 0: 386 self.setup_smb_connection() 387 if self._doKerberos is False: 388 self.__smb_connection.login(self._username, self._password, self._domain, self._lmhash, self._nthash) 389 else: 390 self.__smb_connection.kerberosLogin(self._username, self._password, self._domain, self._lmhash, 391 self._nthash, self._aesKey, kdcHost=self._kdcHost, TGT=self._TGT, 392 TGS=self._TGS) 393 self.__tid = self.__smb_connection.connectTree('IPC$') 394 self.__handle = self.__smb_connection.openFile(self.__tid, self.__filename) 395 self.__socket = self.__smb_connection.getSMBServer().get_socket() 396 return 1 397 398 def disconnect(self): 399 self.__smb_connection.disconnectTree(self.__tid) 400 # If we created the SMB connection, we close it, otherwise 401 # that's up for the caller 402 if self.__existing_smb is False: 403 self.__smb_connection.logoff() 404 self.__smb_connection.close() 405 self.__smb_connection = 0 406 407 def send(self,data, forceWriteAndx = 0, forceRecv = 0): 408 if self._max_send_frag: 409 offset = 0 410 while 1: 411 toSend = data[offset:offset+self._max_send_frag] 412 if not toSend: 413 break 414 self.__smb_connection.writeFile(self.__tid, self.__handle, toSend, offset = offset) 415 offset += len(toSend) 416 else: 417 self.__smb_connection.writeFile(self.__tid, self.__handle, data) 418 if forceRecv: 419 self.__pending_recv += 1 420 421 def recv(self, forceRecv = 0, count = 0 ): 422 if self._max_send_frag or self.__pending_recv: 423 # _max_send_frag is checked because it's the same condition we checked 424 # to decide whether to use write_andx() or send_trans() in send() above. 425 if self.__pending_recv: 426 self.__pending_recv -= 1 427 return self.__smb_connection.readFile(self.__tid, self.__handle, bytesToRead = self._max_recv_frag) 428 else: 429 return self.__smb_connection.readFile(self.__tid, self.__handle) 430 431 def get_smb_connection(self): 432 return self.__smb_connection 433 434 def set_smb_connection(self, smb_connection): 435 self.__smb_connection = smb_connection 436 self.set_credentials(*smb_connection.getCredentials()) 437 self.__existing_smb = True 438 439 def get_smb_server(self): 440 # Raw Access to the SMBServer (whatever type it is) 441 return self.__smb_connection.getSMBServer() 442 443 def get_socket(self): 444 return self.__socket 445 446 def doesSupportNTLMv2(self): 447 return self.__smb_connection.doesSupportNTLMv2() 448 449class LOCALTransport(DCERPCTransport): 450 """ 451 Implementation of ncalocal protocol sequence, not the same 452 as ncalrpc (I'm not doing LPC just opening the local pipe) 453 """ 454 455 def __init__(self, filename = ''): 456 DCERPCTransport.__init__(self, '', 0) 457 self.__filename = filename 458 self.__handle = 0 459 460 def connect(self): 461 if self.__filename.upper().find('PIPE') < 0: 462 self.__filename = '\\PIPE\\%s' % self.__filename 463 self.__handle = os.open('\\\\.\\%s' % self.__filename, os.O_RDWR|os.O_BINARY) 464 return 1 465 466 def disconnect(self): 467 os.close(self.__handle) 468 469 def send(self,data, forceWriteAndx = 0, forceRecv = 0): 470 os.write(self.__handle, data) 471 472 def recv(self, forceRecv = 0, count = 0 ): 473 data = os.read(self.__handle, 65535) 474 return data 475