From 8098fc665c5dffc630af832f5d4b75d8e767829b Mon Sep 17 00:00:00 2001
From: = <=>
Date: Thu, 11 Sep 2025 10:25:16 -0400
Subject: [PATCH] OTP Generator Added
---
AirlockTools.py | 53 +++++++------
utils/allowlist.py | 155 ---------------------------------------
utils/clientfunctions.py | 88 +++++++++++++++++++++-
utils/getdeviceevents.py | 80 --------------------
utils/otpfunctions.py | 47 +++++++++---
utils/policyfunctions.py | 140 ++++++++++++++++++++++++++++++++++-
6 files changed, 288 insertions(+), 275 deletions(-)
delete mode 100644 utils/allowlist.py
delete mode 100644 utils/getdeviceevents.py
diff --git a/AirlockTools.py b/AirlockTools.py
index 86b1842..ecfdedb 100644
--- a/AirlockTools.py
+++ b/AirlockTools.py
@@ -16,11 +16,8 @@ import argparse
import dotenv
import os
import pandas as pd
-import time
import urllib3
-import utils.allowlist
import utils.clientfunctions
-import utils.getdeviceevents
import utils.hashfunctions
import utils.otpfunctions
import utils.pathfunctions
@@ -112,16 +109,16 @@ def menu_main():
while True:
ct.displayIntro();
print(ct.colorText("1. Get All Events for Single Device", "yellow"))
- print(ct.colorText("2. Placeholder for Local Approval", "yellow"))
+ print(ct.colorText("2. OTP", "yellow"))
print(ct.colorText("3. Placeholder for Another Tool", "yellow"))
print(ct.colorText("4. Prepare Policy For Enforcement", "yellow"))
print(ct.colorText("Q. Quit", "yellow"))
choice = input(ct.colorText("\nEnter Menu Item: ", "white"))
if choice == '1':
- utils.getdeviceevents.devicehistory(url,False)
+ utils.clientfunctions.devicehistory(url,False)
elif choice == "2":
- menu_local_approve()
+ menu_otp()
elif choice == "3":
menu_feature2()
elif choice == "4":
@@ -131,23 +128,25 @@ def menu_main():
else:
print(ct.colorText("Invalid choice. Please try again.","red"))
-def menu_local_approve():
- while True:
- print("\n--- Submenu ---")
- print("1. Sub-option A")
- print("2. Sub-option B")
- print("3. Return to Main Menu")
- choice = input("Enter your choice: ")
+def menu_otp():
+ while True:
+ print(ct.colorText("\n--- OTP Submenu ---","cyan"))
+ print(ct.colorText("1. Generate OTP","cyan"))
+ #print(ct.colorText("2. Sub-option B","cyan"))
+ print(ct.colorText("Q. Return to Main Menu","cyan"))
+ choice = input("Enter your choice: ")
- if choice == "1":
- print("You selected Sub-option A")
- elif choice == "2":
- print("You selected Sub-option B")
- elif choice == "3":
- print("Returning to Main Menu...")
- break
- else:
- print("Invalid choice. Please try again.")
+ if choice == "1":
+ utils.otpfunctions.generateOTP(url, utils.clientfunctions.findAgentID(url))
+ break
+
+ elif choice == "2":
+ print("You selected Sub-option B")
+ elif choice == "Q":
+ print("Returning to Main Menu...")
+ break
+ else:
+ print("Invalid choice. Please try again.")
def menu_feature2():
while True:
@@ -192,12 +191,12 @@ def menu_prepare_to_enforce():
if choice == "1":
- choice, policynames, policyid = utils.allowlist.listPolicies(url)
+ choice, policynames, policyid = utils.policyfunctions.listPolicies(url)
first_policy = policynames[choice]
while True:
answer = input(ct.colorText(f"{"Do you want to load a second policy?"} (yes/no): ", "white").strip().lower())
if answer in ("yes", "y"):
- choice, policynames, policyid = utils.allowlist.listPolicies(url)
+ choice, policynames, policyid = utils.policyfunctions.listPolicies(url)
second_policy = policynames[choice]
break
@@ -250,19 +249,19 @@ def menu_prepare_to_enforce():
elif choice == "5":
print(ct.colorText(f"Please choose destination_name Policy for Path Exclusions","white"))
- choice, policynames, policyid = utils.allowlist.listPolicies(url)
+ choice, policynames, policyid = utils.policyfunctions.listPolicies(url)
#print(allowlist_parent_tuple)
destination_name = policynames[choice]
destination_id = policyid[choice]
print(ct.colorText(f"Please choose Parent Allowlist for Known Hashes","white"))
- choice, allowlists,allowid = utils.allowlist.listAllowlists(url)
+ choice, allowlists,allowid = utils.policyfunctions.listAllowlists(url)
#print(allowlist_parent_tuple)
allowlist_parent_name = allowlists[choice]
allowlist_parent_id = allowid[choice]
print(ct.colorText(f"Please choose Child Allowlist for Less-Known Hashes","white"))
- choice, allowlists, allowid = utils.allowlist.listAllowlists(url)
+ choice, allowlists, allowid = utils.policyfunctions.listAllowlists(url)
#print(allowlist_child_tuple)
allowlist_child_name = allowlists[choice]
allowlist_child_id = allowid[choice]
diff --git a/utils/allowlist.py b/utils/allowlist.py
deleted file mode 100644
index 0f02958..0000000
--- a/utils/allowlist.py
+++ /dev/null
@@ -1,155 +0,0 @@
-# Copyright (C) 2025 James Brotosky, Brandon Wickline
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as published
-# by the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with this program. If not, see .
-import datetime
-import requests
-import json
-import os
-import utils.pretty as ct
-import ijson
-import os
-from bson import ObjectId
-import datetime
-import tqdm
-import sys
-
-def pullPolicyExechistories(url, policiesnames, days, outputjson: bool):
- file_path = 'chunkinator.json'
- if not os.path.exists(file_path):
- with open(file_path, 'w') as file:
- json.dump({'error': 'Success', 'response': {'exechistories': []}}, file)
- print(f"File '{file_path}' has been crated.")
- else:
- print(f"File '{file_path}' already exists.")
- headers = {"X-APIKey": os.getenv('APIKEY')}
- checkpoint = str(skipback(days))
- json_output = {'error': 'Success', 'response': {'exechistories': []}}
- with tqdm.tqdm(file=sys.stdout, leave=True, total=10000, desc=f"Checkpoint Progess: {checkpoint}", colour="blue", initial=1) as filebar:
- with tqdm.tqdm(file=sys.stdout, leave=True, total=100, desc=f"Total of {policiesnames} Complete: ") as pbar:
- while True:
- json_response_data = checkpoint_stomper(checkpoint, url, policiesnames, headers)
- histories = json_response_data['response']['exechistories']
- filebar.total=len(histories)
- if not histories:
- break
- match_found = True
- if match_found == True:
- for index, item in enumerate(histories):
- if index == len(histories) - 1:
- checkpoint = item['checkpoint']
- filebar.desc = f"Checkpoint Progress: {checkpoint}"
- break
- else:
- if (datetime.date.today() - datetime.timedelta(days=days) > datetime.datetime.strptime(item['datetime'].replace(' +0000 UTC', ''), '%Y-%m-%dT%H:%M:%SZ').date()):
- pass
- else: json_output['response']['exechistories'].append(item)
- filebar.update(1)
- filebar.refresh()
- seen = {}
- if os.path.exists(file_path):
- with open(file_path, 'r') as file:
- existing_data = json.load(file)
- combined = existing_data['response']['exechistories'] + json_output['response']['exechistories']
- else:
- combined = json_output['response']['exechistories']
- for item in combined:
- key = (item.get('sha256'), item.get('filename'), item.get('hostname'))
- seen[key] = item
- deduplicated = list(seen.values())
- with open(file_path, 'w') as file:
- json.dump({'error': 'Success', 'response': {'exechistories': deduplicated}}, file)
- json_output['response']['exechistories'].clear()
- date_diff = datetime.date.today() - datetime.datetime.strptime(item['datetime'].replace(' +0000 UTC', ''), '%Y-%m-%dT%H:%M:%SZ').date()
- percentage_diff = (((days + 10) - date_diff.days) / (days + 10)) * 100
- pbar.n = round(percentage_diff)
- pbar.set_description_str(f"Total of {policiesnames} Complete: ")
- pbar.refresh()
- filebar.n = 1
- with open(file_path, 'r') as file:
- final_output = json.load(file)
- os.remove(file_path)
- return json.dumps(final_output) if outputjson else None
-
-def checkpoint_stomper(checkpoint, url, policy, headers):
- json_output = {'error': 'Success', 'response': {'exechistories': []}}
- endpoint = url + '/v1/logging/exechistories'
- payload_dict = {
- "type":[1,2,6,7],
- "checkpoint": checkpoint,
- "policy": [policy]
- }
- payload = json.dumps(payload_dict)
- with requests.request("POST", endpoint, headers=headers, data=payload, verify=False, stream=True) as response:
- parser = ijson.items(response.raw, 'response.exechistories.item')
- for item in parser:
- key = (item.get('sha256'), item.get('hostname'))
- if key not in json_output:
- json_output['response']['exechistories'].append(item)
- parse_text = json.loads(json.dumps(json_output))
- return parse_text
-
-def listPolicies(url):
- endpoint = url + '/v1/group'
- print(ct.colorText("[+] Grabbing All Policies", "cyan"))
- payload = {}
- headers = {
- "X-APIKey": os.getenv('APIKEY')
- }
- response = requests.request("POST", endpoint, headers=headers, data=payload, verify=False)
- parse_text = json.loads(response.text)
- policiesnames = []
- policyids = []
- for index, list in enumerate(parse_text['response']['groups'], start=1):
- print(ct.colorText(f"{index}. {list['name']}", "yellow"))
- policiesnames.append(list['name'])
- policyids.append(list['groupid'])
- choice = input(ct.colorText("Select Policy Group: ", "white"))
- choice = int(choice) - 1
- return choice, policiesnames, policyids
-
-def listAllowlists(url):
- endpoint = url + '/v1/application'
- print(ct.colorText("[+] Grabbing All Allowlists", "cyan"))
- payload = {}
- headers = {
- "X-APIKey": os.getenv('APIKEY')
- }
- response = requests.request("POST", endpoint, headers=headers, data=payload, verify=False)
- parse_text = json.loads(response.text)
- policiesnames = []
- policyids = []
- for index, list in enumerate(parse_text['response']['applications'], start=1):
- if index >= 38:
- print(ct.colorText(f"{index}. {list['name']}", "yellow"))
- policiesnames.append(list['name'])
- policyids.append(list['applicationid'])
- choice = int(input(ct.colorText("Select allowlist: ", "white")))
- if choice < 38:
- print(ct.colorText("Please only choose an allowlist designed for this use - '38+'","red"))
- elif choice >= 38:
- choice = choice - 38
- return choice, policiesnames, policyids
- #Need else and catch for upper bound
-
-def skipback(days):
- """
- Generate a MongoDB ObjectId for a given number of days ago from today.
- Adds 1 extra day to the input to look further back.
- """
- adjusted_days = days + 10
- date_days_ago = datetime.datetime.now(datetime.UTC) - datetime.timedelta(days=adjusted_days)
- timestamp = int(date_days_ago.timestamp())
- hex_timestamp = format(timestamp, '08x')
- objectid_hex = hex_timestamp + '0000000000000000'
- return ObjectId(objectid_hex)
\ No newline at end of file
diff --git a/utils/clientfunctions.py b/utils/clientfunctions.py
index 323c7a9..486a1e0 100644
--- a/utils/clientfunctions.py
+++ b/utils/clientfunctions.py
@@ -5,6 +5,25 @@ import pandas as pd
import utils.pretty as ct
+def findAgentID(url):
+
+ print(ct.colorText("WARNING: Device Name is Case Sensitive", "red"))
+ hostname = input(ct.colorText("Enter Device Name: ", "white"))
+
+ endpoint = url + '/v1/agent/find'
+ payload = {
+ "hostname" : f"{hostname}"
+ }
+
+ headers = {"X-APIKey": os.getenv('APIKEY')}
+ payload = json.dumps(payload)
+
+
+ response = requests.post(endpoint, headers=headers, data=payload, verify=False)
+ result = json.loads(response.text)
+ data = pd.DataFrame(result["response"]["agents"])
+ return data.loc[0, "agentid"]
+
def getDestAllowlistFromClientID(url, clientid):
#This is dependant on their being an allowlist in the policy containing the name "localapproval"
@@ -76,4 +95,71 @@ def getPolicyName(url, groupid):
result = json.loads(response.text)
data = pd.DataFrame(result["response"]["groups"])
name = data.loc[data['groupid'] == f"{groupid}", 'name'].values[0]
- return name
\ No newline at end of file
+ return name
+import datetime
+import requests
+import json
+import os
+import utils.pretty as ct
+
+def devicehistory(url, outputjson: bool):
+ endpoint = url + '/v1/getexechistory'
+ print("\n")
+ print(ct.colorText("1. Today", "yellow"))
+ print(ct.colorText("2. Last 24 Hours", "yellow"))
+ print(ct.colorText("3. Past 7 Days", "yellow"))
+ print(ct.colorText("4. Past 30 Days", "yellow"))
+ print(ct.colorText("5. Custom Date Range","yellow"))
+ choice = input(ct.colorText("\nSelect Date Range: ", "white"))
+ today = datetime.date.today()
+ today = today.strftime("%Y-%m-%d")
+ if choice == '1':
+ date_selected = today
+ elif choice == '2':
+ date_selected = datetime.date.today() - datetime.timedelta(days=1)
+ date_selected = date_selected.strftime('%Y-%m-%d')
+ elif choice == '3':
+ date_selected = datetime.date.today() - datetime.timedelta(days=7)
+ date_selected = date_selected.strftime('%Y-%m-%d')
+ elif choice == '4':
+ date_selected = datetime.date.today() - datetime.timedelta(days=30)
+ date_selected = date_selected.strftime('%Y-%m-%d')
+ elif choice == "5":
+ print(ct.colorText("Please Input Dates as YYYY-MM-DD", "cyan"))
+ date_selected = input(ct.colorText("From: ", "white"))
+ today = input(ct.colorText("Date To: ", "white"))
+ print(ct.colorText("WARNING: Device Name is Case Sensitive", "red"))
+ device = input(ct.colorText("Enter Device Name: ", "white"))
+ payload_dict = {
+ "datefrom": date_selected,
+ "dateto": today,
+ "hostname": device
+ }
+ payload = json.dumps(payload_dict)
+ print(ct.colorText(payload, "green"))
+ headers = {
+ "X-APIKey": os.getenv('APIKEY')
+ }
+
+ response = requests.request("POST", endpoint, headers=headers, data=payload, verify=False)
+
+ if outputjson:
+ return response
+
+ parse_text = json.loads(response.text)
+
+ # Safely get exechistory
+ exechistory = parse_text.get('response', {}).get('exechistory')
+
+ if isinstance(exechistory, list):
+ for block in exechistory:
+ print(ct.colorText(f"Command: {block.get('commandline', 'N/A')}", "green"))
+ print(ct.colorText(f"Date: {block.get('datetime', 'N/A')}", "green"))
+ print(ct.colorText(f"Filename: {block.get('filename', 'N/A')}", "green"))
+ print(ct.colorText(f"Policy Name: {block.get('policyname', 'N/A')}", "green"))
+ print(ct.colorText(f"Hostname: {block.get('hostname', 'N/A')}", "green"))
+ print(ct.colorText(f"Hash: {block.get('sha256', 'N/A')}", "green"))
+ print("\n")
+ else:
+ print(ct.colorText("No execution history found or data is not in expected format.", "red"))
+
diff --git a/utils/getdeviceevents.py b/utils/getdeviceevents.py
deleted file mode 100644
index fbb5d66..0000000
--- a/utils/getdeviceevents.py
+++ /dev/null
@@ -1,80 +0,0 @@
-# Copyright (C) 2025 James Brotosky, Brandon Wickline
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as published
-# by the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with this program. If not, see .
-import datetime
-import requests
-import json
-import os
-import utils.pretty as ct
-
-def devicehistory(url, outputjson: bool):
- endpoint = url + '/v1/getexechistory'
- print("\n")
- print(ct.colorText("1. Today", "yellow"))
- print(ct.colorText("2. Last 24 Hours", "yellow"))
- print(ct.colorText("3. Past 7 Days", "yellow"))
- print(ct.colorText("4. Past 30 Days", "yellow"))
- print(ct.colorText("5. Custom Date Range","yellow"))
- choice = input(ct.colorText("\nSelect Date Range: ", "white"))
- today = datetime.date.today()
- today = today.strftime("%Y-%m-%d")
- if choice == '1':
- date_selected = today
- elif choice == '2':
- date_selected = datetime.date.today() - datetime.timedelta(days=1)
- date_selected = date_selected.strftime('%Y-%m-%d')
- elif choice == '3':
- date_selected = datetime.date.today() - datetime.timedelta(days=7)
- date_selected = date_selected.strftime('%Y-%m-%d')
- elif choice == '4':
- date_selected = datetime.date.today() - datetime.timedelta(days=30)
- date_selected = date_selected.strftime('%Y-%m-%d')
- elif choice == "5":
- print(ct.colorText("Please Input Dates as YYYY-MM-DD", "cyan"))
- date_selected = input(ct.colorText("From: ", "white"))
- today = input(ct.colorText("Date To: ", "white"))
- print(ct.colorText("WARNING: Device Name is Case Sensitive", "red"))
- device = input(ct.colorText("Enter Device Name: ", "white"))
- payload_dict = {
- "datefrom": date_selected,
- "dateto": today,
- "hostname": device
- }
- payload = json.dumps(payload_dict)
- print(ct.colorText(payload, "green"))
- headers = {
- "X-APIKey": os.getenv('APIKEY')
- }
-
- response = requests.request("POST", endpoint, headers=headers, data=payload, verify=False)
-
- if outputjson:
- return response
-
- parse_text = json.loads(response.text)
-
- # Safely get exechistory
- exechistory = parse_text.get('response', {}).get('exechistory')
-
- if isinstance(exechistory, list):
- for block in exechistory:
- print(ct.colorText(f"Command: {block.get('commandline', 'N/A')}", "green"))
- print(ct.colorText(f"Date: {block.get('datetime', 'N/A')}", "green"))
- print(ct.colorText(f"Filename: {block.get('filename', 'N/A')}", "green"))
- print(ct.colorText(f"Policy Name: {block.get('policyname', 'N/A')}", "green"))
- print(ct.colorText(f"Hostname: {block.get('hostname', 'N/A')}", "green"))
- print(ct.colorText(f"Hash: {block.get('sha256', 'N/A')}", "green"))
- print("\n")
- else:
- print(ct.colorText("No execution history found or data is not in expected format.", "red"))
\ No newline at end of file
diff --git a/utils/otpfunctions.py b/utils/otpfunctions.py
index 55e4fb4..d78fe85 100644
--- a/utils/otpfunctions.py
+++ b/utils/otpfunctions.py
@@ -11,10 +11,6 @@ import utils.pathfunctions as pathf
import utils.policyfunctions as policyf
from utils.perstscheduler import register_function, run_once_job, recurring_job, reload_jobs, start_scheduler
-
-
-
-
def getActiveOTP(url):
endpoint = url + f'/v1/otp/usage'
@@ -36,7 +32,6 @@ def getActiveOTP(url):
if not otp.empty:
ct.style_dataframe_dark(otp, f"newest_active_OTP.html")
-
def getOTPActivities(url, otpid):
endpoint = url + f'/v1/otp/activities'
payload = {"otpid": f"{otpid}"}
@@ -137,10 +132,6 @@ def monitorOTP(url, pups):
os.remove(f"OTP\\PARQ\\otp_activities_{pid}.parquet")
del finalhashesadded
-
-
-
-
def addOTPHashes(url, clientid, otpid, pups):
path = f"OTP\\PARQ\\otp_activities_{otpid}.parquet"
activities = pd.read_parquet(path)
@@ -160,7 +151,7 @@ def addOTPHashes(url, clientid, otpid, pups):
# Add hashes to policy
if hashes_to_add:
- policyf.addHashReal(url, allowlist, hashes_to_add)
+ policyf.addHash(url, allowlist, hashes_to_add)
# Update 'hash_added' column
activities["hash_added"] = activities.apply(
@@ -171,3 +162,39 @@ def addOTPHashes(url, clientid, otpid, pups):
# Save the updated DataFrame
activities.to_parquet(path)
+
+def generateOTP(url, agentid):
+ purpose = input(ct.colorText(" Please enter the purpose for the OTP: ", "white"))
+
+ possible_durations = [15, 60, 360, 1440, 10080]
+ duration_selected = " "
+
+ print(ct.colorText("Please select a duration:", "white"))
+ for i, option in enumerate(possible_durations, start=1):
+ print(f"{i}. {option}")
+
+ try:
+ choice = int(input("Enter the number of your choice: "))
+ if 1 <= choice <= len(possible_durations):
+ duration_selected = possible_durations[choice - 1]
+ print(ct.colorText(f"You selected: {duration_selected}", "yellow"))
+ else:
+ print(ct.colorText("Invalid choice.", "red"))
+ except ValueError:
+ print(ct.colorText("Invalid input. Please enter a number.", "red"))
+
+ endpoint = url + '/v1/otp/retrieve'
+ payload = {
+ "duration" : f"{duration_selected}",
+ "agentid" : f"{agentid}",
+ "purpose" : f"{purpose}"
+ }
+
+ headers = {"X-APIKey": os.getenv('APIKEY')}
+ payload = json.dumps(payload)
+
+
+ response = requests.post(endpoint, headers=headers, data=payload, verify=False)
+ result = json.loads(response.text)
+ otpcode = result["response"]["otpcode"]
+ print(ct.colorText(f"The OPT code is: {otpcode}", "yellow"))
\ No newline at end of file
diff --git a/utils/policyfunctions.py b/utils/policyfunctions.py
index f6cfb71..227a48d 100644
--- a/utils/policyfunctions.py
+++ b/utils/policyfunctions.py
@@ -19,7 +19,13 @@ import pandas as pd
import re
import requests
import utils.pretty as ct
-import utils.allowlist
+import datetime
+import requests
+import ijson
+from bson import ObjectId
+import datetime
+import tqdm
+import sys
@@ -69,7 +75,7 @@ def addPathReal(url, grouplistID, pathlist):
def getPolicyInfo(url, policy, days):
executionhist_policy = pd.DataFrame()
- exehist = utils.allowlist.pullPolicyExechistories(url, policy, days, True)
+ exehist = pullPolicyExechistories(url, policy, days, True)
data = json.loads(exehist)
executionhist_policy = pd.DataFrame(data["response"]["exechistories"])
if not executionhist_policy.empty:
@@ -120,4 +126,134 @@ def sendToPolicy(url, first_policy, second_policy, destination_name, destination
else:
print(ct.colorText("Operation aborted. You MUST EXPLICITLY AGREE to proceed.", "red"))
+
+def pullPolicyExechistories(url, policiesnames, days, outputjson: bool):
+ file_path = 'chunkinator.json'
+ if not os.path.exists(file_path):
+ with open(file_path, 'w') as file:
+ json.dump({'error': 'Success', 'response': {'exechistories': []}}, file)
+ print(f"File '{file_path}' has been crated.")
+ else:
+ print(f"File '{file_path}' already exists.")
+ headers = {"X-APIKey": os.getenv('APIKEY')}
+ checkpoint = str(skipback(days))
+ json_output = {'error': 'Success', 'response': {'exechistories': []}}
+ with tqdm.tqdm(file=sys.stdout, leave=True, total=10000, desc=f"Checkpoint Progess: {checkpoint}", colour="blue", initial=1) as filebar:
+ with tqdm.tqdm(file=sys.stdout, leave=True, total=100, desc=f"Total of {policiesnames} Complete: ") as pbar:
+ while True:
+ json_response_data = checkpoint_stomper(checkpoint, url, policiesnames, headers)
+ histories = json_response_data['response']['exechistories']
+ filebar.total=len(histories)
+ if not histories:
+ break
+ match_found = True
+ if match_found == True:
+ for index, item in enumerate(histories):
+ if index == len(histories) - 1:
+ checkpoint = item['checkpoint']
+ filebar.desc = f"Checkpoint Progress: {checkpoint}"
+ break
+ else:
+ if (datetime.date.today() - datetime.timedelta(days=days) > datetime.datetime.strptime(item['datetime'].replace(' +0000 UTC', ''), '%Y-%m-%dT%H:%M:%SZ').date()):
+ pass
+ else: json_output['response']['exechistories'].append(item)
+ filebar.update(1)
+ filebar.refresh()
+ seen = {}
+ if os.path.exists(file_path):
+ with open(file_path, 'r') as file:
+ existing_data = json.load(file)
+ combined = existing_data['response']['exechistories'] + json_output['response']['exechistories']
+ else:
+ combined = json_output['response']['exechistories']
+ for item in combined:
+ key = (item.get('sha256'), item.get('filename'), item.get('hostname'))
+ seen[key] = item
+ deduplicated = list(seen.values())
+ with open(file_path, 'w') as file:
+ json.dump({'error': 'Success', 'response': {'exechistories': deduplicated}}, file)
+ json_output['response']['exechistories'].clear()
+ date_diff = datetime.date.today() - datetime.datetime.strptime(item['datetime'].replace(' +0000 UTC', ''), '%Y-%m-%dT%H:%M:%SZ').date()
+ percentage_diff = (((days + 10) - date_diff.days) / (days + 10)) * 100
+ pbar.n = round(percentage_diff)
+ pbar.set_description_str(f"Total of {policiesnames} Complete: ")
+ pbar.refresh()
+ filebar.n = 1
+ with open(file_path, 'r') as file:
+ final_output = json.load(file)
+ os.remove(file_path)
+ return json.dumps(final_output) if outputjson else None
+
+def checkpoint_stomper(checkpoint, url, policy, headers):
+ json_output = {'error': 'Success', 'response': {'exechistories': []}}
+ endpoint = url + '/v1/logging/exechistories'
+ payload_dict = {
+ "type":[1,2,6,7],
+ "checkpoint": checkpoint,
+ "policy": [policy]
+ }
+ payload = json.dumps(payload_dict)
+ with requests.request("POST", endpoint, headers=headers, data=payload, verify=False, stream=True) as response:
+ parser = ijson.items(response.raw, 'response.exechistories.item')
+ for item in parser:
+ key = (item.get('sha256'), item.get('hostname'))
+ if key not in json_output:
+ json_output['response']['exechistories'].append(item)
+ parse_text = json.loads(json.dumps(json_output))
+ return parse_text
+
+def listPolicies(url):
+ endpoint = url + '/v1/group'
+ print(ct.colorText("[+] Grabbing All Policies", "cyan"))
+ payload = {}
+ headers = {
+ "X-APIKey": os.getenv('APIKEY')
+ }
+ response = requests.request("POST", endpoint, headers=headers, data=payload, verify=False)
+ parse_text = json.loads(response.text)
+ policiesnames = []
+ policyids = []
+ for index, list in enumerate(parse_text['response']['groups'], start=1):
+ print(ct.colorText(f"{index}. {list['name']}", "yellow"))
+ policiesnames.append(list['name'])
+ policyids.append(list['groupid'])
+ choice = input(ct.colorText("Select Policy Group: ", "white"))
+ choice = int(choice) - 1
+ return choice, policiesnames, policyids
+
+def listAllowlists(url):
+ endpoint = url + '/v1/application'
+ print(ct.colorText("[+] Grabbing All Allowlists", "cyan"))
+ payload = {}
+ headers = {
+ "X-APIKey": os.getenv('APIKEY')
+ }
+ response = requests.request("POST", endpoint, headers=headers, data=payload, verify=False)
+ parse_text = json.loads(response.text)
+ policiesnames = []
+ policyids = []
+ for index, list in enumerate(parse_text['response']['applications'], start=1):
+ if index >= 38:
+ print(ct.colorText(f"{index}. {list['name']}", "yellow"))
+ policiesnames.append(list['name'])
+ policyids.append(list['applicationid'])
+ choice = int(input(ct.colorText("Select allowlist: ", "white")))
+ if choice < 38:
+ print(ct.colorText("Please only choose an allowlist designed for this use - '38+'","red"))
+ elif choice >= 38:
+ choice = choice - 38
+ return choice, policiesnames, policyids
+ #Need else and catch for upper bound
+
+def skipback(days):
+ """
+ Generate a MongoDB ObjectId for a given number of days ago from today.
+ Adds 1 extra day to the input to look further back.
+ """
+ adjusted_days = days + 10
+ date_days_ago = datetime.datetime.now(datetime.UTC) - datetime.timedelta(days=adjusted_days)
+ timestamp = int(date_days_ago.timestamp())
+ hex_timestamp = format(timestamp, '08x')
+ objectid_hex = hex_timestamp + '0000000000000000'
+ return ObjectId(objectid_hex)
\ No newline at end of file