Skip to content

Commit

Permalink
fix: rename group anomaly ACL to anomaly ACL
Browse files Browse the repository at this point in the history
  • Loading branch information
thomas-girard committed Nov 9, 2023
1 parent a902de9 commit c39ef0b
Show file tree
Hide file tree
Showing 7 changed files with 40 additions and 39 deletions.
4 changes: 2 additions & 2 deletions ad_miner/sources/modules/config.json
Expand Up @@ -102,8 +102,8 @@
"objects_to_operators_member": "true",
"vuln_permissions_adminsdholder": "true",
"da_to_da": "true",
"group_anomaly_acl_1": "true",
"group_anomaly_acl_2": "true",
"anomaly_acl_1": "true",
"anomaly_acl_2": "true",
"get_empty_groups": "true",
"get_empty_ous": "true",
"has_sid_history": "true",
Expand Down
4 changes: 2 additions & 2 deletions ad_miner/sources/modules/description.json
Expand Up @@ -479,8 +479,8 @@
"risk": "Such misconfiguration could lead to some account having non compliant password suck as a blank or weak password and might be easier to compromise.",
"poa": "Ensure that this list is empty by setting the ms-DS-User-Password-Not-Required attribute to false for every user."
},
"group_anomaly_acl": {
"title": "Group anomaly ACL",
"anomaly_acl": {
"title": "Anomaly ACL",
"description": "An ACL (Access Control List) is a security mechanism that defines permissions and access rights for objects within the Active Directory structure.",
"risk": "Misconfigured ACL can create access points or privilege escalation that an attacker could use to compromise the domain.<br /><br /><i class='bi bi-star-fill'></i><i class='bi bi-star-fill'></i><i class='bi bi-star-fill'></i> : At least one domain admin as target<br /><i class='bi bi-star-fill'></i><i class='bi bi-star-fill'></i><i class='bi bi-star'></i> : At least one object has a path to domain admin<br /><i class='bi bi-star-fill'></i><i class='bi bi-star'></i><i class='bi bi-star'></i> : At least one object admin of a computer<br /><i class='bi bi-star'></i><i class='bi bi-star'></i><i class='bi bi-star'></i> : Other",
"poa": "Regularly review and clean up ACL entries for users and groups that no longer require them."
Expand Down
4 changes: 2 additions & 2 deletions ad_miner/sources/modules/main_page.py
Expand Up @@ -233,7 +233,7 @@ def create_dico_data(
"dangerous_path": domains.total_dangerous_paths,
"users_password_not_required":len(users.users_password_not_required),
"can_read_laps":len(users.can_read_laps_parsed),
"group_anomaly_acl": users.number_group_ACL_anomaly,
"anomaly_acl": users.number_group_ACL_anomaly,
"empty_groups": len(domains.empty_groups),
"empty_ous": len(domains.empty_ous),
"has_sid_history": len(users.has_sid_history),
Expand Down Expand Up @@ -331,7 +331,7 @@ def render(
"dangerous_paths": f"More than {domains.total_dangerous_paths} dangerous paths to DA",
"users_password_not_required":f"{dico_data['value']['users_password_not_required']} users can bypass your password policy",
"can_read_laps": f"{len(users.can_read_laps_parsed)} accounts can read LAPS passwords",
"group_anomaly_acl": f"{users.number_group_ACL_anomaly} groups with potential ACL anomalies",
"anomaly_acl": f"{users.number_group_ACL_anomaly} groups with potential ACL anomalies",
"empty_groups": f"{len(domains.empty_groups)} groups without any member",
"empty_ous": f"{len(domains.empty_ous)} OUs without any member",
"has_sid_history": f"{len(users.has_sid_history)} objects can exploit SID History",
Expand Down
2 changes: 1 addition & 1 deletion ad_miner/sources/modules/rating.py
Expand Up @@ -159,7 +159,7 @@ def rating(users, domains, computers, objects, arguments):

d[2 if len(users.can_read_laps_parsed) > len(domains.users_nb_domain_admins) else 5].append("can_read_laps")

d[2 if users.number_group_ACL_anomaly > 0 else 5].append("group_anomaly_acl")
d[2 if users.number_group_ACL_anomaly > 0 else 5].append("anomaly_acl")

d[2 if len(domains.empty_groups)/len(domains.groups) > 0.40 else 3 if len(domains.empty_groups)/len(domains.groups) > 0.20 else 5].append("empty_groups")
d[2 if len(domains.empty_ous)/len(domains.groups) > 0.40 else 3 if len(domains.empty_ous)/len(domains.groups) > 0.20 else 5].append("empty_ous")
Expand Down
8 changes: 4 additions & 4 deletions ad_miner/sources/modules/requests.json
Expand Up @@ -597,13 +597,13 @@
"request": "MATCH p=allShortestPaths((g:Group{is_dag:true})-[r:$properties*1..$recursive_level]->(gg:Group{is_dag:true})) WHERE g<>gg AND g.domain <> gg.domain RETURN p",
"output_type": "Graph"
},
"group_anomaly_acl_1": {
"name": "group_anomaly_acl_1",
"anomaly_acl_1": {
"name": "anomaly_acl_1",
"request": "MATCH (gg) WHERE NOT gg:Group with gg as g MATCH (g)-[r2{isacl:true}]->(n) WHERE ((g.is_da IS NULL OR g.is_da=FALSE) AND (g.is_dc IS NULL OR g.is_dc=FALSE)) OR (NOT n.domain CONTAINS '.' + g.domain AND n.domain <> g.domain) RETURN n.name,g.name, type(r2)",
"output_type": "dict"
},
"group_anomaly_acl_2": {
"name": "group_anomaly_acl_2",
"anomaly_acl_2": {
"name": "anomaly_acl_2",
"request": "MATCH (gg:Group) WHERE EXISTS(gg.members_count) with gg as g order by gg.members_count DESC MATCH (g)-[r2{isacl:true}]->(n) WHERE ((g.is_da IS NULL OR g.is_da=FALSE) AND (g.is_dc IS NULL OR g.is_dc=FALSE)) OR (NOT n.domain CONTAINS '.' + g.domain AND n.domain <> g.domain) RETURN g.members_count,n.name,g.name, type(r2) order by g.members_count DESC",
"output_type": "dict"
},
Expand Down
2 changes: 1 addition & 1 deletion ad_miner/sources/modules/smolcard_class.py
Expand Up @@ -44,7 +44,7 @@
"users_GPO_access",
"da_to_da",
"dangerous_paths",
"group_anomaly_acl",
"anomaly_acl",
"has_sid_history",
"cross_domain_admin_privileges",
"guest_accounts",
Expand Down
55 changes: 28 additions & 27 deletions ad_miner/sources/modules/users.py
Expand Up @@ -84,8 +84,8 @@ def __init__(self, arguments, neo4j, domain):
else:
self.users_dc_impersonation_count=0

self.group_anomaly_acl_1 = neo4j.all_requests["group_anomaly_acl_1"]["result"]
self.group_anomaly_acl_2 = neo4j.all_requests["group_anomaly_acl_2"]["result"]
self.anomaly_acl_1 = neo4j.all_requests["anomaly_acl_1"]["result"]
self.anomaly_acl_2 = neo4j.all_requests["anomaly_acl_2"]["result"]

# users_can_impersonate_to_count = generic_computing.getCountValueFromKey(self.users_dc_impersonation, 'name')
# self.users_can_impersonate_count = len(users_can_impersonate_to_count) if self.users_dc_impersonation is not None else None
Expand Down Expand Up @@ -1453,33 +1453,33 @@ def generatePasswordNotRequiredPage(self):

def genGroupAnomalyAcl(self, domain):

if self.group_anomaly_acl_1 is None and self.group_anomaly_acl_2 is None:
if self.anomaly_acl_1 is None and self.anomaly_acl_2 is None:
page = Page(
self.arguments.cache_prefix, "group_anomaly_acl", "Group Anomaly ACL", "group_anomaly_acl"
self.arguments.cache_prefix, "anomaly_acl", "Group Anomaly ACL", "anomaly_acl"
)
page.render()
return 0

for each in range(len(self.group_anomaly_acl_1)):
self.group_anomaly_acl_1[each]['g.members_count'] = '-'
for each in range(len(self.anomaly_acl_1)):
self.anomaly_acl_1[each]['g.members_count'] = '-'

self.group_anomaly_acl = self.group_anomaly_acl_1 + self.group_anomaly_acl_2
self.anomaly_acl = self.anomaly_acl_1 + self.anomaly_acl_2

formated_data_details = []
formated_data = {}
group_anomaly_acl_extract = []
anomaly_acl_extract = []

for k in range(len(self.group_anomaly_acl)):
if formated_data.get(self.group_anomaly_acl[k]["g.name"]) and formated_data[self.group_anomaly_acl[k]["g.name"]]["type"] == self.group_anomaly_acl[k]["type(r2)"]:
formated_data[self.group_anomaly_acl[k]["g.name"]]["targets"].append(self.group_anomaly_acl[k]["n.name"])
elif formated_data.get(self.group_anomaly_acl[k]["g.name"]) and formated_data[self.group_anomaly_acl[k]["g.name"]]["targets"] == [self.group_anomaly_acl[k]["n.name"]] and self.group_anomaly_acl[k]["type(r2)"] not in formated_data[self.group_anomaly_acl[k]["g.name"]]["type"] :
formated_data[self.group_anomaly_acl[k]["g.name"]]["type"] += f" | {self.group_anomaly_acl[k]['type(r2)']}"
for k in range(len(self.anomaly_acl)):
if formated_data.get(self.anomaly_acl[k]["g.name"]) and formated_data[self.anomaly_acl[k]["g.name"]]["type"] == self.anomaly_acl[k]["type(r2)"]:
formated_data[self.anomaly_acl[k]["g.name"]]["targets"].append(self.anomaly_acl[k]["n.name"])
elif formated_data.get(self.anomaly_acl[k]["g.name"]) and formated_data[self.anomaly_acl[k]["g.name"]]["targets"] == [self.anomaly_acl[k]["n.name"]] and self.anomaly_acl[k]["type(r2)"] not in formated_data[self.anomaly_acl[k]["g.name"]]["type"] :
formated_data[self.anomaly_acl[k]["g.name"]]["type"] += f" | {self.anomaly_acl[k]['type(r2)']}"
else:
formated_data[self.group_anomaly_acl[k]["g.name"]] = {
"name": self.group_anomaly_acl[k]["g.name"],
"type": self.group_anomaly_acl[k]["type(r2)"],
"members_count": self.group_anomaly_acl[k]["g.members_count"],
"targets": [self.group_anomaly_acl[k]["n.name"]]
formated_data[self.anomaly_acl[k]["g.name"]] = {
"name": self.anomaly_acl[k]["g.name"],
"type": self.anomaly_acl[k]["type(r2)"],
"members_count": self.anomaly_acl[k]["g.members_count"],
"targets": [self.anomaly_acl[k]["n.name"]]
}

for name_instance in formated_data:
Expand Down Expand Up @@ -1514,7 +1514,7 @@ def genGroupAnomalyAcl(self, domain):
formated_data_details.append(tmp_dict)

page = Page(
self.arguments.cache_prefix, f"group_anomaly_acl_details_{name_instance}", "Group Anomaly ACL Details", "group_anomaly_acl"
self.arguments.cache_prefix, f"anomaly_acl_details_{name_instance}", "Group Anomaly ACL Details", "anomaly_acl"
)


Expand All @@ -1525,27 +1525,28 @@ def genGroupAnomalyAcl(self, domain):
page.addComponent(grid)
page.render()

group_anomaly_acl_extract.append(
anomaly_acl_extract.append(
{
"name": '<i class="bi bi-people-fill"></i> ' + name_instance,
"name": '<i class="bi bi-people-fill"></i> ' + name_instance if formated_data[name_instance]["members_count"] != "-" else '<i class="bi bi-person-fill"></i> ' + name_instance,
"type": formated_data[name_instance]["type"],
"members count": f'<i class="{str(formated_data[name_instance]["members_count"]).zfill(6)} bi bi-people-fill"></i> ' + str(formated_data[name_instance]["members_count"]),
"members count": f'<i class="{str(formated_data[name_instance]["members_count"]).zfill(6)} bi bi-people-fill"></i> ' + str(formated_data[name_instance]["members_count"]) if formated_data[name_instance]["members_count"] != '-' else '-',
"targets count": grid_data_stringify({
"link": f"group_anomaly_acl_details_{quote(str(name_instance))}.html",
"value": f"{len(formated_data[name_instance]['targets'])} target{'s' if len(formated_data[name_instance]['targets']) > 1 else ''} <i class='bi bi-box-arrow-up-right' aria-hidden='true'></i>",
"link": f"anomaly_acl_details_{quote(str(name_instance))}.html",
"value": f"{str(len(formated_data[name_instance]['targets'])) +' targets' if len(formated_data[name_instance]['targets']) > 1 else formated_data[name_instance]['targets'][0]} <i class='bi bi-box-arrow-up-right' aria-hidden='true'></i>",
"before_link": f"<i class='<i bi bi-bullseye {str(len(formated_data[name_instance]['targets'])).zfill(6)}'></i> "
}),
"interest": f"<span class='{interest}'></span><i class='bi bi-star-fill'></i>"*interest + "<i class='bi bi-star'></i>"*(3-interest)
}
)
#{'s' if len(formated_data[name_instance]['targets']) > 1 else ''}

page = Page(
self.arguments.cache_prefix, "group_anomaly_acl", "Group Anomaly ACL", "group_anomaly_acl"
self.arguments.cache_prefix, "anomaly_acl", "Group Anomaly ACL", "anomaly_acl"
)
grid = Grid("group_anomaly_acl")
grid = Grid("anomaly_acl")
grid.setheaders(["name", "type", "members count", "targets count", "interest"])

grid.setData(group_anomaly_acl_extract)
grid.setData(anomaly_acl_extract)
page.addComponent(grid)
page.render()

Expand Down

0 comments on commit c39ef0b

Please sign in to comment.