From 79a0541871f4bfc1ee116e10f8e169fa8beb1da3 Mon Sep 17 00:00:00 2001 From: brotoskyj Date: Tue, 19 Aug 2025 17:11:11 -0400 Subject: [PATCH 1/4] Working on Policy List Grab --- AirlockTools.py | 9 +++-- utils/__pycache__/allowlist.cpython-313.pyc | Bin 0 -> 1837 bytes .../getdeviceevents.cpython-313.pyc | Bin 3012 -> 3012 bytes utils/allowlist.py | 33 ++++++++++++++++++ 4 files changed, 40 insertions(+), 2 deletions(-) create mode 100644 utils/__pycache__/allowlist.cpython-313.pyc create mode 100644 utils/allowlist.py diff --git a/AirlockTools.py b/AirlockTools.py index 0995fe8..de6960b 100644 --- a/AirlockTools.py +++ b/AirlockTools.py @@ -1,6 +1,10 @@ import dotenv import os import utils.getdeviceevents +import utils.allowlist +import urllib3 + +urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) dotenv.load_dotenv() @@ -18,12 +22,13 @@ def apivalidation(): def menu(): print("\n--- Main Menu ---") print("1. Get All Events for Single Device") - print("2. Policy Enforcement Readiness") - print("3. Get Device with Highest Blocks in 7 Days") + print("2. Get Execution Histories for Allow List") while True: choice = input("Enter Menu Item: ") if choice == '1': utils.getdeviceevents.devicehistory(url) + if choice == '2': + utils.allowlist.allowlistexechistories(url) if __name__ == "__main__": apivalidation() \ No newline at end of file diff --git a/utils/__pycache__/allowlist.cpython-313.pyc b/utils/__pycache__/allowlist.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f029c4b9dc17a515bd978c618585a58a0ec30fd9 GIT binary patch literal 1837 zcmcgs%}*Og6rcUF*Easbn6E@-K`n_%!Jw#0gQBRUEg#{dDH~T*b{Z}A6qYQz+u1e2 zTqyxI4D@ArFe z#|P197{O@%^T)-nMTGufhCleD%|Rb*9v~H|+$180D@Qob<_Yhu0ufZc6A^KN%Lw}{ zaHhx|#j{0*M}X(xXFz>`a)8wjrpe`Bd8zz6M|%YgB9m8zyw3p50=Ltn2dSbTG02%h zKHz)IvTUaYcuRAs9Ipn{;AP1a^PXX&+6E`)|!~zRf?)h5b`C+z+)e!_cF|VUMy%O@7SHEivRQr#27p zX5>Wu+I@`U73vr9@l5`N@)JG&fBW^SJ1M3D`T~73n@%umQd>?{4*I|KylyTiBZi^eunfJZV`ojC>mRu>F^MZQz}62xrQ(f`)SIIj zfd&_`R>H)gVo7r~8d$+Z|GYwFf*sp39ZUlry0gzNL58D}<7&iR;|CNfn%Xj^LGQg@ zT1%_gz(w~kYXz79sbOWUgE2I$g#}=h#jCius5`DjfL2B%5dht1*J+>KlKmjd!Bf$# z*pS#}^eZ&_S`ZBv7jf~9ZRw_)iBicXu%W!=08#8>bA?g=qKqBap~0ga6_*^#q>^E2 zB@pW3RhNdaSzg9Ob0Lvt+t@5oNjL4XO9cifmCDPu0||sOF{li$y^J(yYZV4V7+(^C ziH2>BIQX`Q8ud7XHdY#kh7Kj@kQKm7c$JCmgfgy3>7}G?wX#O!iY|$tm2&!+a}hEW)#YQ)qq{0 zt*@-tDoL`CiOFz2f!ez2`^Y_;tPTZfUE$+0O zdoJ+tKy~b8043U=#=CxvcWnh8$&Z8EbGPd8&mcCLe%N`xvo`+2^yc(Vvafo*(b8I- zZX}wk6MJpSR_CMZ_3lf*$o1}#$I0Ec$$OD?X-|%Bq&I>ar1oZQW%KRY8{4t-yK<%> zr=QAa>+;#!&8^|C| z;p^wVn%RrD{YPnu&f0K2(Yy8KZsO9q2s+kF8&~Qv<%!(=mw=M*9zc0fMxruYF6`>? z!F9j^Im0IQALsSpCqxF>egGyaUs<+FWdmO&7r|tg&S{0|xxjJUGZgoJk!L7$AfDlR HytuysYC@uL literal 0 HcmV?d00001 diff --git a/utils/__pycache__/getdeviceevents.cpython-313.pyc b/utils/__pycache__/getdeviceevents.cpython-313.pyc index 3b24f9449b6e03d2f73ede3b655726b88c0a20a0..a7fc47120640b6378923c88b1fc28fff7e1a299f 100644 GIT binary patch delta 19 ZcmX>iengz>GcPX}0}!y>*~qnv8vr*61vmfz delta 19 ZcmX>iengz>GcPX}0}yPxu#syQHvl^M1>OJv diff --git a/utils/allowlist.py b/utils/allowlist.py new file mode 100644 index 0000000..c8bbbdb --- /dev/null +++ b/utils/allowlist.py @@ -0,0 +1,33 @@ +import datetime +import requests +import json +import os + +def allowlistexechistories(url): + endpoint = url + '/v1/group' + print("[+] Grabbing All Policies") + 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(f"{index}. {list['name']}") + policiesnames.append(list['name']) + policyids.append(list['groupid']) + choice = input("Select Policy Group: ") + choice = int(choice) - 1 + endpoint = url + '/v1/logging/exechistories' + payload_dict = { + "type":[1,2,6,7], + "policy": [policiesnames[choice]] + } + payload = json.dumps(payload_dict) + print(payload) + response = requests.request("POST", endpoint, headers=headers, data=payload, verify=False) + parse_text = json.loads(response.text) + for item in parse_text['response']['exechistories']: + print(item['checkpoint']) \ No newline at end of file -- 2.34.1 From 8dfd7412f0a217e093777f6c40f0fe92f4d67695 Mon Sep 17 00:00:00 2001 From: Driven-Element <129630760+Driven-Element@users.noreply.github.com> Date: Tue, 19 Aug 2025 21:15:31 -0400 Subject: [PATCH 2/4] First Draft --- utils/hashfunctions.py | 84 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 utils/hashfunctions.py diff --git a/utils/hashfunctions.py b/utils/hashfunctions.py new file mode 100644 index 0000000..ff57f43 --- /dev/null +++ b/utils/hashfunctions.py @@ -0,0 +1,84 @@ +import pandas as pd +import requests +import os + +def aggregateHashes(executions_json: dict) -> pd.DataFrame: + """ + Takes the executions, aggregates all the data with sha256 as primary, then returns aggregated dataframe + """ + + exechistories = executions_json.get("response", {}).get("exechistories", []) + df = pd.DataFrame(exechistories) + + if df.empty: + return df + + # Aggregate by sha256 - keep all entries in lists + agg_df = df.groupby("sha256").agg(lambda x: list(x)).reset_index() + + return agg_df + +def augmentAggregatedHashes(url, agg_df: pd.DataFrame) -> pd.DataFrame: + """ + Takes output of aggregatedHashes, queries API for those hashes, flattens response while keeping one row per hash, + aggregate applications and baselines into lists, then merges results back into agg_df to create a + """ + endpoint = url + 'v1/hash/query' + payload = agg_df['sha256'].tolist() + headers = {"X-APIKey": os.getenv('APIKEY')} + + response = requests.post(endpoint, headers=headers, json=payload, verify=False) + data = response.json() + results = data.get("response", {}).get("results", []) + + rows = [] + for res in results: + row = {"sha256": res.get("sha256"), "result": res.get("result")} + + if "data" in res: + d = res["data"] + for key in ["filename", "filepath", "description", "filesize", "md5", + "productname", "productversion", "publisher", "createtime", "modtime", + "sha128", "sha384", "sha512", "datetime"]: + row[key] = d.get(key) + + row["applications"] = d.get("applications", []) + row["baselines"] = d.get("baselines", []) + + reputation = d.get("reputation", {}) + for k, v in reputation.items(): + row[f"reputation_{k}"] = v + + rows.append(row) + + df_api = pd.DataFrame(rows) + + aug_df = agg_df.merge(df_api, on="sha256", how="left") + + return aug_df + +def categorize_hashes(aug_df: pd.DataFrame, threat_tolerance: int, untrusted_publishers: list): + """ + Categorize hashes into needsreview, approved, and remaining based on publisher and threat level. + + """ + if untrusted_publishers is None: + untrusted_publishers = [] + + # Flatten threatlevel from nested reputation dict + df = df.copy() + df["threatlevel"] = df["reputation"].apply(lambda x: x.get("threatlevel") if pd.notnull(x) else None) + + # Masks for each category + mask_needsreview = (df["publisher"] == "Not Signed") & (df["threatlevel"] > threat_tolerance) + mask_approved = (df["publisher"] != "Not Signed") & (~df["publisher"].isin(untrusted_publishers)) + + # Create DataFrames for each category + needsreview_df = df[mask_needsreview].drop(columns=["threatlevel"]) + approved_df = df[mask_approved].drop(columns=["threatlevel"]) + remaining_df = df[~(mask_needsreview | mask_approved)].drop(columns=["threatlevel"]) + + return needsreview_df, approved_df, remaining_df + +def approve_hashes(approved_df: pd.DataFrame): + pass \ No newline at end of file -- 2.34.1 From e8b267753defb69d5b40d01fe74717ee80314164 Mon Sep 17 00:00:00 2001 From: brotoskyj Date: Wed, 20 Aug 2025 14:54:31 -0400 Subject: [PATCH 3/4] Checkpoint Stomper --- utils/__pycache__/allowlist.cpython-313.pyc | Bin 1837 -> 2666 bytes .../getdeviceevents.cpython-313.pyc | Bin 3012 -> 3012 bytes utils/allowlist.py | 24 ++++++++++++++++-- 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/utils/__pycache__/allowlist.cpython-313.pyc b/utils/__pycache__/allowlist.cpython-313.pyc index f029c4b9dc17a515bd978c618585a58a0ec30fd9..3f3e0380652072ed39756defd178e79fd83f757e 100644 GIT binary patch delta 1096 zcmaJ=O>7cD6rN?5WuZX%t(0Qfn%J^!Qc9r}T5UWS^`K4chS=DHQdkSv0-0TnO*Awf z4Dn*g#)P96O*nYayC<(6B$C=}duU8N;7N-|56_FS(_G% z36Ztud7XZ0C|f<$RC#P7Aktoh^=p*CG(fwUq=0l5M<`8plcC=#?)Y&8;%%tS(~kt(e*%4R{*Ov$vAyjRF)a*DP9yZ){Sl%zM)`VOg@}Cu%|e&3j<8m*ib{aM#J#oPkdc;irLdv0K-Enba(MvsmXw z_Bl%_wLMp}4%a#VKMp&b|Imo+(IyFuk_=#H^}6CB^?h-?w#OS@AJSw#aF_%aSUf;T`?p(F@WK%Wvz0{^wanV7a_2Ylsr&2F0w%8u!5fvKTt8@v aqV`aK>+9QdAmbt9r}*Y2%1lkP-2VW%x!&^t delta 334 zcmaDQvX+nUGcPX}0}u$kSd!7sGLcV$QEQ^QHcL9ACiBD?8Kw==k3h6Th3kEVq zWU&HO>N5rl=`l^VV>+xW$iR@t6U_+H4+5b)!6G0PrceQ-wX|fb?1J%7`0ugK=f*nX`GTmY+sVqpHoW~;0bBilEBQ-g@AU`v&Wbz7@ z8a9x!BF@QLtb4iiflLs$IBfDq)~BM6_$6)#D_j)TxFDqYk(F75t-C%vZ!_?N5%+Bd&d)5;j7ngA0Db{Vr2qf` diff --git a/utils/__pycache__/getdeviceevents.cpython-313.pyc b/utils/__pycache__/getdeviceevents.cpython-313.pyc index a7fc47120640b6378923c88b1fc28fff7e1a299f..f039bb8a4172372fc09e9dfe1dd8fabc544dc6c9 100644 GIT binary patch delta 20 acmX>ieng!6GcPX}0}$vwS-O#XCpQ2;S_TgQ delta 20 acmX>ieng!6GcPX}0}!y>S+bFPCpQ2-1_j*! diff --git a/utils/allowlist.py b/utils/allowlist.py index c8bbbdb..edb3c6f 100644 --- a/utils/allowlist.py +++ b/utils/allowlist.py @@ -22,7 +22,8 @@ def allowlistexechistories(url): choice = int(choice) - 1 endpoint = url + '/v1/logging/exechistories' payload_dict = { - "type":[1,2,6,7], + "type":[1], + "checkpoint":"68a153c23963989b484541b4", "policy": [policiesnames[choice]] } payload = json.dumps(payload_dict) @@ -30,4 +31,23 @@ def allowlistexechistories(url): response = requests.request("POST", endpoint, headers=headers, data=payload, verify=False) parse_text = json.loads(response.text) for item in parse_text['response']['exechistories']: - print(item['checkpoint']) \ No newline at end of file + print(item['checkpoint']) + print(item['datetime']) + print(item['hostname']) + print(item['filename']) + # checkpoint_stomper(item['checkpoint'], endpoint, headers, policiesnames[choice]) + +def checkpoint_stomper(checkpoint, endpoint, headers, policyname): + print(checkpoint) + payload_dict = { + "type":[1,2,6,7], + "checkpoint": checkpoint, + "policy":[policyname] + } + payload = json.dumps(payload_dict) + response = requests.request("POST", endpoint, headers=headers, data=payload, verify=False) + parse_text = json.loads(response.text) + # Send Whole JSON Response + for item in parse_text['response']['exechistories']: + filenametest.add(item['filename']) + -- 2.34.1 From df75658bfb27db5fd5e7e7f44504acdd689f7e84 Mon Sep 17 00:00:00 2001 From: brotoskyj Date: Wed, 20 Aug 2025 14:55:56 -0400 Subject: [PATCH 4/4] Fixed Error --- utils/allowlist.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/allowlist.py b/utils/allowlist.py index edb3c6f..bb4d75a 100644 --- a/utils/allowlist.py +++ b/utils/allowlist.py @@ -49,5 +49,5 @@ def checkpoint_stomper(checkpoint, endpoint, headers, policyname): parse_text = json.loads(response.text) # Send Whole JSON Response for item in parse_text['response']['exechistories']: - filenametest.add(item['filename']) + print("test") -- 2.34.1