예제 #1
0
 def transferResponse(self):
     data = self.relaySocket.recv(self.packetSize)
     headerSize = data.find(EOL + EOL)
     headers = self.getHeaders(data)
     try:
         bodySize = int(headers['content-length'])
         readSize = len(data)
         # Make sure we send the entire response, but don't keep it in memory
         self.socksSocket.send(data)
         while readSize < bodySize + headerSize + 4:
             data = self.relaySocket.recv(self.packetSize)
             readSize += len(data)
             self.socksSocket.send(data)
     except KeyError:
         try:
             if headers['transfer-encoding'] == 'chunked':
                 # Chunked transfer-encoding, bah
                 LOG.debug('Server sent chunked encoding - transferring')
                 self.transferChunked(data, headers)
             else:
                 # No body in the response, send as-is
                 self.socksSocket.send(data)
         except KeyError:
             # No body in the response, send as-is
             self.socksSocket.send(data)
예제 #2
0
    def initConnection(self):
        self.connect()
        #This is copied from tds.py
        resp = self.preLogin()
        if resp['Encryption'] == TDS_ENCRYPT_REQ or resp[
                'Encryption'] == TDS_ENCRYPT_OFF:
            LOG.debug("Encryption required, switching to TLS")

            # Switching to TLS now
            ctx = SSL.Context(SSL.TLSv1_METHOD)
            ctx.set_cipher_list('RC4, AES256')
            tls = SSL.Connection(ctx, None)
            tls.set_connect_state()
            while True:
                try:
                    tls.do_handshake()
                except SSL.WantReadError:
                    data = tls.bio_read(4096)
                    self.sendTDS(TDS_PRE_LOGIN, data, 0)
                    tds = self.recvTDS()
                    tls.bio_write(tds['Data'])
                else:
                    break

            # SSL and TLS limitation: Secure Socket Layer (SSL) and its replacement,
            # Transport Layer Security(TLS), limit data fragments to 16k in size.
            self.packetSize = 16 * 1024 - 1
            self.tlsSocket = tls
        self.resp = resp
        return True
예제 #3
0
    def toTGS(self, newSPN=None):
        tgs_rep = TGS_REP()
        tgs_rep['pvno'] = 5
        tgs_rep['msg-type'] = int(
            constants.ApplicationTagNumbers.TGS_REP.value)
        tgs_rep['crealm'] = self['server'].realm['data']

        # Fake EncryptedData
        tgs_rep['enc-part'] = noValue
        tgs_rep['enc-part']['etype'] = 1
        tgs_rep['enc-part']['cipher'] = ''
        seq_set(tgs_rep, 'cname',
                self['client'].toPrincipal().components_to_asn1)
        ticket = types.Ticket()
        ticket.from_asn1(self.ticket['data'])
        if newSPN is not None:
            if newSPN.upper() != str(ticket.service_principal).upper():
                LOG.debug(
                    'Changing sname from %s to %s and hoping for the best' %
                    (ticket.service_principal, newSPN))
                ticket.service_principal = types.Principal(
                    newSPN, type=int(ticket.service_principal.type))
        seq_set(tgs_rep, 'ticket', ticket.to_asn1)

        cipher = crypto._enctype_table[self['key']['keytype']]()

        tgs = dict()
        tgs['KDC_REP'] = encoder.encode(tgs_rep)
        tgs['cipher'] = cipher
        tgs['sessionKey'] = crypto.Key(cipher.enctype,
                                       str(self['key']['keyvalue']))
        return tgs
예제 #4
0
def hBaseRegQueryValue(dce, hKey, lpValueName, dataLen=512):
    request = BaseRegQueryValue()
    request['hKey'] = hKey
    request['lpValueName'] = checkNullString(lpValueName)
    retries = 1

    # We need to be aware the size might not be enough, so let's catch ERROR_MORE_DATA exception
    while True:
        try:
            request['lpData'] = ' ' * dataLen
            request['lpcbData'] = dataLen
            request['lpcbLen'] = dataLen
            resp = dce.request(request)
        except DCERPCSessionError, e:
            if retries > 1:
                LOG.debug(
                    'Too many retries when calling hBaseRegQueryValue, aborting'
                )
                raise
            if e.get_error_code() == system_errors.ERROR_MORE_DATA:
                # We need to adjust the size
                dataLen = e.get_packet()['lpcbData']
                continue
            else:
                raise
        else:
            break
예제 #5
0
    def __init__(self, url, baseDN='', dstIp=None):
        """
        LDAPConnection class

        :param string url:
        :param string baseDN:
        :param string dstIp:

        :return: a LDAP instance, if not raises a LDAPSessionError exception
        """
        self._SSL = False
        self._dstPort = 0
        self._dstHost = 0
        self._socket = None
        self._baseDN = baseDN
        self._messageId = 1
        self._dstIp = dstIp

        if url.startswith('ldap://'):
            self._dstPort = 389
            self._SSL = False
            self._dstHost = url[7:]
        elif url.startswith('ldaps://'):
            self._dstPort = 636
            self._SSL = True
            self._dstHost = url[8:]
        elif url.startswith('gc://'):
            self._dstPort = 3268
            self._SSL = False
            self._dstHost = url[5:]
        else:
            raise LDAPSessionError(errorString="Unknown URL prefix: '%s'" %
                                   url)

        # Try to connect
        if self._dstIp is not None:
            targetHost = self._dstIp
        else:
            targetHost = self._dstHost

        LOG.debug('Connecting to %s, port %d, SSL %s' %
                  (targetHost, self._dstPort, self._SSL))
        try:
            af, socktype, proto, _, sa = socket.getaddrinfo(
                targetHost, self._dstPort, 0, socket.SOCK_STREAM)[0]
            self._socket = socket.socket(af, socktype, proto)
        except socket.error as e:
            raise socket.error('Connection error (%s:%d)' % (targetHost, 88),
                               e)

        if self._SSL is False:
            self._socket.connect(sa)
        else:
            # Switching to TLS now
            ctx = SSL.Context(SSL.TLSv1_METHOD)
            # ctx.set_cipher_list('RC4')
            self._socket = SSL.Connection(ctx, self._socket)
            self._socket.connect(sa)
            self._socket.do_handshake()
예제 #6
0
 def skipAuthentication(self):
     LOG.debug('Wrapping client connection in TLS/SSL')
     self.wrapClientConnection()
     if not HTTPSocksRelay.skipAuthentication(self):
         # Shut down TLS connection
         self.socksSocket.shutdown()
         return False
     return True
예제 #7
0
 def handle_one_request(self):
     try:
         SimpleHTTPServer.SimpleHTTPRequestHandler.handle_one_request(self)
     except KeyboardInterrupt:
         raise
     except Exception, e:
         LOG.error('Exception in HTTP request handler: %s' % e)
         LOG.debug(traceback.format_exc())
예제 #8
0
 def initConnection(self):
     self.session = imaplib.IMAP4(self.targetHost, self.targetPort)
     self.authTag = self.session._new_tag()
     LOG.debug('IMAP CAPABILITIES: %s' % str(self.session.capabilities))
     if 'AUTH=NTLM' not in self.session.capabilities:
         LOG.error('IMAP server does not support NTLM authentication!')
         return False
     return True
예제 #9
0
 def skipAuthentication(self):
     LOG.debug('Wrapping IMAP client connection in TLS/SSL')
     self.wrapClientConnection()
     try:
         if not IMAPSocksRelay.skipAuthentication(self):
             # Shut down TLS connection
             self.socksSocket.shutdown()
             return False
     except Exception, e:
         LOG.debug('IMAPS: %s' % str(e))
         return False
예제 #10
0
    def __getitem__(self, key):
        if key == 'Data':
            try:
                return ''.join([chr(i) for i in self.fields[key]])
            except ValueError:
                # We might have Unicode chars in here, let's use unichr instead
                LOG.debug('ValueError exception on %s' % self.fields[key])
                LOG.debug('Switching to unichr()')
                return ''.join([unichr(i) for i in self.fields[key]])

        else:
            return NDR.__getitem__(self, key)
예제 #11
0
 def getPage(self, pageNum):
     LOG.debug("Trying to fetch page %d (0x%x)" %
               (pageNum, (pageNum + 1) * self.__pageSize))
     self.__DB.seek((pageNum + 1) * self.__pageSize, 0)
     data = self.__DB.read(self.__pageSize)
     while len(data) < self.__pageSize:
         remaining = self.__pageSize - len(data)
         data += self.__DB.read(remaining)
     # Special case for the first page
     if pageNum <= 0:
         return data
     else:
         return ESENT_PAGE(self.__DBHeader, data)
예제 #12
0
    def getCredential(self, server, anySPN=True):
        for c in self.credentials:
            if c['server'].prettyPrint().upper() == server.upper() or c['server'].prettyPrint().upper().split('@')[0] == server.upper() \
                    or c['server'].prettyPrint().upper().split('@')[0] == server.upper().split('@')[0]:
                LOG.debug('Returning cached credential for %s' %
                          c['server'].prettyPrint().upper())
                return c
        LOG.debug('SPN %s not found in cache' % server.upper())
        if anySPN is True:
            LOG.debug('AnySPN is True, looking for another suitable SPN')
            for c in self.credentials:
                # Let's search for any TGT/TGS that matches the server w/o the SPN's service type/port, returns
                # the first one
                if c['server'].prettyPrint().find('/') >= 0:
                    # Let's take the port out for comparison
                    cachedSPN = '%s@%s' % (c['server'].prettyPrint().upper(
                    ).split('/')[1].split('@')[0].split(':')[0],
                                           c['server'].prettyPrint().upper(
                                           ).split('/')[1].split('@')[1])
                    searchSPN = '%s@%s' % (
                        server.upper().split('/')[1].split('@')[0].split(':')
                        [0], server.upper().split('/')[1].split('@')[1])
                    if cachedSPN == searchSPN:
                        LOG.debug('Returning cached credential for %s' %
                                  c['server'].prettyPrint().upper())
                        return c

        return None
예제 #13
0
 def __getBlock(self, offset):
     self.fd.seek(4096 + offset, 0)
     sizeBytes = self.fd.read(4)
     data = sizeBytes + self.fd.read(unpack('<l', sizeBytes)[0] * -1 - 4)
     if len(data) == 0:
         return None
     else:
         block = REG_HBINBLOCK(data)
         if StructMappings.has_key(block['Data'][:2]):
             return StructMappings[block['Data'][:2]](block['Data'])
         else:
             LOG.debug("Unknown type 0x%s" % block['Data'][:2])
             return block
         return None
예제 #14
0
def sendReceive(data, host, kdcHost):
    if kdcHost is None:
        targetHost = host
    else:
        targetHost = kdcHost

    messageLen = struct.pack('!i', len(data))

    LOG.debug('Trying to connect to KDC at %s' % targetHost)
    try:
        af, socktype, proto, canonname, sa = socket.getaddrinfo(
            targetHost, 88, 0, socket.SOCK_STREAM)[0]
        s = socket.socket(af, socktype, proto)
        s.connect(sa)
    except socket.error, e:
        raise socket.error("Connection error (%s:%s)" % (targetHost, 88), e)
예제 #15
0
def generateImpacketCert(certname='/tmp/impacket.crt'):
    # Create a private key
    pkey = crypto.PKey()
    pkey.generate_key(crypto.TYPE_RSA, 2048)

    # Create the certificate
    cert = crypto.X509()
    cert.gmtime_adj_notBefore(0)
    # Valid for 5 years
    cert.gmtime_adj_notAfter(60 * 60 * 24 * 365 * 5)
    subj = cert.get_subject()
    subj.CN = 'impacket'
    cert.set_pubkey(pkey)
    cert.sign(pkey, "sha256")
    # We write both from the same file
    with open(certname, 'w') as certfile:
        certfile.write(
            crypto.dump_privatekey(crypto.FILETYPE_PEM, pkey).decode('utf-8'))
        certfile.write(
            crypto.dump_certificate(crypto.FILETYPE_PEM, cert).decode('utf-8'))
    LOG.debug('Wrote certificate to %s' % certname)
예제 #16
0
    def tunnelConnection(self):
        keyword = ''
        tag = ''
        while True:
            try:
                data = self.socksSocket.recv(self.packetSize)
            except Exception, e:
                # Socks socket (client) closed connection or something else. Not fatal for killing the existing relay
                print keyword, tag
                LOG.debug('IMAP: sockSocket recv(): %s' % (str(e)))
                break
            # If this returns with an empty string, it means the socket was closed
            if data == '':
                break
            # Set the new keyword, unless it is false, then break out of the function
            result = self.processTunnelData(keyword, tag, data)

            if result is False:
                break
            # If its not false, it's a tuple with the keyword and tag
            keyword, tag = result
예제 #17
0
 def tunnelConnection(self):
     doneIndicator = EOL + '.' + EOL
     while True:
         data = self.socksSocket.recv(self.packetSize)
         # If this returns with an empty string, it means the socket was closed
         if data == '':
             return
         info = data.strip().split(' ')
         # See if a QUIT command was sent, in which case we want to close
         # the connection to the client but keep the relayed connection alive
         if info[0].upper() == 'QUIT':
             LOG.debug(
                 'Client sent QUIT command, closing socks connection to client'
             )
             self.socksSocket.send(
                 '221 2.0.0 Service closing transmission channel%s' % EOL)
             return
         self.relaySocket.send(data)
         data = self.relaySocket.recv(self.packetSize)
         self.socksSocket.send(data)
         if info[0].upper() == 'DATA':
             LOG.debug('SMTP Socks entering DATA transfer mode')
             # DATA transfer, forward to the server till done
             while data[-5:] != doneIndicator:
                 prevdata = data
                 data = self.socksSocket.recv(self.packetSize)
                 self.relaySocket.send(data)
                 if len(data) < 5:
                     # This can happen, the .CRLF will be in a packet after the first CRLF
                     # we stitch them back together for analysis
                     data = prevdata + data
             LOG.debug('SMTP Socks DATA transfer mode finished')
             # DATA done, forward server reply
             data = self.relaySocket.recv(self.packetSize)
             self.socksSocket.send(data)
예제 #18
0
def activeConnectionsWatcher(server):
    while True:
        # This call blocks until there is data, so it doesn't loop endlessly
        target, port, scheme, userName, client, data = activeConnections.get()
        # ToDo: Careful. Dicts are not thread safe right?
        if server.activeRelays.has_key(target) is not True:
            server.activeRelays[target] = {}
        if server.activeRelays[target].has_key(port) is not True:
            server.activeRelays[target][port] = {}

        if server.activeRelays[target][port].has_key(userName) is not True:
            LOG.info('SOCKS: Adding %s@%s(%s) to active SOCKS connection. Enjoy' % (userName, target, port))
            server.activeRelays[target][port][userName] = {}
            # This is the protocolClient. Needed because we need to access the killConnection from time to time.
            # Inside this instance, you have the session attribute pointing to the relayed session.
            server.activeRelays[target][port][userName]['protocolClient'] = client
            server.activeRelays[target][port][userName]['inUse'] = False
            server.activeRelays[target][port][userName]['data'] = data
            # Just for the CHALLENGE data, we're storing this general
            server.activeRelays[target][port]['data'] = data
            # Let's store the protocol scheme, needed be used later when trying to find the right socks relay server to use
            server.activeRelays[target][port]['scheme'] = scheme

            # Default values in case somebody asks while we're gettting the data
            server.activeRelays[target][port][userName]['isAdmin'] = 'N/A'
            # Do we have admin access in this connection?
            try:
                LOG.debug("Checking admin status for user %s" % str(userName))
                isAdmin = client.isAdmin()
                server.activeRelays[target][port][userName]['isAdmin'] = isAdmin
            except Exception as e:
                # Method not implemented
                server.activeRelays[target][port][userName]['isAdmin'] = 'N/A'
                pass
            LOG.debug("isAdmin returned: %s" % server.activeRelays[target][port][userName]['isAdmin'])
        else:
            LOG.info('Relay connection for %s at %s(%d) already exists. Discarding' % (userName, target, port))
            client.killConnection()
예제 #19
0
    def transferChunked(self, data, headers):
        headerSize = data.find(EOL + EOL)

        self.socksSocket.send(data[:headerSize + 4])

        body = data[headerSize + 4:]
        # Size of the chunk
        datasize = int(body[:body.find(EOL)], 16)
        while datasize > 0:
            # Size of the total body
            bodySize = body.find(EOL) + 2 + datasize + 2
            readSize = len(body)
            # Make sure we send the entire response, but don't keep it in memory
            self.socksSocket.send(body)
            while readSize < bodySize:
                maxReadSize = bodySize - readSize
                body = self.relaySocket.recv(min(self.packetSize, maxReadSize))
                readSize += len(body)
                self.socksSocket.send(body)
            body = self.relaySocket.recv(self.packetSize)
            datasize = int(body[:body.find(EOL)], 16)
        LOG.debug('Last chunk received - exiting chunked transfer')
        self.socksSocket.send(body)
예제 #20
0
 def __init__(self,request, client_address, server):
     self.server = server
     self.protocol_version = 'HTTP/1.1'
     self.challengeMessage = None
     self.target = None
     self.client = None
     self.machineAccount = None
     self.machineHashes = None
     self.domainIp = None
     self.authUser = None
     self.wpad = 'function FindProxyForURL(url, host){if ((host == "localhost") || shExpMatch(host, "localhost.*") ||' \
                 '(host == "127.0.0.1")) return "DIRECT"; if (dnsDomainIs(host, "%s")) return "DIRECT"; ' \
                 'return "PROXY %s:80; DIRECT";} '
     if self.server.config.mode != 'REDIRECT':
         if self.server.config.target is None:
             # Reflection mode, defaults to SMB at the target, for now
             self.server.config.target = TargetsProcessor(singleTarget='SMB://%s:445/' % client_address[0])
         self.target = self.server.config.target.getTarget(self.server.config.randomtargets)
         LOG.info("HTTPD: Received connection from %s, attacking target %s://%s" % (client_address[0] ,self.target.scheme, self.target.netloc))
     try:
         SimpleHTTPServer.SimpleHTTPRequestHandler.__init__(self,request, client_address, server)
     except Exception, e:
         LOG.error(str(e))
         LOG.debug(traceback.format_exc())
예제 #21
0
def hBaseRegEnumValue(dce, hKey, dwIndex, dataLen=256):
    request = BaseRegEnumValue()
    request['hKey'] = hKey
    request['dwIndex'] = dwIndex
    retries = 1

    # We need to be aware the size might not be enough, so let's catch ERROR_MORE_DATA exception
    while True:
        try:
            # Only the maximum length field of the lpValueNameIn is used to determine the buffer length to be allocated
            # by the service. Specify a string with a zero length but maximum length set to the largest buffer size
            # needed to hold the value names.
            request.fields['lpValueNameIn'].fields[
                'MaximumLength'] = dataLen * 2
            request.fields['lpValueNameIn'].fields['Data'].fields[
                'Data'].fields['MaximumCount'] = dataLen

            request['lpData'] = ' ' * dataLen
            request['lpcbData'] = dataLen
            request['lpcbLen'] = dataLen
            resp = dce.request(request)
        except DCERPCSessionError, e:
            if retries > 1:
                LOG.debug(
                    'Too many retries when calling hBaseRegEnumValue, aborting'
                )
                raise
            if e.get_error_code() == system_errors.ERROR_MORE_DATA:
                # We need to adjust the size
                retries += 1
                dataLen = e.get_packet()['lpcbData']
                continue
            else:
                raise
        else:
            break
예제 #22
0
def keepAliveTimer(server):
    LOG.debug('KeepAlive Timer reached. Updating connections')

    for target in server.activeRelays.keys():
        for port in server.activeRelays[target].keys():
            # Now cycle through the users
            for user in server.activeRelays[target][port].keys():
                if user != 'data' and user != 'scheme':
                    # Let's call the keepAlive method for the handler to keep the connection alive
                    if server.activeRelays[target][port][user]['inUse'] is False:
                        LOG.debug('Calling keepAlive() for %s@%s:%s' % (user, target, port))
                        try:
                            server.activeRelays[target][port][user]['protocolClient'].keepAlive()
                        except Exception, e:
                            LOG.debug('SOCKS: %s' % str(e))
                            if str(e).find('Broken pipe') >= 0 or str(e).find('reset by peer') >=0 or \
                                            str(e).find('Invalid argument') >= 0 or str(e).find('Server not connected') >=0:
                                # Connection died, taking out of the active list
                                del (server.activeRelays[target][port][user])
                                if len(server.activeRelays[target][port].keys()) == 1:
                                    del (server.activeRelays[target][port])
                                LOG.debug('Removing active relay for %s@%s:%s' % (user, target, port))
                    else:
                        LOG.debug('Skipping %s@%s:%s since it\'s being used at the moment' % (user, target, port))
예제 #23
0
 def mountDB(self):
     LOG.debug("Mounting DB...")
     if self.__isRemote is True:
         self.__DB = self.__fileName
         self.__DB.open()
     else:
         self.__DB = open(self.__fileName, "rb")
     mainHeader = self.getPage(-1)
     self.__DBHeader = ESENT_DB_HEADER(mainHeader)
     self.__pageSize = self.__DBHeader['PageSize']
     self.__DB.seek(0, 2)
     self.__totalPages = (self.__DB.tell() / self.__pageSize) - 2
     LOG.debug("Database Version:0x%x, Revision:0x%x" %
               (self.__DBHeader['Version'],
                self.__DBHeader['FileFormatRevision']))
     LOG.debug("Page Size: %d" % self.__pageSize)
     LOG.debug("Total Pages in file: %d" % self.__totalPages)
     self.parseCatalog(CATALOG_PAGE_NUMBER)
예제 #24
0
    def addComputer(self, parent, domainDumper):
        """
        Add a new computer. Parent is preferably CN=computers,DC=Domain,DC=local, but can
        also be an OU or other container where we have write privileges
        """
        global alreadyAddedComputer
        if alreadyAddedComputer:
            LOG.error('New computer already added. Refusing to add another')
            return

        # Random password
        newPassword = ''.join(
            random.choice(string.ascii_letters + string.digits +
                          string.punctuation) for _ in range(15))

        # Get the domain we are in
        domaindn = domainDumper.root
        domain = re.sub(',DC=',
                        '.',
                        domaindn[domaindn.find('DC='):],
                        flags=re.I)[3:]

        # Random computername
        newComputer = (
            ''.join(random.choice(string.ascii_letters)
                    for _ in range(8)) + '$').upper()
        computerHostname = newComputer[:-1]
        newComputerDn = ('CN=%s,%s' %
                         (computerHostname, parent)).encode('utf-8')

        # Default computer SPNs
        spns = [
            'HOST/%s' % computerHostname,
            'HOST/%s.%s' % (computerHostname, domain),
            'RestrictedKrbHost/%s' % computerHostname,
            'RestrictedKrbHost/%s.%s' % (computerHostname, domain),
        ]
        ucd = {
            'dnsHostName': '%s.%s' % (computerHostname, domain),
            'userAccountControl': 4096,
            'servicePrincipalName': spns,
            'sAMAccountName': newComputer,
            'unicodePwd': '"{}"'.format(newPassword).encode('utf-16-le')
        }
        LOG.debug('New computer info %s', ucd)
        LOG.info('Attempting to create computer in: %s', parent)
        res = self.client.add(
            newComputerDn.decode('utf-8'),
            ['top', 'person', 'organizationalPerson', 'user', 'computer'], ucd)
        if not res:
            # Adding computers requires LDAPS
            if self.client.result[
                    'result'] == RESULT_UNWILLING_TO_PERFORM and not self.client.server.ssl:
                LOG.error(
                    'Failed to add a new computer. The server denied the operation. Try relaying to LDAP with TLS enabled (ldaps) or escalating an existing account.'
                )
            else:
                LOG.error('Failed to add a new computer: %s' %
                          str(self.client.result))
            return False
        else:
            LOG.info(
                'Adding new computer with username: %s and password: %s result: OK'
                % (newComputer, newPassword))
            alreadyAddedComputer = True
            # Return the SAM name
            return newComputer
예제 #25
0
    def skipAuthentication(self):
        self.socksSocket.sendall('* OK The Microsoft Exchange IMAP4 service is ready.'+EOL)

        # Next should be the client requesting CAPABILITIES
        tag, cmd = self.recvPacketClient()
        if cmd.upper() == 'CAPABILITY':
            clientcapabilities = list(self.getServerCapabilities())
            # Don't offer these AUTH options so the client won't use them
            blacklist = ['AUTH=GSSAPI', 'AUTH=NTLM', 'LOGINDISABLED']
            for cap in blacklist:
                if cap in clientcapabilities:
                    clientcapabilities.remove(cap)

            # Offer PLAIN auth for specifying the username
            if 'AUTH=PLAIN' not in clientcapabilities:
                clientcapabilities.append('AUTH=PLAIN')
            # Offer LOGIN for specifying the username
            if 'LOGIN' not in clientcapabilities:
                clientcapabilities.append('LOGIN')

            LOG.debug('IMAP: Sending mirrored capabilities from server: %s' % ' '.join(clientcapabilities))
            self.socksSocket.sendall('* CAPABILITY %s%s%s OK CAPABILITY completed.%s' % (' '.join(clientcapabilities), EOL, tag, EOL))
        else:
            LOG.error('IMAP: Socks plugin expected CAPABILITY command, but got: %s' % cmd)
            return False
        # next
        tag, cmd = self.recvPacketClient()
        args = cmd.split(' ')
        if cmd.upper() == 'AUTHENTICATE PLAIN':
            # Send continuation command
            self.socksSocket.sendall('+'+EOL)
            # Client will now send their AUTH
            data = self.socksSocket.recv(self.packetSize)
            # This contains base64(\x00username\x00password), decode and split
            creds = base64.b64decode(data.strip())
            self.username = creds.split('\x00')[1].upper()
        elif args[0].upper() == 'LOGIN':
            # Simple login
            self.username = args[1].upper()
        else:
            LOG.error('IMAP: Socks plugin expected LOGIN or AUTHENTICATE PLAIN command, but got: %s' % cmd)
            return False

        # Check if we have a connection for the user
        if self.activeRelays.has_key(self.username):
            # Check the connection is not inUse
            if self.activeRelays[self.username]['inUse'] is True:
                LOG.error('IMAP: Connection for %s@%s(%s) is being used at the moment!' % (
                    self.username, self.targetHost, self.targetPort))
                return False
            else:
                LOG.info('IMAP: Proxying client session for %s@%s(%s)' % (
                    self.username, self.targetHost, self.targetPort))
                self.session = self.activeRelays[self.username]['protocolClient'].session
        else:
            LOG.error('IMAP: No session for %s@%s(%s) available' % (
                self.username, self.targetHost, self.targetPort))
            return False

        # We arrived here, that means all is OK
        self.socksSocket.sendall('%s OK %s completed.%s' % (tag, args[0].upper(), EOL))
        self.relaySocket = self.session.sock
        self.relaySocketFile = self.session.file
        return True
예제 #26
0
    def processTunnelData(self, keyword, tag, data):
        # Pass the request to the server, store the tag unless the last command
        # was a continuation. In the case of the continuation we still check if
        # there were commands issued after
        analyze = data.split(EOL)[:-1]
        if keyword == '+':
            # We do send the continuation to the server
            # but we don't analyze it
            self.relaySocket.sendall(analyze.pop(0)+EOL)
            keyword = ''

        for line in analyze:
            info = line.split(' ')
            tag = info[0]
            # See if a LOGOUT command was sent, in which case we want to close
            # the connection to the client but keep the relayed connection alive
            # also handle APPEND commands
            try:
                if info[1].upper() == 'IDLE':
                    self.idleState = True
                elif info[1].upper() == 'DONE':
                    self.idleState = False
                elif info[1].upper() == 'CLOSE':
                    self.shouldClose = False
                elif info[1].upper() == 'LOGOUT':
                    self.socksSocket.sendall('%s OK LOGOUT completed.%s' % (tag, EOL))
                    return False
                elif info[1].upper() == 'APPEND':
                    LOG.debug('IMAP socks APPEND command detected, forwarding email data')
                    # APPEND command sent, forward all the data, no further commands here
                    self.relaySocket.sendall(data)
                    sent = len(data) - len(line) + len(EOL)

                    # https://tools.ietf.org/html/rfc7888
                    literal = info[4][1:-1]
                    if literal[-1] == '+':
                        literalPlus = True
                        totalSize = int(literal[:-1])
                    else:
                        literalPlus = False
                        totalSize = int(literal)

                    while sent < totalSize:
                        data = self.socksSocket.recv(self.packetSize)
                        self.relaySocket.sendall(data)
                        sent += len(data)
                        LOG.debug('Forwarded %d bytes' % sent)

                    if literalPlus:
                        data = self.socksSocket.recv(self.packetSize)
                        self.relaySocket.sendall(data)

                    LOG.debug('IMAP socks APPEND command complete')
                    # break out of the analysis loop
                    break
            except IndexError:
                pass
            self.relaySocket.sendall(line+EOL)

        # Send the response back to the client, until the command is complete
        # or the server requests more data
        while keyword != tag and keyword != '+':
            try:
                data = self.relaySocketFile.readline()
            except Exception, e:
                # This didn't break the connection to the server, don't make it fatal
                LOG.debug("IMAP relaySocketFile: %s" % str(e))
                return False
            keyword = data.split(' ', 2)[0]
            try:
                self.socksSocket.sendall(data)
            except Exception, e:
                LOG.debug("IMAP socksSocket: %s" % str(e))
                return False
예제 #27
0
    def kerberosLogin(self,
                      user,
                      password,
                      domain='',
                      lmhash='',
                      nthash='',
                      aesKey='',
                      kdcHost=None,
                      TGT=None,
                      TGS=None,
                      useCache=True):
        """
        logins into the target system explicitly using Kerberos. Hashes are used if RC4_HMAC is supported.

        :param string user: username
        :param string password: password for the user
        :param string domain: domain where the account is valid for (required)
        :param string lmhash: LMHASH used to authenticate using hashes (password is not used)
        :param string nthash: NTHASH used to authenticate using hashes (password is not used)
        :param string aesKey: aes256-cts-hmac-sha1-96 or aes128-cts-hmac-sha1-96 used for Kerberos authentication
        :param string kdcHost: hostname or IP Address for the KDC. If None, the domain will be used (it needs to resolve tho)
        :param struct TGT: If there's a TGT available, send the structure here and it will be used
        :param struct TGS: same for TGS. See smb3.py for the format
        :param bool useCache: whether or not we should use the ccache for credentials lookup. If TGT or TGS are specified this is False

        :return: None, raises a Session Error if error.
        """
        import os
        from nebulousAD.modimpacket.krb5.ccache import CCache
        from nebulousAD.modimpacket.krb5.kerberosv5 import KerberosError
        from nebulousAD.modimpacket.krb5 import constants

        self._kdcHost = kdcHost
        self._useCache = useCache

        if TGT is not None or TGS is not None:
            useCache = False

        if useCache is True:
            try:
                ccache = CCache.loadFile(os.getenv('KRB5CCNAME'))
            except:
                # No cache present
                pass
            else:
                LOG.debug("Using Kerberos Cache: %s" % os.getenv('KRB5CCNAME'))
                # retrieve domain information from CCache file if needed
                if domain == '':
                    domain = ccache.principal.realm['data']
                    LOG.debug('Domain retrieved from CCache: %s' % domain)

                principal = 'cifs/%s@%s' % (self.getRemoteName().upper(),
                                            domain.upper())
                creds = ccache.getCredential(principal)
                if creds is None:
                    # Let's try for the TGT and go from there
                    principal = 'krbtgt/%s@%s' % (domain.upper(),
                                                  domain.upper())
                    creds = ccache.getCredential(principal)
                    if creds is not None:
                        TGT = creds.toTGT()
                        LOG.debug('Using TGT from cache')
                    else:
                        LOG.debug("No valid credentials found in cache. ")
                else:
                    TGS = creds.toTGS(principal)
                    LOG.debug('Using TGS from cache')

                # retrieve user information from CCache file if needed
                if user == '' and creds is not None:
                    user = creds['client'].prettyPrint().split('@')[0]
                    LOG.debug('Username retrieved from CCache: %s' % user)
                elif user == '' and len(ccache.principal.components) > 0:
                    user = ccache.principal.components[0]['data']
                    LOG.debug('Username retrieved from CCache: %s' % user)

        while True:
            try:
                if self.getDialect() == smb.SMB_DIALECT:
                    return self._SMBConnection.kerberos_login(
                        user, password, domain, lmhash, nthash, aesKey,
                        kdcHost, TGT, TGS)
                return self._SMBConnection.kerberosLogin(
                    user, password, domain, lmhash, nthash, aesKey, kdcHost,
                    TGT, TGS)
            except (smb.SessionError, smb3.SessionError), e:
                raise SessionError(e.get_error_code(), e.get_error_packet())
            except KerberosError, e:
                if e.getErrorCode(
                ) == constants.ErrorCodes.KDC_ERR_ETYPE_NOSUPP.value:
                    # We might face this if the target does not support AES
                    # So, if that's the case we'll force using RC4 by converting
                    # the password to lm/nt hashes and hope for the best. If that's already
                    # done, byebye.
                    if lmhash is '' and nthash is '' and (
                            aesKey is ''
                            or aesKey is None) and TGT is None and TGS is None:
                        from nebulousAD.modimpacket.ntlm import compute_lmhash, compute_nthash
                        lmhash = compute_lmhash(password)
                        nthash = compute_nthash(password)
                    else:
                        raise e
                else:
                    raise e
예제 #28
0
    def kerberosLogin(self,
                      user,
                      password,
                      domain='',
                      lmhash='',
                      nthash='',
                      aesKey='',
                      kdcHost=None,
                      TGT=None,
                      TGS=None,
                      useCache=True):
        """
        logins into the target system explicitly using Kerberos. Hashes are used if RC4_HMAC is supported.

        :param string user: username
        :param string password: password for the user
        :param string domain: domain where the account is valid for (required)
        :param string lmhash: LMHASH used to authenticate using hashes (password is not used)
        :param string nthash: NTHASH used to authenticate using hashes (password is not used)
        :param string aesKey: aes256-cts-hmac-sha1-96 or aes128-cts-hmac-sha1-96 used for Kerberos authentication
        :param string kdcHost: hostname or IP Address for the KDC. If None, the domain will be used (it needs to resolve tho)
        :param struct TGT: If there's a TGT available, send the structure here and it will be used
        :param struct TGS: same for TGS. See smb3.py for the format
        :param bool useCache: whether or not we should use the ccache for credentials lookup. If TGT or TGS are specified this is False

        :return: True, raises a LDAPSessionError if error.
        """

        if lmhash != '' or nthash != '':
            if len(lmhash) % 2:
                lmhash = '0' + lmhash
            if len(nthash) % 2:
                nthash = '0' + nthash
            try:  # just in case they were converted already
                lmhash = unhexlify(lmhash)
                nthash = unhexlify(nthash)
            except TypeError:
                pass

        # Importing down here so pyasn1 is not required if kerberos is not used.
        from nebulousAD.modimpacket.krb5.ccache import CCache
        from nebulousAD.modimpacket.krb5.asn1 import AP_REQ, Authenticator, TGS_REP, seq_set
        from nebulousAD.modimpacket.krb5.kerberosv5 import getKerberosTGT, getKerberosTGS
        from nebulousAD.modimpacket.krb5 import constants
        from nebulousAD.modimpacket.krb5.types import Principal, KerberosTime, Ticket
        import datetime

        if TGT is not None or TGS is not None:
            useCache = False

        if useCache:
            try:
                ccache = CCache.loadFile(os.getenv('KRB5CCNAME'))
            except:
                # No cache present
                pass
            else:
                # retrieve domain information from CCache file if needed
                if domain == '':
                    domain = ccache.principal.realm['data']
                    LOG.debug('Domain retrieved from CCache: %s' % domain)

                LOG.debug('Using Kerberos Cache: %s' % os.getenv('KRB5CCNAME'))
                principal = 'ldap/%s@%s' % (self._dstHost.upper(),
                                            domain.upper())
                creds = ccache.getCredential(principal)
                if creds is None:
                    # Let's try for the TGT and go from there
                    principal = 'krbtgt/%s@%s' % (domain.upper(),
                                                  domain.upper())
                    creds = ccache.getCredential(principal)
                    if creds is not None:
                        TGT = creds.toTGT()
                        LOG.debug('Using TGT from cache')
                    else:
                        LOG.debug('No valid credentials found in cache')
                else:
                    TGS = creds.toTGS(principal)
                    LOG.debug('Using TGS from cache')

                # retrieve user information from CCache file if needed
                if user == '' and creds is not None:
                    user = creds['client'].prettyPrint().split('@')[0]
                    LOG.debug('Username retrieved from CCache: %s' % user)
                elif user == '' and len(ccache.principal.components) > 0:
                    user = ccache.principal.components[0]['data']
                    LOG.debug('Username retrieved from CCache: %s' % user)

        # First of all, we need to get a TGT for the user
        userName = Principal(
            user, type=constants.PrincipalNameType.NT_PRINCIPAL.value)
        if TGT is None:
            if TGS is None:
                tgt, cipher, oldSessionKey, sessionKey = getKerberosTGT(
                    userName, password, domain, lmhash, nthash, aesKey,
                    kdcHost)
        else:
            tgt = TGT['KDC_REP']
            cipher = TGT['cipher']
            sessionKey = TGT['sessionKey']

        if TGS is None:
            serverName = Principal(
                'ldap/%s' % self._dstHost,
                type=constants.PrincipalNameType.NT_SRV_INST.value)
            tgs, cipher, oldSessionKey, sessionKey = getKerberosTGS(
                serverName, domain, kdcHost, tgt, cipher, sessionKey)
        else:
            tgs = TGS['KDC_REP']
            cipher = TGS['cipher']
            sessionKey = TGS['sessionKey']

            # Let's build a NegTokenInit with a Kerberos REQ_AP

        blob = SPNEGO_NegTokenInit()

        # Kerberos
        blob['MechTypes'] = [TypesMech['MS KRB5 - Microsoft Kerberos 5']]

        # Let's extract the ticket from the TGS
        tgs = decoder.decode(tgs, asn1Spec=TGS_REP())[0]
        ticket = Ticket()
        ticket.from_asn1(tgs['ticket'])

        # Now let's build the AP_REQ
        apReq = AP_REQ()
        apReq['pvno'] = 5
        apReq['msg-type'] = int(constants.ApplicationTagNumbers.AP_REQ.value)

        opts = []
        apReq['ap-options'] = constants.encodeFlags(opts)
        seq_set(apReq, 'ticket', ticket.to_asn1)

        authenticator = Authenticator()
        authenticator['authenticator-vno'] = 5
        authenticator['crealm'] = domain
        seq_set(authenticator, 'cname', userName.components_to_asn1)
        now = datetime.datetime.utcnow()

        authenticator['cusec'] = now.microsecond
        authenticator['ctime'] = KerberosTime.to_asn1(now)

        encodedAuthenticator = encoder.encode(authenticator)

        # Key Usage 11
        # AP-REQ Authenticator (includes application authenticator
        # subkey), encrypted with the application session key
        # (Section 5.5.1)
        encryptedEncodedAuthenticator = cipher.encrypt(sessionKey, 11,
                                                       encodedAuthenticator,
                                                       None)

        apReq['authenticator'] = noValue
        apReq['authenticator']['etype'] = cipher.enctype
        apReq['authenticator']['cipher'] = encryptedEncodedAuthenticator

        blob['MechToken'] = encoder.encode(apReq)

        # Done with the Kerberos saga, now let's get into LDAP

        bindRequest = BindRequest()
        bindRequest['version'] = 3
        bindRequest['name'] = user
        bindRequest['authentication']['sasl']['mechanism'] = 'GSS-SPNEGO'
        bindRequest['authentication']['sasl']['credentials'] = blob.getData()

        response = self.sendReceive(bindRequest)[0]['protocolOp']

        if response['bindResponse']['resultCode'] != ResultCode('success'):
            raise LDAPSessionError(
                errorString='Error in bindRequest -> %s: %s' %
                (response['bindResponse']['resultCode'].prettyPrint(),
                 response['bindResponse']['diagnosticMessage']))

        return True
예제 #29
0
def getKerberosType1(username,
                     password,
                     domain,
                     lmhash,
                     nthash,
                     aesKey='',
                     TGT=None,
                     TGS=None,
                     targetName='',
                     kdcHost=None,
                     useCache=True):
    if TGT is None and TGS is None:
        if useCache is True:
            try:
                ccache = CCache.loadFile(os.getenv('KRB5CCNAME'))
            except Exception, e:
                # No cache present
                pass
            else:
                # retrieve domain information from CCache file if needed
                if domain == '':
                    domain = ccache.principal.realm['data']
                    LOG.debug('Domain retrieved from CCache: %s' % domain)

                LOG.debug("Using Kerberos Cache: %s" % os.getenv('KRB5CCNAME'))
                principal = 'host/%s@%s' % (targetName.upper(), domain.upper())
                creds = ccache.getCredential(principal)
                if creds is None:
                    # Let's try for the TGT and go from there
                    principal = 'krbtgt/%s@%s' % (domain.upper(),
                                                  domain.upper())
                    creds = ccache.getCredential(principal)
                    if creds is not None:
                        TGT = creds.toTGT()
                        LOG.debug('Using TGT from cache')
                    else:
                        LOG.debug("No valid credentials found in cache. ")
                else:
                    TGS = creds.toTGS(principal)

                # retrieve user information from CCache file if needed
                if username == '' and creds is not None:
                    username = creds['client'].prettyPrint().split('@')[0]
                    LOG.debug('Username retrieved from CCache: %s' % username)
                elif username == '' and len(ccache.principal.components) > 0:
                    username = ccache.principal.components[0]['data']
                    LOG.debug('Username retrieved from CCache: %s' % username)
예제 #30
0
                    from nebulousAD.modimpacket.ntlm import compute_lmhash, compute_nthash
                    lmhash = compute_lmhash(password)
                    nthash = compute_nthash(password)
                    return getKerberosTGT(clientName, password, domain, lmhash,
                                          nthash, aesKey, kdcHost, requestPAC)
            raise

        asRep = decoder.decode(tgt, asn1Spec=AS_REP())[0]

    # So, we have the TGT, now extract the new session key and finish
    cipherText = asRep['enc-part']['cipher']

    if preAuth is False:
        # Let's output the TGT enc-part/cipher in John format, in case somebody wants to use it.
        LOG.debug('$krb5asrep$%d$%s@%s:%s$%s' %
                  (asRep['enc-part']['etype'], clientName, domain,
                   hexlify(asRep['enc-part']['cipher'].asOctets()[:16]),
                   hexlify(asRep['enc-part']['cipher'].asOctets()[16:])))
    # Key Usage 3
    # AS-REP encrypted part (includes TGS session key or
    # application session key), encrypted with the client key
    # (Section 5.4.2)
    try:
        plainText = cipher.decrypt(key, 3, str(cipherText))
    except InvalidChecksum, e:
        # probably bad password if preauth is disabled
        if preAuth is False:
            error_msg = "failed to decrypt session key: %s" % str(e)
            raise SessionKeyDecryptionError(error_msg, asRep, cipher, key,
                                            cipherText)
        raise
    encASRepPart = decoder.decode(plainText, asn1Spec=EncASRepPart())[0]