RustImplementation #23
+115
-3
@@ -15,6 +15,7 @@
|
|||||||
import argparse
|
import argparse
|
||||||
import dotenv
|
import dotenv
|
||||||
import os
|
import os
|
||||||
|
import re
|
||||||
import pandas as pd
|
import pandas as pd
|
||||||
import urllib3
|
import urllib3
|
||||||
import utils.clientfunctions
|
import utils.clientfunctions
|
||||||
@@ -133,7 +134,7 @@ def menu_main():
|
|||||||
ct.displayIntro();
|
ct.displayIntro();
|
||||||
print(ct.colorText("1. Get All Events for Single Device", "yellow"))
|
print(ct.colorText("1. Get All Events for Single Device", "yellow"))
|
||||||
print(ct.colorText("2. OTP", "yellow"))
|
print(ct.colorText("2. OTP", "yellow"))
|
||||||
print(ct.colorText("3. Placeholder for Another Tool", "yellow"))
|
print(ct.colorText("3. Divide by Exclusion", "yellow"))
|
||||||
print(ct.colorText("4. Prepare Policy For Enforcement", "yellow"))
|
print(ct.colorText("4. Prepare Policy For Enforcement", "yellow"))
|
||||||
print(ct.colorText("Q. Quit", "yellow"))
|
print(ct.colorText("Q. Quit", "yellow"))
|
||||||
|
|
||||||
@@ -174,9 +175,11 @@ def menu_otp():
|
|||||||
def menu_feature2():
|
def menu_feature2():
|
||||||
while True:
|
while True:
|
||||||
print("\n--- Submenu ---")
|
print("\n--- Submenu ---")
|
||||||
print("1. Pull last 24 horus execution for all ATPolicys")
|
print("1. Pull last 24 hours execution for all ATPolicys")
|
||||||
print("2. Generate Paths")
|
print("2. Generate Paths")
|
||||||
print("3 Merge")
|
print("3. Merge")
|
||||||
|
print("4. Pull existing paths")
|
||||||
|
print("5. Divide By Excluded or not excluded by path")
|
||||||
print("Q. Exit")
|
print("Q. Exit")
|
||||||
choice = input("Enter your choice: ")
|
choice = input("Enter your choice: ")
|
||||||
|
|
||||||
@@ -218,6 +221,115 @@ def menu_feature2():
|
|||||||
|
|
||||||
elif choice == "3":
|
elif choice == "3":
|
||||||
utils.pathfunctions.mergeTesting()
|
utils.pathfunctions.mergeTesting()
|
||||||
|
|
||||||
|
elif choice == "4":
|
||||||
|
|
||||||
|
|
||||||
|
# Get the AT policies dictionary
|
||||||
|
atpolicies = utils.policyfunctions.listATPolicies(url)
|
||||||
|
|
||||||
|
# List to hold each policy's path data
|
||||||
|
all_paths = []
|
||||||
|
|
||||||
|
# Loop through each policy name and group ID
|
||||||
|
for policy_name, group_id in atpolicies.items():
|
||||||
|
try:
|
||||||
|
# Get the list of paths using the group ID
|
||||||
|
paths = utils.pathfunctions.listPaths(url, group_id)
|
||||||
|
|
||||||
|
# Ensure each path is a string and append properly
|
||||||
|
for path in paths:
|
||||||
|
if isinstance(path, str):
|
||||||
|
all_paths.append({
|
||||||
|
'PolicyName': policy_name,
|
||||||
|
'GroupID': group_id,
|
||||||
|
'Path': path
|
||||||
|
})
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error retrieving paths for policy '{policy_name}': {e}")
|
||||||
|
|
||||||
|
# Convert to DataFrame
|
||||||
|
if all_paths:
|
||||||
|
paths_df = pd.DataFrame(all_paths)
|
||||||
|
print(paths_df.head()) # Optional: preview first few rows
|
||||||
|
print("✅ Paths DataFrame created.")
|
||||||
|
else:
|
||||||
|
paths_df = pd.DataFrame()
|
||||||
|
print("⚠️ No paths were retrieved.")
|
||||||
|
|
||||||
|
# Save to CSV
|
||||||
|
paths_df.to_csv("paths.csv", index=False)
|
||||||
|
elif choice == "5":
|
||||||
|
|
||||||
|
# Load and clean data
|
||||||
|
filenames_df = pd.read_csv('merged_output.csv') # Contains 'PolicyName' and 'filename'
|
||||||
|
exclusions_df = pd.read_csv('paths.csv') # Contains 'PolicyName' and 'Path'
|
||||||
|
|
||||||
|
# Clean and normalize columns
|
||||||
|
filenames_df['filename'] = filenames_df['filename'].fillna('').astype(str).str.strip()
|
||||||
|
filenames_df['PolicyName'] = filenames_df['PolicyName'].fillna('').astype(str).str.strip()
|
||||||
|
exclusions_df['Path'] = exclusions_df['Path'].fillna('').astype(str).str.strip()
|
||||||
|
exclusions_df['PolicyName'] = exclusions_df['PolicyName'].fillna('').astype(str).str.strip()
|
||||||
|
|
||||||
|
# Decode escaped backslashes in exclusion patterns
|
||||||
|
exclusions_df['Path'] = exclusions_df['Path'].apply(lambda p: p.encode('utf-8').decode('unicode_escape'))
|
||||||
|
|
||||||
|
# Build exclusion map: {PolicyName: [compiled regex patterns]}
|
||||||
|
exclusion_map = {}
|
||||||
|
for _, row in exclusions_df.iterrows():
|
||||||
|
policy = row['PolicyName']
|
||||||
|
raw_pattern = row['Path']
|
||||||
|
try:
|
||||||
|
regex = utils.pathfunctions.wildcardRegex(raw_pattern)
|
||||||
|
print(f"[EXCLUSION MAP] Policy: {policy}, Pattern: {raw_pattern} → Regex: {regex.pattern}")
|
||||||
|
exclusion_map.setdefault(policy, []).append(regex)
|
||||||
|
except Exception as e:
|
||||||
|
print(f"[ERROR] Failed to compile pattern for Policy: {policy}, Path: {raw_pattern}, Error: {e}")
|
||||||
|
|
||||||
|
# Function to check if a filename matches any exclusion pattern for its PolicyName
|
||||||
|
def is_excluded(row):
|
||||||
|
policy = row['PolicyName'].strip()
|
||||||
|
path = os.path.normpath(row['filename'].strip())
|
||||||
|
patterns = exclusion_map.get(policy, [])
|
||||||
|
for r in patterns:
|
||||||
|
if r.match(path):
|
||||||
|
print(f"[MATCH] {policy}: {path} matches {r.pattern}")
|
||||||
|
return True
|
||||||
|
print(f"[NO MATCH] {policy}: {path}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Apply matching
|
||||||
|
excluded = filenames_df[filenames_df.apply(is_excluded, axis=1)]
|
||||||
|
not_excluded = filenames_df[~filenames_df.apply(is_excluded, axis=1)]
|
||||||
|
|
||||||
|
|
||||||
|
# Desired column order
|
||||||
|
column_order = [
|
||||||
|
'PolicyName', 'filename', 'longestcfp',
|
||||||
|
'pprocess', 'gprocess', 'sha256', 'publisher', 'description', 'productname','commandline', 'middle', 'filename_only',
|
||||||
|
'file_extension', 'unique_sha256_count', 'hostname', 'username', 'productversion',
|
||||||
|
'reputation_lastseen', 'reputation_scannermatch', 'reputation_scannercount',
|
||||||
|
'reputation_status', 'reputation_threatlevel', 'reputation_threatname', 'reputation_timestamp'
|
||||||
|
]
|
||||||
|
|
||||||
|
# Reorder columns (ignore missing ones)
|
||||||
|
excluded = excluded[[col for col in column_order if col in excluded.columns]]
|
||||||
|
not_excluded = not_excluded[[col for col in column_order if col in not_excluded.columns]]
|
||||||
|
|
||||||
|
|
||||||
|
# Save results
|
||||||
|
excluded.to_csv('excluded_filenames.csv', index=False)
|
||||||
|
not_excluded.to_csv('non_excluded_filenames.csv', index=False)
|
||||||
|
|
||||||
|
|
||||||
|
elif choice == "6":
|
||||||
|
|
||||||
|
pattern = utils.pathfunctions.wildcardRegex("C:\\Windows\\SystemTemp\\????????\\????????.dll")
|
||||||
|
test_path = "C:\\Windows\\SystemTemp\\fsq2xzua\\fsq2xzua.dll"
|
||||||
|
|
||||||
|
print("Match:", pattern.match(test_path) is not None)
|
||||||
|
|
||||||
|
|
||||||
elif choice == "Q":
|
elif choice == "Q":
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
|
|||||||
Binary file not shown.
@@ -22,11 +22,13 @@ from AirlockTools import tryToReadCSV
|
|||||||
#Standard Libary Imports:
|
#Standard Libary Imports:
|
||||||
import ast
|
import ast
|
||||||
import gc
|
import gc
|
||||||
|
import json
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
|
|
||||||
#3rd Party Imports:
|
#3rd Party Imports:
|
||||||
import pandas as pd
|
import pandas as pd
|
||||||
|
import requests
|
||||||
|
|
||||||
def split_filepaths_grouped(df, col="filename", group_parts=4, min_parts=4):
|
def split_filepaths_grouped(df, col="filename", group_parts=4, min_parts=4):
|
||||||
def clean_split(path):
|
def clean_split(path):
|
||||||
@@ -321,3 +323,45 @@ def mergeTesting():
|
|||||||
merged_df.to_csv("merged_output.csv", index=False)
|
merged_df.to_csv("merged_output.csv", index=False)
|
||||||
|
|
||||||
print(f"Merged DataFrame saved with {len(merged_df)} rows.")
|
print(f"Merged DataFrame saved with {len(merged_df)} rows.")
|
||||||
|
|
||||||
|
|
||||||
|
def listPaths(url, group):
|
||||||
|
endpoint = url + '/v1/group/policies'
|
||||||
|
print(ct.colorText("[+] Grabbing All Paths", "cyan"))
|
||||||
|
|
||||||
|
payload = {
|
||||||
|
"groupid": [group],
|
||||||
|
}
|
||||||
|
headers = {
|
||||||
|
"X-APIKey": os.getenv('APIKEY')
|
||||||
|
}
|
||||||
|
|
||||||
|
try:
|
||||||
|
response = requests.post(endpoint, headers=headers, data=payload, verify=False)
|
||||||
|
response.raise_for_status()
|
||||||
|
parse_text = response.json()
|
||||||
|
|
||||||
|
pathnames = []
|
||||||
|
print(parse_text) # Optional: for debugging
|
||||||
|
|
||||||
|
for item in parse_text.get('response', {}).get('paths', []):
|
||||||
|
path = item.get('name')
|
||||||
|
if path:
|
||||||
|
pathnames.append(path)
|
||||||
|
|
||||||
|
return pathnames
|
||||||
|
|
||||||
|
except requests.exceptions.RequestException as e:
|
||||||
|
print(ct.colorText(f"[!] Request failed: {e}", "red"))
|
||||||
|
return []
|
||||||
|
except (KeyError, json.JSONDecodeError) as e:
|
||||||
|
print(ct.colorText(f"[!] Failed to parse response: {e}", "red"))
|
||||||
|
return []
|
||||||
|
|
||||||
|
def wildcardRegex(pattern):
|
||||||
|
pattern = pattern.replace("\\", "\\\\")
|
||||||
|
pattern = pattern.replace("**", "___RECURSIVE___")
|
||||||
|
pattern = pattern.replace("*", "[^\\\\]*")
|
||||||
|
pattern = pattern.replace("?", ".")
|
||||||
|
pattern = pattern.replace("___RECURSIVE___", ".*")
|
||||||
|
return re.compile(f"^{pattern}$", re.IGNORECASE)
|
||||||
@@ -353,3 +353,4 @@ def sendToPolicyTest(url, first_policy, second_policy, destination_name, destina
|
|||||||
print(ct.colorText(f"These hashes would be added to {allowlist_child_name}", "yellow"))
|
print(ct.colorText(f"These hashes would be added to {allowlist_child_name}", "yellow"))
|
||||||
allowlist_childhashlist = allowbyhash[allowbyhash['reputation_status'] == 'UNKNOWN']['sha256'].unique().tolist()
|
allowlist_childhashlist = allowbyhash[allowbyhash['reputation_status'] == 'UNKNOWN']['sha256'].unique().tolist()
|
||||||
addHash(url, allowlist_child_id, allowlist_childhashlist)
|
addHash(url, allowlist_child_id, allowlist_childhashlist)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user