Skip to content

Commit 0972f1b

Browse files
authored
### Fixed (#55)
- **MITRE ATT&CK**: Updated for detection strategies Issue #53 - Fixed hardcoding issues #### New Data Sources - **MITRE ATLAS** - Parsing script: `download_threat_information/parsing_scripts/parse_atlas.py` - Build script: `offense/build_atlas.py` - **Campaigns**: Support for threat campaign data - Build script: `offense/build_campaigns.py` - **CAR (Cyber Analytics Repository)**: Integration of MITRE CAR analytics - Analytics processing: `mitigations/ecar_analytics.py` - **KEV (Known Exploited Vulnerabilities)**: CISA's KEV catalog integration - KEV schema and edge collections linking KEV entries to CVEs #### Schema Enhancements - Added schemas for new data types: #### Infrastructure Improvements - Updated Docker and docker-compose configuration - Enhanced bootstrap script with improved CVE processing (year-by-year to avoid crashes)
1 parent 8a18686 commit 0972f1b

File tree

54 files changed

+3063
-190
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

54 files changed

+3063
-190
lines changed

Dockerfile

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
# This stage is used to get pre-compiled arangodb client binaries.
2-
FROM arangodb:3.8.1 AS deps
2+
FROM arangodb:3.12.4 AS deps
33

44
# This stage installs dependencies to build and load BRON into arangodb.
5-
FROM python:3.8-slim AS runtime
5+
FROM python:3.12-slim AS runtime
66

77
RUN apt-get update && \
88
apt-get upgrade -y && \
@@ -14,6 +14,8 @@ RUN pip3 install -r requirements.txt
1414

1515
COPY --from=deps /usr/bin/arangoimport /usr/bin/.
1616
COPY --from=deps /etc/arangodb3/arangoimport.conf /etc/arangodb3/arangoimport.conf
17+
COPY --from=deps /usr/bin/arangoexport /usr/bin/.
18+
COPY --from=deps /etc/arangodb3/arangoexport.conf /etc/arangodb3/arangoexport.conf
1719
COPY . .
1820

1921
ENV DATA_DIR=$dir

README.md

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
1+
[![BRON](docs/figures/bron-logo.png)](docs/figures/brom-logo.png)
2+
13
# BRON - Link and evaluate public threat and mitigation data for Cyber Hunting
24

5+
**TODO** update figures
6+
37
[![BRON February 2023](docs/figures/BRON_drawing.png)](docs/figures/BRON_drawing.png)
48

5-
Threat data from [MITRE ATT&CK](https://attack.mitre.org/), [CAPEC](https://capec.mitre.org/), [CWE](https://cwe.mitre.org/) , [CVE](https://nvd.nist.gov), [MITRE Engage](https://engage.mitre.org/), [MITRE D3FEND](https://d3fend.mitre.org/), [MITRE CAR](https://car.mitre.org/) and , [exploitdb](https://exploit-db.com/) data sources are linked together in a graph called BRON. The data types are linked with bidirectional edges. Orange nodes in figure have "offensive" information. Blue nodes in figure are "defensive" information.
9+
Threat data from [MITRE ATT&CK](https://attack.mitre.org/), [CAPEC](https://capec.mitre.org/), [CWE](https://cwe.mitre.org/) , [CVE](https://nvd.nist.gov), [MITRE Engage](https://engage.mitre.org/), [MITRE D3FEND](https://d3fend.mitre.org/), [MITRE CAR](https://car.mitre.org/) , [exploitdb](https://exploit-db.com/) , [MITRE ATLAS](https://atlas.mitre.org/) data sources are linked together in a graph called BRON. The data types are linked with bidirectional edges. Orange nodes in figure have "offensive" information. Blue nodes in figure are "defensive" information.
610

711
## Deployment
812
See [graph_db](graph_db) for a public instance of graph data base implementaion [bron.alfa.csail.mit.edu](http://bron.alfa.csail.mit.edu:8529)
@@ -17,7 +21,7 @@ sudo apt-key add - < Release.key
1721
echo 'deb https://download.arangodb.com/arangodb310/DEBIAN/ /' | sudo tee /etc/apt/sources.list.d/arangodb.list
1822
sudo apt-get install apt-transport-https
1923
sudo apt-get update
20-
sudo apt-get install arangodb3=3.10.2-1
24+
sudo apt-get install arangodb3=3.12.4
2125
2226
# Python venv
2327
python3 -m venv ~/.venvs/BRON-dev
@@ -86,31 +90,33 @@ tail -n 1 build_bron.log
8690
This should produce a `build_bron.log` file that ends with `END building BRON`.
8791

8892
## Tutorials
89-
Tutorials are available in the `tutorials` folder on the following topics:
90-
- Using BRON in Arangodb, `tutorials/using_bron_graphdb.py`
93+
Tutorials are available in the `tutorials` folder
9194

9295

9396
## Usage
9497
```
95-
usage: build_bron.py [-h] --username USERNAME --password PASSWORD --ip IP [--clean] [--clean_local_files] [--delete_mitigations] [--no_download] [--no_parsing] [--no_building] [--no_arangodb]
96-
[--no_mitigations] [--no_validation]
98+
usage: build_bron.py [-h] --username USERNAME --password PASSWORD --ip IP [--clean] [--clean_local_files] [--no_download] [--no_parsing] [--no_building] [--no_atlas] [--only_recent] [--no_arangodb] [--no_mitigations] [--no_validation] [--start_year START_YEAR] [--end_year END_YEAR]
9799
98100
Build BRON in Arango DB
99101
100-
optional arguments:
102+
options:
101103
-h, --help show this help message and exit
102104
--username USERNAME DB username
103105
--password PASSWORD DB password
104106
--ip IP DB IP address
105107
--clean Clean all files and db
106108
--clean_local_files Clean all local files
107-
--delete_mitigations Clean all mitigation collections
108109
--no_download Do not download data
109110
--no_parsing Do not parse data
110111
--no_building Do not build BRON
112+
--no_atlas Do not add ATLAS
113+
--only_recent Only recent CVEs
111114
--no_arangodb Do not create and import to Arangodb
112115
--no_mitigations Do not create and import mitigations
113116
--no_validation Do not validate entries imported to the ArangoDb
117+
--start_year START_YEAR
118+
Start year
119+
--end_year END_YEAR End year
114120
```
115121

116122
## Structure of BRON
@@ -130,3 +136,17 @@ arXiv report: [https://arxiv.org/abs/2010.00533](https://arxiv.org/abs/2010.0053
130136
primaryClass={cs.CR}
131137
}
132138
```
139+
140+
DTRAP paper: [https://dl.acm.org/doi/full/10.1145/3615668](https://dl.acm.org/doi/full/10.1145/3615668)
141+
```
142+
@article{hemberg2024enhancements,
143+
title={Enhancements to threat, vulnerability, and mitigation knowledge for cyber analytics, hunting, and simulations},
144+
author={Hemberg, Erik and Turner, Matthew J and Rutar, Nick and O’reilly, Una-May},
145+
journal={Digital Threats: Research and Practice},
146+
volume={5},
147+
number={1},
148+
pages={1--33},
149+
year={2024},
150+
publisher={ACM New York, NY}
151+
}
152+
```

bootstrap.sh

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,21 @@
33
# parse environment
44
ARANGO_ROOT_PASSWORD=$(cat $ARANGO_ROOT_PASSWORD_FILE)
55

6+
# TODO build BRON a year at a time (Docker can run out of memory)
67
# build BRON
7-
python3 tutorials/build_bron.py --username=root --password=${ARANGO_ROOT_PASSWORD} --ip=brondb
8+
9+
YEARS=(2024 2023 2022 2021 2020 2019 2018 2017 2016 2015 2014 2013 2012 2011 2010 2009 2008 2007 2006 2005 2004 2003 2002)
10+
INITIAL_YEAR=2025
11+
END_YEAR=$((INITIAL_YEAR + 1))
12+
echo "++++ Building BRON for ${INITIAL_YEAR} to ${END_YEAR}"
13+
python3 tutorials/build_bron.py --username=root --password=${ARANGO_ROOT_PASSWORD} --ip=brondb --start_year=${INITIAL_YEAR} --end_year=${END_YEAR}
14+
15+
for YEAR in ${YEARS[@]}; do
16+
END_YEAR=$((YEAR + 1))
17+
echo "++++ Building BRON for ${YEAR} to ${END_YEAR}"
18+
python3 tutorials/build_bron.py --username=root --password=${ARANGO_ROOT_PASSWORD} --ip=brondb --start_year=${YEAR} --end_year=${END_YEAR} --no_atlas --no_mitigations
19+
done
20+
21+
echo "++++ Building BRON for final mitigations"
22+
END_YEAR=$((INITIAL_YEAR + 1))
23+
python3 tutorials/build_bron.py --username=root --password=${ARANGO_ROOT_PASSWORD} --ip=brondb --no_arangodb --no_atlas --start_year=$INITIAL_YEAR --end_year=$END_YEAR

docker-compose.yml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
version: "3.5"
21
services:
32
brondb:
3+
platform: linux/arm64
44
container_name: brondb
5-
image: arangodb:3.8.1
5+
image: arangodb:3.12.4
66
environment:
77
ARANGO_ROOT_PASSWORD_FILE: /run/secrets/arango_root_password
88
ports:
@@ -13,6 +13,7 @@ services:
1313
- brondb_data_container:/var/lib/arangodb3
1414
- brondb_apps_data_container:/var/lib/arangodb3-apps
1515
bootstrap:
16+
platform: linux/arm64
1617
container_name: bootstrap
1718
build: .
1819
image: bronbootstrap
@@ -24,6 +25,7 @@ services:
2425
secrets:
2526
- arango_root_password
2627
volumes:
28+
- .:/usr/local/bron
2729
- bootstrap_data_container:/data
2830
links:
2931
- brondb

docs/figures/bron-logo.png

307 KB
Loading

download_threat_information/download_threat_data.py

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@
99
import logging
1010

1111
import requests
12-
12+
import yaml
13+
from tqdm import tqdm
1314

1415
# TODO how to check if there are updates, or different URLs
1516
# TODO add some verbosity
@@ -21,10 +22,11 @@
2122
CAPEC_XML_URL = "http://capec.mitre.org/data/xml/capec_latest.xml"
2223
# TODO are there better CAPEC views?
2324
CWE_XML_URL = "http://cwe.mitre.org/data/xml/cwec_latest.xml.zip"
24-
CVE_BASE_URL = "https://nvd.nist.gov/feeds/json/cve/1.1"
25+
CVE_BASE_URL = "https://nvd.nist.gov/feeds/json/cve/2.0"
26+
ATLAS_DATA_URL = "https://raw.githubusercontent.com/mitre-atlas/atlas-data/refs/heads/main/dist/ATLAS.yaml"
2527
LAST_YEAR = datetime.datetime.now().year + 1
2628
FIRST_YEAR = 2002
27-
RECENT_OFFSET = 1
29+
RECENT_OFFSET = 10
2830
CVE_ALL_YEARS = list(map(str, range(FIRST_YEAR, LAST_YEAR)))
2931
CVE_RECENT_YEARS = list(map(str, range(LAST_YEAR - RECENT_OFFSET, LAST_YEAR)))
3032

@@ -36,6 +38,7 @@
3638
"CWE": "raw_CWE.zip",
3739
"CWE_XML": "raw_CWE_xml.zip",
3840
"CVE": "raw_CVE.json.gz",
41+
"ATLAS": "raw_atlas.yaml",
3942
}
4043
BRON_META_DATA_PATH = "bron_meta_data.json"
4144

@@ -73,11 +76,26 @@ def md5(file_path: str) -> str:
7376
return hash_md5.hexdigest()
7477

7578

79+
80+
def _download_atlas():
81+
response = requests.get(ATLAS_DATA_URL)
82+
file_path = os.path.join(OUTPUT_FOLDER, THREAT_DATA_TYPES["ATLAS"])
83+
data = response.text
84+
yaml_data = yaml.safe_load(data)
85+
with open(file_path, "w") as fd:
86+
yaml.safe_dump(yaml_data, fd)
87+
88+
assert os.path.exists(file_path)
89+
_write_meta_data(threat_data_type="ATLAS", file_path=file_path)
90+
logging.info(f"Downloaded ATLAS from {ATLAS_DATA_URL} to {file_path}")
91+
92+
7693
def _download_attack():
7794
response = requests.get(ENTERPRISE_ATTACK_URL)
7895
file_path = os.path.join(OUTPUT_FOLDER, THREAT_DATA_TYPES["ATTACK"])
7996
with open(file_path, "w") as fd:
80-
json.dump(response.json(), fd)
97+
json_data = response.json()
98+
json.dump(json_data, fd)
8199

82100
# TODO make sure to remove file before for this assert to be meaningful (or assert timestamps)
83101
assert os.path.exists(file_path)
@@ -110,10 +128,11 @@ def _download_cwe_xml():
110128

111129

112130
def _download_cve(cve_years):
113-
combined_cve = {"CVE_Items": []}
114-
for year in cve_years:
131+
logging.info(f"Begin download CVEs from {CVE_BASE_URL} for {cve_years}")
132+
combined_cve = {"vulnerabilities": []}
133+
for year in tqdm(cve_years, desc="Downloading CVEs"):
115134
# Download CVE data for each year
116-
cve_url = os.path.join(CVE_BASE_URL, f"nvdcve-1.1-{year}.json.gz")
135+
cve_url = os.path.join(CVE_BASE_URL, f"nvdcve-2.0-{year}.json.gz")
117136
response = requests.get(cve_url, stream=True)
118137
year_file_path = os.path.join(OUTPUT_FOLDER, f"raw_CVE_{year}.json.gz")
119138
with open(year_file_path, "wb") as fd:
@@ -124,7 +143,7 @@ def _download_cve(cve_years):
124143
cve_path = os.path.join(OUTPUT_FOLDER, f"raw_CVE_{year}.json.gz")
125144
with gzip.open(cve_path, "rt", encoding="utf-8") as f:
126145
cve_data = json.load(f)
127-
combined_cve["CVE_Items"].extend(cve_data["CVE_Items"])
146+
combined_cve["vulnerabilities"].extend(cve_data["vulnerabilities"])
128147
json_string = json.dumps(combined_cve)
129148
encoded = json_string.encode("utf-8")
130149
file_path = os.path.join(OUTPUT_FOLDER, THREAT_DATA_TYPES["CVE"])
@@ -141,6 +160,7 @@ def main(cve_years) -> None:
141160
os.makedirs(OUTPUT_FOLDER)
142161
logging.info(f"Created {OUTPUT_FOLDER}")
143162

163+
_download_atlas()
144164
_download_attack()
145165
_download_capec_xml()
146166
_download_cwe_xml()
@@ -196,6 +216,8 @@ def clean():
196216
_download_cwe_xml()
197217
elif args.threat_data_type == "CVE":
198218
_download_cve(cve_years_)
219+
elif args.threat_data_type == "ATLAS":
220+
_download_atlas()
199221
else:
200222
raise Exception(f"Bad threat data type: {args.threat_data_type}")
201223
else:

0 commit comments

Comments
 (0)