Skip to content

Commit

Permalink
New repo, added base_url into config
Browse files Browse the repository at this point in the history
  • Loading branch information
dakotaPPP committed Aug 9, 2024
1 parent dc067d2 commit bf2d69e
Show file tree
Hide file tree
Showing 8 changed files with 141 additions and 1 deletion.
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
test
logs
src/__pycache__
config.json
build
dist
*.spec
14 changes: 13 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,14 @@
# Qualys-MITRE-ATTACK-Grabber
Pulls data from Qualys' API and is grouped by the MITRE ATT&CK matrix. Creates a csv file that allows for easy data visualization via a pivot table. Created per Larry Lotspeich's, Threat and Vulnerability Management Manager at Entergy, request.
Pulls data from Qualys' API and is grouped by the MITRE ATT&CK matrix. Creates a csv file that allows for easy data visualization via a pivot table.<br />
Created per Larry Lotspeich's, Threat and Vulnerability Management Manager at Entergy, request.

# Setup
1. Download the latest release package or just download the source code! <br />
**NOTE:** If running via the .exe provided be sure to place this .exe in it's own folder as the program populates and reads from it's own directories <br />
**NOTE:** If running via the source code, be sure to download all packages used (for quick install of all packages download the `requirements.txt` in the `src` folder)
2. After program finishes a csv should populate in the directory `/logs/CSVs` <br />
The CSV naming format is `tactic_technique_counts_YYYY-MM-DDThh-mm-ss.csv`
3. Follow the steps below to create the pivot table <br />
`Open the CSV in excel -> Select all the data (header included) -> (Insert tab -> PivotTable) -> Select where you want the table -> Check mark all the PivotTable Fields`

Pretty straight forward thank you :D
Binary file added img/mitre_python_logo.ico
Binary file not shown.
17 changes: 17 additions & 0 deletions src/ExportToEXCEL.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import csv

def saveCSVData(input, fileName):
data = []
for entry in input:
tactic = entry['tactic']
techniques = entry['techniques']
if not techniques:
data.append({'Tactic': tactic, 'Technique': 'No Instances', 'Count': 0})
for technique in techniques:
data.append({'Tactic': tactic, 'Technique': technique['name'], 'Count': int(technique['count'])})

header = data[0].keys()
with open(fileName, 'w', newline='') as file:
writer = csv.DictWriter(file, fieldnames=header)
writer.writeheader()
writer.writerows(data)
23 changes: 23 additions & 0 deletions src/MitreMatrixUpdate.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import requests
from mitreattack.stix20 import MitreAttackData
from os import remove, rmdir, mkdir

def updateJson():
response = requests.get(url="https://raw.githubusercontent.com/mitre/cti/master/enterprise-attack/enterprise-attack.json")
mkdir("temp")
f = open('temp/enterprise-attack.json', 'w')
f.write(response.text)
f.close()

def getTactics():
updateJson()
mitre_attack_data = MitreAttackData("temp/enterprise-attack.json")
output = []
tactics = mitre_attack_data.get_tactics(remove_revoked_deprecated=True)
for tactic in tactics:
for reference in tactic["external_references"]:
if reference["source_name"] == "mitre-attack":
output.append({"id":reference["external_id"],"name":tactic["name"]})
remove("temp/enterprise-attack.json")
rmdir("temp")
return output
44 changes: 44 additions & 0 deletions src/QualysAPIRequest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import requests
import json
from requests.auth import HTTPBasicAuth
from time import sleep

def getTechniqueCounts(tacticID):
with open("config/config.json") as config_file:
config = json.load(config_file)
username = config["username"]
password = config["password"]
BASE_URL = config["base_url"]

if username == "API_USERNAME" or password == "API_PASSWORD" or BASE_URL == "https://BASE_URL":
raise RuntimeError("Please update the default config/config.json file!")
url = f'{BASE_URL}/portal-front/rest/assetview/1.0/assetvuln/pivotList?limit=200&offset=0&query=&groupByPivot=VM&havingQuery=(vulnerabilities.typeDetected:Confirmed and (vulnerabilities.severity:3 or vulnerabilities.severity:4 or vulnerabilities.severity:5) and vulnerabilities.nonRunningKernel:FALSE ) and (vulnerabilities.typeDetected: [Confirmed, Potential] and vulnerabilities.found: TRUE and vulnerabilities.disabled: FALSE and vulnerabilities.ignored: FALSE)&groupBy=vulnerabilities.vulnerability.mitre.attack.tactic.id:{tacticID}&invalidateCache=true'
output = []

# Sometimes qualys will give us a landing page saying "Please wait"
# When this occurs just retry 3 times and if it still doesn't work then return
retry_count = 3
delay = 30

for attempt in range(retry_count):
response = requests.get(url, auth=HTTPBasicAuth(username, password))
if response.status_code == 200:
if("</html>" in response.text):
print(f"Rate limited, retrying...")
sleep(delay)
delay *= 2
continue

data = response.json()
if data["total"] == 0:
return []

for technique in data["data"]:
output.append({"name":technique["vulnerabilities.vulnerability.mitre.attack.technique.name"],"count":technique["count"]})

return output
else:
print(f"Error: {response.status_code} - {response.text}")
return []
print("Retry limit reached")
return []
37 changes: 37 additions & 0 deletions src/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
from ExportToEXCEL import saveCSVData
from MitreMatrixUpdate import getTactics
from QualysAPIRequest import getTechniqueCounts
import json
from datetime import datetime
from os import path, makedirs

if not path.exists("logs"):
makedirs("logs")
if not path.exists("logs/CSVs"):
makedirs("logs/CSVs")

if not path.exists("config"):
makedirs("config")
f = open("config/config.json", "w")
f.write("{\"username\":\"API_USERNAME\", \"password\":\"API_PASSWORD\", \"base_url\":\"https://BASE_URL\"}")
f.close()
raise RuntimeError("Please update the default config/config.json file!")

#I don't like that I've had to hardcode this but it helps prevent rate limiting
mitreTacticsInVMDR = ["Initial Access", "Execution", "Persistence", "Privilege Escalation", "Defense Evasion", "Credential Access", "Lateral Movement", "Collection", "Impact"]
data = []
for tactic in getTactics():
if tactic["name"] in mitreTacticsInVMDR:
data.append({"tactic":tactic["name"], "techniques":getTechniqueCounts(tactic["id"])})
print(data[-1])

now = datetime.now()
formatted_now = now.strftime("%Y-%m-%dT%H-%M-%S")

f = open("logs/data_log.txt", "a")
f.write(f"\n\nTIMEDATE: {formatted_now}\n")
f.write(json.dumps(data))
f.close()

filename = f"logs/CSVs/tactic_technique_counts_{formatted_now}.csv"
saveCSVData(data, filename)
Binary file added src/requirements.txt
Binary file not shown.

0 comments on commit bf2d69e

Please sign in to comment.