diff --git a/pywerview/cli/helpers.py b/pywerview/cli/helpers.py index 9ee7378..3cd40cd 100644 --- a/pywerview/cli/helpers.py +++ b/pywerview/cli/helpers.py @@ -36,7 +36,7 @@ def get_adobject(domain_controller, domain, user, password=str(), queried_sam_account_name=queried_sam_account_name, ads_path=ads_path, attributes=attributes, custom_filter=custom_filter) -def get_adserviceaccount(domain_controller, domain, user, password=str(), +def get_netgmsa(domain_controller, domain, user, password=str(), lmhash=str(), nthash=str(), do_kerberos=False, do_tls=False, user_cert=str(), user_key=str(), queried_domain=str(), queried_sid=str(), queried_name=str(), @@ -44,11 +44,24 @@ def get_adserviceaccount(domain_controller, domain, user, password=str(), requester = NetRequester(domain_controller, domain, user, password, lmhash, nthash, do_kerberos, do_tls, user_cert, user_key) - return requester.get_adserviceaccount(queried_domain=queried_domain, + return requester.get_netgmsa(queried_domain=queried_domain, queried_sid=queried_sid, queried_name=queried_name, queried_sam_account_name=queried_sam_account_name, ads_path=ads_path, resolve_sids=resolve_sids) +def get_netsmsa(domain_controller, domain, user, password=str(), + lmhash=str(), nthash=str(), do_kerberos=False, do_tls=False, + user_cert=str(), user_key=str(), + queried_domain=str(), queried_sid=str(), queried_name=str(), + queried_sam_account_name=str(), ads_path=str()): + requester = NetRequester(domain_controller, domain, user, password, + lmhash, nthash, do_kerberos, do_tls, + user_cert, user_key) + return requester.get_netsmsa(queried_domain=queried_domain, + queried_sid=queried_sid, queried_name=queried_name, + queried_sam_account_name=queried_sam_account_name, + ads_path=ads_path) + def get_objectacl(domain_controller, domain, user, password=str(), lmhash=str(), nthash=str(), do_kerberos=False, do_tls=False, queried_domain=str(), queried_sid=str(), queried_name=str(), diff --git a/pywerview/cli/main.py b/pywerview/cli/main.py index fc8433f..3ec15ef 100644 --- a/pywerview/cli/main.py +++ b/pywerview/cli/main.py @@ -129,24 +129,40 @@ def main(): default=[], help='Object attributes to return') get_adobject_parser.set_defaults(func=get_adobject) - # Parser for the get-adserviceaccount command - get_adserviceaccount_parser = subparsers.add_parser('get-adserviceaccount', help='Returns a list of all the '\ + # Parser for the get-netgmsa command + get_netgmsa_parser = subparsers.add_parser('get-netgmsa', help='Returns a list of all the '\ 'gMSA of the specified domain. To retrieve passwords, you need a privileged account and '\ 'a TLS connection to the LDAP server (use the --tls switch).', parents=[ad_parser, logging_parser, json_output_parser, certificate_parser]) - get_adserviceaccount_parser.add_argument('--sid', dest='queried_sid', + get_netgmsa_parser.add_argument('--sid', dest='queried_sid', help='SID to query (wildcards accepted)') - get_adserviceaccount_parser.add_argument('--sam-account-name', dest='queried_sam_account_name', + get_netgmsa_parser.add_argument('--sam-account-name', dest='queried_sam_account_name', help='samAccountName to query (wildcards accepted)') - get_adserviceaccount_parser.add_argument('--name', dest='queried_name', + get_netgmsa_parser.add_argument('--name', dest='queried_name', help='Name to query (wildcards accepted)') - get_adserviceaccount_parser.add_argument('-d', '--domain', dest='queried_domain', + get_netgmsa_parser.add_argument('-d', '--domain', dest='queried_domain', help='Domain to query') - get_adserviceaccount_parser.add_argument('-a', '--ads-path', + get_netgmsa_parser.add_argument('-a', '--ads-path', help='Additional ADS path') - get_adserviceaccount_parser.add_argument('--resolve-sids', dest='resolve_sids', + get_netgmsa_parser.add_argument('--resolve-sids', dest='resolve_sids', action='store_true', help='Resolve SIDs when querying PrincipalsAllowedToRetrieveManagedPassword') - get_adserviceaccount_parser.set_defaults(func=get_adserviceaccount) + get_netgmsa_parser.set_defaults(func=get_netgmsa) + + # Parser for the get-netsmsa command + get_netsmsa_parser = subparsers.add_parser('get-netsmsa', help='Returns a list of all the '\ + 'sMSA of the specified domain.', + parents=[ad_parser, logging_parser, json_output_parser, certificate_parser]) + get_netsmsa_parser.add_argument('--sid', dest='queried_sid', + help='SID to query (wildcards accepted)') + get_netsmsa_parser.add_argument('--sam-account-name', dest='queried_sam_account_name', + help='samAccountName to query (wildcards accepted)') + get_netsmsa_parser.add_argument('--name', dest='queried_name', + help='Name to query (wildcards accepted)') + get_netsmsa_parser.add_argument('-d', '--domain', dest='queried_domain', + help='Domain to query') + get_netsmsa_parser.add_argument('-a', '--ads-path', + help='Additional ADS path') + get_netsmsa_parser.set_defaults(func=get_netsmsa) # Parser for the get-objectacl command get_objectacl_parser = subparsers.add_parser('get-objectacl', help='Takes a domain SID, '\ diff --git a/pywerview/functions/net.py b/pywerview/functions/net.py index a4be1e5..ec605fe 100644 --- a/pywerview/functions/net.py +++ b/pywerview/functions/net.py @@ -49,7 +49,7 @@ def get_adobject(self, queried_domain=str(), queried_sid=str(), return self._ldap_search(object_filter, adobj.ADObject, attributes=attributes) @LDAPRPCRequester._ldap_connection_init - def get_adserviceaccount(self, queried_domain=str(), queried_sid=str(), + def get_netgmsa(self, queried_domain=str(), queried_sid=str(), queried_name=str(), queried_sam_account_name=str(), ads_path=str(), resolve_sids=False): filter_objectclass = '(ObjectClass=msDS-GroupManagedServiceAccount)' @@ -69,10 +69,10 @@ def get_adserviceaccount(self, queried_domain=str(), queried_sid=str(), else: object_filter = '(&(name=*){})'.format(filter_objectclass) - adserviceaccounts = self._ldap_search(object_filter, adobj.GMSAAccount, attributes=attributes) + gmsa = self._ldap_search(object_filter, adobj.GMSAAccount, attributes=attributes) # In this loop, we resolve SID (if true) and we populate 'enabled' attribute - for i, adserviceaccount in enumerate(adserviceaccounts): + for i, adserviceaccount in enumerate(gmsa): if resolve_sids: results = list() for sid in getattr(adserviceaccount, 'msds-groupmsamembership'): @@ -83,10 +83,36 @@ def get_adserviceaccount(self, queried_domain=str(), queried_sid=str(), self._logger.warning('We did not manage to resolve this SID ({}) against the DC'.format(sid)) resolved_sid = sid results.append(resolved_sid) - adserviceaccounts[i].add_attributes({'msds-groupmsamembership': results}) - adserviceaccounts[i].add_attributes({'Enabled': 'ACCOUNTDISABLE' not in adserviceaccount.useraccountcontrol}) - adserviceaccounts[i]._attributes_dict.pop('useraccountcontrol') - return adserviceaccounts + gmsa[i].add_attributes({'msds-groupmsamembership': results}) + gmsa[i].add_attributes({'Enabled': 'ACCOUNTDISABLE' not in adserviceaccount.useraccountcontrol}) + gmsa[i]._attributes_dict.pop('useraccountcontrol') + + return gmsa + + @LDAPRPCRequester._ldap_connection_init + def get_netsmsa(self, queried_domain=str(), queried_sid=str(), + queried_name=str(), queried_sam_account_name=str(), + ads_path=str()): + + filter_objectclass = '(ObjectClass=msDS-ManagedServiceAccount)' + attributes = ['samaccountname', 'distinguishedname', 'objectsid', 'description', + 'msds-hostserviceaccountbl', 'useraccountcontrol'] + for attr_desc, attr_value in (('objectSid', queried_sid), ('name', escape_filter_chars(queried_name)), + ('samAccountName', escape_filter_chars(queried_sam_account_name))): + if attr_value: + object_filter = '(&({}={}){})'.format(attr_desc, attr_value, filter_objectclass) + break + else: + object_filter = '(&(name=*){})'.format(filter_objectclass) + + smsas = self._ldap_search(object_filter, adobj.SMSAAccount, attributes=attributes) + + # In this loop, we populate 'enabled' attribute + for i, adserviceaccount in enumerate(smsas): + smsas[i].add_attributes({'Enabled': 'ACCOUNTDISABLE' not in adserviceaccount.useraccountcontrol}) + smsas[i]._attributes_dict.pop('useraccountcontrol') + + return smsas @LDAPRPCRequester._ldap_connection_init def get_objectacl(self, queried_domain=str(), queried_sid=str(), diff --git a/pywerview/objects/adobjects.py b/pywerview/objects/adobjects.py index e8a9d28..0362334 100644 --- a/pywerview/objects/adobjects.py +++ b/pywerview/objects/adobjects.py @@ -185,3 +185,5 @@ class GPOLocation(ADObject): class GMSAAccount(ADObject): pass +class SMSAAccount(ADObject): + pass