├── pyattck ├── utils │ ├── __init__.py │ ├── version.py │ ├── utils.py │ ├── exceptions.py │ ├── interactive.py │ ├── logger.py │ ├── menu.py │ └── layout.py ├── __init__.py ├── cli.py ├── base.py ├── data │ └── logging.yml ├── preattck.py ├── mobile.py ├── ics.py ├── enterprise.py ├── configuration.py └── attck.py ├── docs-requirements.txt ├── setup.cfg ├── .gitattributes ├── test-requirements.txt ├── docs ├── .DS_Store ├── _static │ └── style.css ├── _templates │ └── layout.html ├── Makefile ├── control.md ├── tactic.md ├── mitigation.md ├── malware.md ├── make.bat ├── actor.md ├── attck.md ├── technique.md ├── tools.md ├── images │ ├── macos_support.svg │ ├── ubuntu_support.svg │ └── windows_support.svg ├── configuration.md ├── preattck.md ├── ics.md ├── conf.py ├── mobile.md ├── enterprise.md └── index.md ├── requirements.txt ├── images ├── pyattck_interactive_menu.gif ├── code_coverage.svg ├── macos_support.svg ├── ubuntu_support.svg └── windows_support.svg ├── .github ├── workflows │ ├── constraints.txt │ ├── main.yml │ └── quality.yml └── ISSUE_TEMPLATE │ ├── feature_request.md │ └── bug_report.md ├── attck_to_nist_controls.json ├── generated_attck_data.json ├── pylama.ini ├── .flake8 ├── tests ├── test_tactics.py ├── test_mitigation.py ├── test_malware.py ├── conftest.py ├── test_campaigns.py ├── test_actors.py ├── test_tools.py ├── test_attck.py ├── test_techniques.py └── test_configuration.py ├── Dockerfile ├── CONTRIBUTING.md ├── Makefile ├── make.bat ├── pyproject.toml ├── setup.py ├── LICENSE.md ├── .gitignore ├── CHANGELOG.md ├── README.md └── bin └── test.py /pyattck/utils/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs-requirements.txt: -------------------------------------------------------------------------------- 1 | recommonmark 2 | sphinx 3 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | description-file = README.md -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.json filter=lfs diff=lfs merge=lfs -text 2 | -------------------------------------------------------------------------------- /test-requirements.txt: -------------------------------------------------------------------------------- 1 | pytest 2 | pylama==7.7.1 3 | coverage 4 | Pillow>=6.2.2 -------------------------------------------------------------------------------- /docs/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/swimlane/pyattck/HEAD/docs/.DS_Store -------------------------------------------------------------------------------- /docs/_static/style.css: -------------------------------------------------------------------------------- 1 | .wy-nav-content { 2 | max-width: 1200px !important; 3 | } -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | requests 2 | PyYaml>=5.4.1 3 | fire==0.3.1 4 | attrs==21.4.0 5 | pyattck-data>=2.5.0 -------------------------------------------------------------------------------- /pyattck/utils/version.py: -------------------------------------------------------------------------------- 1 | __version_info__ = (7, 1, 0) 2 | __version__ = ".".join(map(str, __version_info__)) 3 | -------------------------------------------------------------------------------- /images/pyattck_interactive_menu.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/swimlane/pyattck/HEAD/images/pyattck_interactive_menu.gif -------------------------------------------------------------------------------- /.github/workflows/constraints.txt: -------------------------------------------------------------------------------- 1 | pip==22.1.2 2 | nox==2022.1.7 3 | nox-poetry==1.0.0 4 | poetry==1.3.2 5 | virtualenv==20.14.1 6 | -------------------------------------------------------------------------------- /pyattck/__init__.py: -------------------------------------------------------------------------------- 1 | from .attck import Attck 2 | from .configuration import Configuration, Options 3 | from .utils.version import __version__ 4 | -------------------------------------------------------------------------------- /attck_to_nist_controls.json: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:95ba36bfc291bce2c584ebb585520e2e15af2b9195b205ecd2e10c3be397274a 3 | size 9105826 4 | -------------------------------------------------------------------------------- /generated_attck_data.json: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:da5ce48f76b0780b62c268d62b163595800a2cb0f5e4a59ee4064614a82bb20c 3 | size 31467191 4 | -------------------------------------------------------------------------------- /pyattck/cli.py: -------------------------------------------------------------------------------- 1 | import fire 2 | 3 | from pyattck import Attck 4 | 5 | 6 | def main(args=None): 7 | fire.Fire(Attck) 8 | 9 | 10 | if __name__ == "__main__": 11 | main() 12 | -------------------------------------------------------------------------------- /docs/_templates/layout.html: -------------------------------------------------------------------------------- 1 | {% extends "!layout.html" %} 2 | {% block extrahead %} 3 | 4 | {% endblock %} -------------------------------------------------------------------------------- /pylama.ini: -------------------------------------------------------------------------------- 1 | [pylama] 2 | format = pylint 3 | skip = */.tox/*,*/.env/* 4 | linters = pylint,mccabe 5 | ignore = F0401,C0111,E731 6 | 7 | [pylama:pyflakes] 8 | builtins = _ 9 | 10 | [pylama:pycodestyle] 11 | max_line_length = 120 12 | 13 | [pylama:pylint] 14 | max_line_length = 120 15 | disable = R -------------------------------------------------------------------------------- /.flake8: -------------------------------------------------------------------------------- 1 | [flake8] 2 | max-line-length = 120 3 | exclude = 4 | __init__.py 5 | extend-ignore = 6 | F841 #local variable 'e' is assigned to but never used 7 | per-file-ignores = 8 | # F401: Module imported by unused (non-implicit modules) 9 | # TC002: Move third-party import '...' into a type-checking block 10 | __init__.py:F401,TC002, 11 | logger.py:F841, 12 | configuration.py:E501, 13 | attck.py:E501 14 | base.py:E501 15 | -------------------------------------------------------------------------------- /tests/test_tactics.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | 4 | @pytest.mark.parametrize("target_attribute", ["enterprise", "mobile", "preattack", "ics"]) 5 | def test_tactics_have_techniques(attck_fixture, target_attribute): 6 | """ 7 | All MITRE ATT&CK Tactics should have Techniques 8 | 9 | Args: 10 | attck_fixture ([type]): our default MITRE ATT&CK JSON fixture 11 | """ 12 | for tactic in getattr(attck_fixture, target_attribute).tactics: 13 | assert getattr(tactic, "techniques") 14 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.6-alpine 2 | 3 | COPY requirements.txt / 4 | 5 | RUN apk add --update --no-cache g++ gcc libffi libxslt-dev python2-dev python3-dev libffi-dev openssl-dev 6 | RUN apk add --no-cache jpeg-dev zlib-dev 7 | RUN apk add --no-cache --virtual .build-deps build-base linux-headers 8 | RUN pip install -r /requirements.txt 9 | 10 | ENV TZ="America/Chicago" 11 | 12 | COPY . /app 13 | 14 | WORKDIR /app 15 | 16 | RUN export PYTHONPATH=/app:$PYTHONPATH 17 | RUN python setup.py install 18 | 19 | CMD [ "python", "/app/bin/test.py" ] -------------------------------------------------------------------------------- /tests/test_mitigation.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | 4 | @pytest.mark.parametrize("target_attribute", ["enterprise", "mobile", "ics"]) 5 | def test_mitigation_have_techniques(attck_fixture, target_attribute): 6 | """ 7 | Some MITRE ATT&CK Mitigation should have Techniques 8 | 9 | Args: 10 | attck_fixture ([type]): our default MITRE ATT&CK JSON fixture 11 | """ 12 | for mitigation in getattr(attck_fixture, target_attribute).mitigations: 13 | if mitigation.techniques: 14 | assert getattr(mitigation, "techniques") 15 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Swimlane welcomes contributions to our projects. Please follow these instructions on how to submit your contribution! 4 | 5 | - Fork this repository 6 | - Add your changes to your fork 7 | - Add tests for your changes 8 | - Review your changes, running any linting and tests to be sure they pass 9 | - Create a pull request in this repository with your changes 10 | - Be sure to outline what you are accomplishing with your changes and follow any template if applicable 11 | 12 | Please be patient with your request! We value your contribution and will try to find time to review your pull request when we can. -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | SOURCEDIR = . 8 | BUILDDIR = _build 9 | 10 | # Put it first so that "make" without argument is like "make help". 11 | help: 12 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 13 | 14 | .PHONY: help Makefile 15 | 16 | # Catch-all target: route all unknown targets to Sphinx using the new 17 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 18 | %: Makefile 19 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | SOURCEDIR = . 8 | BUILDDIR = _build 9 | 10 | # Put it first so that "make" without argument is like "make help". 11 | help: 12 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 13 | 14 | .PHONY: help Makefile 15 | 16 | # Catch-all target: route all unknown targets to Sphinx using the new 17 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 18 | %: Makefile 19 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) -------------------------------------------------------------------------------- /docs/control.md: -------------------------------------------------------------------------------- 1 | # Control 2 | 3 | This documentation provides details about Control class within the `pyattck` package. 4 | 5 | > The `Control` object is based on the following [data model](https://github.com/swimlane/pyattck-data-models/blob/main/src/pyattck_data_models/control.py) 6 | 7 | The `Control` class provides detailed information about compliance controls related to a techniques within the MITRE ATT&CK Framework. 8 | 9 | > Currently the `Control` class only supports data for NIST 800-53 10 | 11 | ## Control Class 12 | 13 | ```eval_rst 14 | .. autoclass:: pyattck_data_models.control.Control 15 | :members: 16 | :undoc-members: 17 | :show-inheritance: 18 | ``` 19 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. -------------------------------------------------------------------------------- /docs/tactic.md: -------------------------------------------------------------------------------- 1 | # Tactic 2 | 3 | This documentation provides details about the `Tactic` class within the `pyattck` package. 4 | 5 | > The `Tactic` object is based on the following [data model](https://github.com/swimlane/pyattck-data-models/blob/main/src/pyattck_data_models/tactic.py) 6 | 7 | This class provides information about the tactics (columns) within the MITRE ATT&CK Frameworks. 8 | Additionally, a `Tactic` object allows the user to access additional relationships within the MITRE ATT&CK Frameworks: 9 | 10 | * Techniques found in a specific Tactic (phase) 11 | 12 | ## Tactic Class 13 | 14 | ```eval_rst 15 | .. autoclass:: pyattck_data_models.tactic.Tactic 16 | :members: 17 | :undoc-members: 18 | :show-inheritance: 19 | ``` -------------------------------------------------------------------------------- /pyattck/utils/utils.py: -------------------------------------------------------------------------------- 1 | import os 2 | from pathlib import Path 3 | from urllib.parse import urlparse 4 | 5 | 6 | def get_absolute_path(path: str): 7 | if path.startswith("http") or path.startswith("https"): 8 | return path 9 | else: 10 | try: 11 | if Path(path): 12 | return os.path.abspath(os.path.expanduser(os.path.expandvars(path))) 13 | except Exception as e: 14 | pass 15 | 16 | 17 | def is_path(value: str) -> bool: 18 | try: 19 | Path(value) 20 | except Exception as e: 21 | return False 22 | return True 23 | 24 | 25 | def is_url(value: str) -> bool: 26 | try: 27 | return urlparse(value).scheme in ["http", "https"] 28 | except Exception as e: 29 | pass 30 | return False 31 | -------------------------------------------------------------------------------- /docs/mitigation.md: -------------------------------------------------------------------------------- 1 | # Mitigation 2 | 3 | This documentation provides details about the `Mitigation` class within the `pyattck` package. 4 | 5 | > The `Mitigation` object is based on the following [data model](https://github.com/swimlane/pyattck-data-models/blob/main/src/pyattck_data_models/mitigation.py) 6 | 7 | This class provides detailed information provided by MITRE to help mitigate specific techniques within the MITRE ATT&CK Frameworks. 8 | Additionally, a `Mitigation` object allows the user to access additional relationships within the MITRE ATT&CK Frameworks: 9 | 10 | * Techniques related to a specific set of mitigation suggestions 11 | 12 | ## Mitigation Class 13 | 14 | ```eval_rst 15 | .. autoclass:: pyattck_data_models.mitigation.Mitigation 16 | :members: 17 | :undoc-members: 18 | :show-inheritance: 19 | ``` -------------------------------------------------------------------------------- /docs/malware.md: -------------------------------------------------------------------------------- 1 | # Malware 2 | 3 | This documentation provides details about the `Malware` class within the `pyattck` package. 4 | 5 | > The `Malware` object is based on the following [data model](https://github.com/swimlane/pyattck-data-models/blob/main/src/pyattck_data_models/malware.py) 6 | 7 | This class provides detailed information about identified malware used by actors or impact specific techniques within the MITRE ATT&CK Frameworks. 8 | Additionally, a `Malware` object allows the user to access additional relationships within the MITRE ATT&CK Frameworks: 9 | 10 | * Actor or Group(s) using this malware 11 | * Techniques this malware is used with 12 | 13 | ## Malware Class 14 | 15 | ```eval_rst 16 | .. autoclass:: pyattck_data_models.malware.Malware 17 | :members: 18 | :undoc-members: 19 | :show-inheritance: 20 | ``` -------------------------------------------------------------------------------- /pyattck/utils/exceptions.py: -------------------------------------------------------------------------------- 1 | class GeneratedDatasetException(Exception): 2 | """Raised when unable to retrieve generated datasets and related properties.""" 3 | 4 | pass 5 | 6 | 7 | class ConfigurationException(Exception): 8 | """Raised when unable to load or read the pyattck configuration file.""" 9 | 10 | pass 11 | 12 | 13 | class UnknownFileError(ValueError): 14 | """Raised when the provided file extension is unkown or is not json, yml or yaml.""" 15 | 16 | def __init__(self, provided_value=None, known_values=None): 17 | if provided_value and known_values: 18 | if isinstance(known_values, list): 19 | super().__init__( 20 | f"The provided value {provided_value} is unknown. " 21 | f"Please provide a file path with one of these '{[x for x in known_values]}' extensions." 22 | ) 23 | else: 24 | pass 25 | -------------------------------------------------------------------------------- /make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | pushd %~dp0 4 | 5 | REM Command file for Sphinx documentation 6 | 7 | if "%SPHINXBUILD%" == "" ( 8 | set SPHINXBUILD=sphinx-build 9 | ) 10 | set SOURCEDIR=. 11 | set BUILDDIR=_build 12 | 13 | if "%1" == "" goto help 14 | 15 | %SPHINXBUILD% >NUL 2>NUL 16 | if errorlevel 9009 ( 17 | echo. 18 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 19 | echo.installed, then set the SPHINXBUILD environment variable to point 20 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 21 | echo.may add the Sphinx directory to PATH. 22 | echo. 23 | echo.If you don't have Sphinx installed, grab it from 24 | echo.http://sphinx-doc.org/ 25 | exit /b 1 26 | ) 27 | 28 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% 29 | goto end 30 | 31 | :help 32 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% 33 | 34 | :end 35 | popd 36 | -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | pushd %~dp0 4 | 5 | REM Command file for Sphinx documentation 6 | 7 | if "%SPHINXBUILD%" == "" ( 8 | set SPHINXBUILD=sphinx-build 9 | ) 10 | set SOURCEDIR=. 11 | set BUILDDIR=_build 12 | 13 | if "%1" == "" goto help 14 | 15 | %SPHINXBUILD% >NUL 2>NUL 16 | if errorlevel 9009 ( 17 | echo. 18 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 19 | echo.installed, then set the SPHINXBUILD environment variable to point 20 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 21 | echo.may add the Sphinx directory to PATH. 22 | echo. 23 | echo.If you don't have Sphinx installed, grab it from 24 | echo.http://sphinx-doc.org/ 25 | exit /b 1 26 | ) 27 | 28 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% 29 | goto end 30 | 31 | :help 32 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% 33 | 34 | :end 35 | popd 36 | -------------------------------------------------------------------------------- /images/code_coverage.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | coverage 17 | coverage 18 | 93% 19 | 93% 20 | 21 | 22 | -------------------------------------------------------------------------------- /tests/test_malware.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | 4 | @pytest.mark.parametrize("target_attribute", ["enterprise", "mobile"]) 5 | def test_malware_have_actors(attck_fixture, target_attribute): 6 | """ 7 | All MITRE ATT&CK Malware should have Actors 8 | 9 | Args: 10 | attck_fixture ([type]): our default MITRE ATT&CK JSON fixture 11 | """ 12 | for malware in getattr(attck_fixture, target_attribute).malwares: 13 | if malware.actors: 14 | assert getattr(malware, "actors") 15 | 16 | 17 | @pytest.mark.parametrize("target_attribute", ["enterprise", "mobile", "ics"]) 18 | def test_malware_have_techniques(attck_fixture, target_attribute): 19 | """ 20 | All MITRE ATT&CK Malware should havre techniques 21 | 22 | Args: 23 | attck_fixture ([type]): our default MITRE ATT&CK JSON fixture 24 | """ 25 | for malware in getattr(attck_fixture, target_attribute).malwares: 26 | if malware.techniques: 27 | assert getattr(malware, "techniques") 28 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "pyattck" 3 | version = "7.1.2" 4 | description = "A Python package to interact with the Mitre ATT&CK Frameworks" 5 | authors = ["Swimlane "] 6 | license = "MIT" 7 | readme = "README.md" 8 | homepage = "https://github.com/swimlane/pyattck" 9 | repository = "https://github.com/swimlane/pyattck" 10 | 11 | [tool.poetry.scripts] 12 | pyattck = "pyattck.cli:main" 13 | 14 | [tool.poetry.dependencies] 15 | python = "^3.7" 16 | requests = "^2.27.1" 17 | fire = "^0.4.0" 18 | attrs = "^21.4.0" 19 | pyattck-data = "^2.6.3" 20 | rich = "^12.5.1" 21 | 22 | [tool.poetry.dev-dependencies] 23 | pytest = "^7.1.2" 24 | pylama = "^8.3.8" 25 | coverage = "^6.4.1" 26 | recommonmark = "^0.7.1" 27 | black = "^22.3.0" 28 | isort = "^5.10.1" 29 | flake8 = "^4.0.1" 30 | bandit = "^1.7.4" 31 | Sphinx = "<4.4.0" 32 | importlib-metadata = "<3.4" 33 | 34 | [tool.poetry.group.dev.dependencies] 35 | safety = "^2.3.5" 36 | 37 | [build-system] 38 | requires = ["poetry-core>=1.4.0"] 39 | build-backend = "poetry.core.masonry.api" 40 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import find_packages, setup 2 | 3 | 4 | def parse_requirements(requirement_file): 5 | with open(requirement_file) as f: 6 | return f.readlines() 7 | 8 | 9 | version = dict() 10 | with open("./pyattck/utils/version.py") as fp: 11 | exec(fp.read(), version) 12 | 13 | 14 | setup( 15 | name="pyattck", 16 | version=version["__version__"], 17 | packages=find_packages(exclude=["tests*"]), 18 | license="MIT", 19 | description="A Python package to interact with the Mitre ATT&CK Frameworks", 20 | long_description=open("README.md").read(), 21 | long_description_content_type="text/markdown", 22 | install_requires=parse_requirements("./requirements.txt"), 23 | keywords=["att&ck", "mitre", "swimlane"], 24 | url="https://github.com/swimlane/pyattck", 25 | author="Swimlane", 26 | author_email="info@swimlane.com", 27 | python_requires=">=3.6, <4", 28 | package_data={"pyattck": ["data/logging.yml"]}, 29 | entry_points={"console_scripts": ["pyattck = pyattck.cli:main"]}, 30 | ) 31 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Swimlane 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /pyattck/base.py: -------------------------------------------------------------------------------- 1 | # Copyright: (c) 2022, Swimlane 2 | # MIT License (see LICENSE or https://opensource.org/licenses/MIT) 3 | from .utils.logger import LoggingBase 4 | 5 | 6 | class Base(metaclass=LoggingBase): 7 | 8 | config = None 9 | LOGO = "CgouX19fX19fICAgX19fXyAgICBfX19fICBfX18gICAuX19fX19fX19fX18uX19fX19fX19fX18uICBfX19fX18gIF9fICBfX18KfCAgIF8gIFwgIFwgICBcICAvICAgLyAvICAgXCAgfCAgICAgICAgICAgfCAgICAgICAgICAgfCAvICAgICAgfHwgIHwvICAvCnwgIHxfKSAgfCAgXCAgIFwvICAgLyAvICBeICBcIGAtLS18ICB8LS0tLWAtLS18ICB8LS0tLWB8ICAsLS0tLSd8ICAnICAvCnwgICBfX18vICAgIFxfICAgIF8vIC8gIC9fXCAgXCAgICB8ICB8ICAgICAgICB8ICB8ICAgICB8ICB8ICAgICB8ICAgIDwKfCAgfCAgICAgICAgICB8ICB8ICAvICBfX19fXyAgXCAgIHwgIHwgICAgICAgIHwgIHwgICAgIHwgIGAtLS0tLnwgIC4gIFwKfCBffCAgICAgICAgICB8X198IC9fXy8gICAgIFxfX1wgIHxfX3wgICAgICAgIHxfX3wgICAgICBcX19fX19ffHxfX3xcX19cCgo=" 10 | FRAMEWORKS = ["enterprise", "ics", "mobile", "preattack"] 11 | ATTCK_TYPES = [ 12 | "actors", 13 | "campaigns", 14 | "controls", 15 | "data_components", 16 | "data_sources", 17 | "malwares", 18 | "mitigations", 19 | "tactics", 20 | "techniques", 21 | "tools", 22 | ] 23 | -------------------------------------------------------------------------------- /docs/actor.md: -------------------------------------------------------------------------------- 1 | # Actor 2 | 3 | This documentation provides details about Actor class within the `pyattck` package. 4 | 5 | > The Actor object is based on the following [data model](https://github.com/swimlane/pyattck-data-models/blob/main/src/pyattck_data_models/actor.py) 6 | 7 | The `Actor` class provides detailed information about identified actors & groups within the MITRE ATT&CK Framework. Additionally, an `Actor` object allows the user to access additional relationships within the MITRE ATT&CK Framework: 8 | 9 | * Tools used by the Actor or Group 10 | * Malware used by the Actor or Group 11 | * Techniques this Actor or Group uses 12 | 13 | You can also access external data properties. The following properties are generated using external data: 14 | 15 | * country 16 | * operations 17 | * attribution_links 18 | * known_tools 19 | * targets 20 | * additional_comments 21 | * external_description 22 | 23 | You can retrieve the entire dataset using the `external_dataset` property. 24 | 25 | ## Actor Class 26 | 27 | ```eval_rst 28 | .. autoclass:: pyattck_data_models.base.BaseModel 29 | :undoc-members: 30 | :inherited-members: 31 | .. autoclass:: pyattck_data_models.actor.Actor 32 | :members: 33 | :undoc-members: 34 | :show-inheritance: 35 | :inherited-members: 36 | ``` 37 | -------------------------------------------------------------------------------- /tests/conftest.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | 4 | @pytest.fixture 5 | def attck_fixture(): 6 | from pyattck import Attck 7 | 8 | yield Attck( 9 | use_config=False, 10 | save_config=False, 11 | enterprise_attck_json="https://swimlane-pyattck.s3.us-west-2.amazonaws.com/merged_enterprise_attck_v1.json", 12 | pre_attck_json="https://swimlane-pyattck.s3.us-west-2.amazonaws.com/merged_pre_attck_v1.json", 13 | mobile_attck_json="https://swimlane-pyattck.s3.us-west-2.amazonaws.com/merged_mobile_attck_v1.json", 14 | ics_attck_json="https://swimlane-pyattck.s3.us-west-2.amazonaws.com/merged_ics_attck_v1.json", 15 | nist_controls_json="https://swimlane-pyattck.s3.us-west-2.amazonaws.com/merged_nist_controls_v1.json", 16 | generated_nist_json="https://swimlane-pyattck.s3.us-west-2.amazonaws.com/attck_to_nist_controls.json", 17 | ) 18 | 19 | 20 | @pytest.fixture 21 | def attck_fixture_nested_techniques_false(): 22 | from pyattck import Attck 23 | 24 | yield Attck(nested_techniques=False) 25 | 26 | 27 | @pytest.fixture 28 | def attck_configuration(): 29 | from pyattck.configuration import Options 30 | 31 | yield Options 32 | 33 | 34 | @pytest.fixture 35 | def attck_datasets(): 36 | from pyattck import Attck 37 | 38 | yield Attck 39 | -------------------------------------------------------------------------------- /pyattck/utils/interactive.py: -------------------------------------------------------------------------------- 1 | """Main interactive menu for pyattck.""" 2 | from ..base import Base 3 | from .layout import CustomLayout 4 | from .menu import Menu 5 | 6 | 7 | class Interactive(Base): 8 | """Generates the interactive menu, options, and drives the display of the menu system.""" 9 | 10 | _framework = None 11 | _type = None 12 | _object = None 13 | 14 | def __init__(self, attck_instance) -> None: 15 | """A pyattck Attck instance class.""" 16 | self._attck_instance = attck_instance 17 | 18 | def generate(self): 19 | """Generates the interactive console for pyattck.""" 20 | main_menu = Menu() 21 | main_menu.prompt = "Select the appropriate MITRE ATT&CK Framework:\n" 22 | for framework in self.FRAMEWORKS: 23 | obj_menu = Menu() 24 | obj_menu.prompt = "Select an entity below:\n" 25 | for obj in dir(getattr(self._attck_instance, framework)): 26 | if not obj.startswith("_") and obj in self.ATTCK_TYPES: 27 | item_menu = Menu() 28 | for item in getattr(getattr(self._attck_instance, framework), obj): 29 | item_menu.add_option(getattr(item, "name"), CustomLayout(item)) 30 | obj_menu.add_option(obj, item_menu) 31 | main_menu.add_option(framework, obj_menu, True) 32 | main_menu.run() 33 | -------------------------------------------------------------------------------- /pyattck/data/logging.yml: -------------------------------------------------------------------------------- 1 | --- 2 | version: 1 3 | disable_existing_loggers: False 4 | formatters: 5 | simple: 6 | format: "%(asctime)s - %(name)s - %(levelname)s - %(message)s" 7 | 8 | handlers: 9 | console: 10 | class: logging.StreamHandler 11 | level: DEBUG 12 | formatter: simple 13 | stream: ext://sys.stdout 14 | 15 | info_file_handler: 16 | class: logging.handlers.RotatingFileHandler 17 | level: INFO 18 | formatter: simple 19 | filename: pyattck_info.log 20 | maxBytes: 10485760 # 10MB 21 | backupCount: 20 22 | encoding: utf8 23 | 24 | error_file_handler: 25 | class: logging.handlers.RotatingFileHandler 26 | level: ERROR 27 | formatter: simple 28 | filename: pyattck_errors.log 29 | maxBytes: 10485760 # 10MB 30 | backupCount: 20 31 | encoding: utf8 32 | 33 | warning_file_handler: 34 | class: logging.handlers.RotatingFileHandler 35 | level: WARNING 36 | formatter: simple 37 | filename: pyattck_warnings.log 38 | maxBytes: 10485760 # 10MB 39 | backupCount: 20 40 | encoding: utf8 41 | 42 | loggers: 43 | my_module: 44 | level: INFO 45 | handlers: [console] 46 | propagate: no 47 | 48 | root: 49 | level: INFO 50 | handlers: [console, info_file_handler, error_file_handler, warning_file_handler] -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | permissions: 8 | contents: write 9 | pull-requests: write 10 | jobs: 11 | release: 12 | name: Release 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: google-github-actions/release-please-action@v3 16 | id: release 17 | with: 18 | release-type: python 19 | package-name: pyattck 20 | bump-minor-pre-major: true 21 | bump-patch-for-minor-pre-major: true 22 | include-v-in-tag: false 23 | # The logic below handles the PyPi distribution: 24 | - uses: actions/checkout@v3 25 | # these if statements ensure that a publication only occurs when 26 | # a new release is created: 27 | if: ${{ steps.release.outputs.release_created }} 28 | - name: Set up Python 29 | uses: actions/setup-python@v4 30 | with: 31 | python-version: "3.10" 32 | if: ${{ steps.release.outputs.release_created }} 33 | - name: Set up poetry 34 | uses: abatilo/actions-poetry@v2.2.0 35 | with: 36 | poetry-version: 1.3.2 37 | if: ${{ steps.release.outputs.release_created }} 38 | - name: Publish 39 | run: | 40 | poetry config http-basic.pypi "${{ secrets.PYPI_USERNAME }}" "${{ secrets.PYPI_PASSWORD }}" 41 | poetry publish --build 42 | if: ${{ steps.release.outputs.release_created }} 43 | -------------------------------------------------------------------------------- /tests/test_campaigns.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | 4 | @pytest.mark.parametrize("target_attribute", ["enterprise"]) 5 | def test_campaigns(attck_fixture, target_attribute): 6 | """ 7 | All MITRE ATT&CK Frameworks Campaigns should have tools 8 | 9 | Args: 10 | attck_fixture ([type]): our default MITRE ATT&CK JSON fixture 11 | """ 12 | campaign_list = set() 13 | for campaign in getattr(attck_fixture, target_attribute).campaigns: 14 | if campaign.name not in campaign_list: 15 | campaign_list.add(campaign.name) 16 | else: 17 | assert False 18 | assert True 19 | 20 | 21 | @pytest.mark.parametrize("target_attribute", ["enterprise"]) 22 | def test_campaigns_have_malwares(attck_fixture, target_attribute): 23 | """ 24 | All MITRE ATT&CK Framework Campaigns should have malwares 25 | 26 | Args: 27 | attck_fixture ([type]): our default MITRE ATT&CK JSON fixture 28 | """ 29 | for campaign in getattr(attck_fixture, target_attribute).campaigns: 30 | if campaign.malwares: 31 | assert getattr(campaign, "malwares") 32 | 33 | 34 | @pytest.mark.parametrize("target_attribute", ["enterprise"]) 35 | def test_campaigns_have_techniques(attck_fixture, target_attribute): 36 | """ 37 | All MITRE ATT&CK Campaigns should have techniques 38 | 39 | Args: 40 | attck_fixture ([type]): our default MITRE ATT&CK JSON fixture 41 | """ 42 | for campaign in getattr(attck_fixture, target_attribute).campaigns: 43 | if campaign.techniques: 44 | assert getattr(campaign, "techniques") 45 | -------------------------------------------------------------------------------- /pyattck/utils/logger.py: -------------------------------------------------------------------------------- 1 | # Copyright: (c) 2022, Swimlane 2 | # MIT License (see LICENSE or https://opensource.org/licenses/MIT) 3 | 4 | import logging 5 | import logging.config 6 | import os 7 | from logging import DEBUG, FileHandler 8 | 9 | import yaml 10 | 11 | 12 | class DebugFileHandler(FileHandler): 13 | def __init__(self, filename, mode="a", encoding=None, delay=False): 14 | super().__init__(filename, mode, encoding, delay) 15 | 16 | def emit(self, record): 17 | if not record.levelno == DEBUG: 18 | return 19 | super().emit(record) 20 | 21 | 22 | class LoggingBase(type): 23 | def __init__(cls, *args): 24 | super().__init__(*args) 25 | cls.setup_logging() 26 | 27 | # Explicit name mangling 28 | logger_attribute_name = "_" + cls.__name__ + "__logger" 29 | 30 | # Logger name derived accounting for inheritance for the bonus marks 31 | logger_name = ".".join([c.__name__ for c in cls.mro()[-2::-1]]) 32 | 33 | setattr(cls, logger_attribute_name, logging.getLogger(logger_name)) 34 | 35 | def setup_logging(cls, default_path="./pyattck/data/logging.yml", default_level=logging.INFO, env_key="LOG_CFG"): 36 | """Setup logging configuration.""" 37 | path = os.path.abspath(os.path.expanduser(os.path.expandvars(default_path))) 38 | value = os.getenv(env_key, None) 39 | if value: 40 | path = value 41 | if os.path.exists(os.path.abspath(path)): 42 | with open(path, "rt") as f: 43 | config = yaml.safe_load(f.read()) 44 | logger = logging.config.dictConfig(config) 45 | else: 46 | logger = logging.basicConfig(level=default_level) 47 | -------------------------------------------------------------------------------- /docs/attck.md: -------------------------------------------------------------------------------- 1 | # Attck 2 | 3 | This documentation provides details about the main entry point called `Attck` within the `pyattck` package. 4 | 5 | > The `MitreAttck` object is based on the following [data model](https://github.com/swimlane/pyattck-data-models/blob/main/src/pyattck_data_models/attack.py) 6 | 7 | This class provides access to the MITRE Enterprise, PRE-ATT&CK, Mobile, and ICS Frameworks. 8 | 9 | * MITRE Enterprise ATT&CK Framework 10 | * MITRE PRE-ATT&CK Framework 11 | * MITRE Mobile ATT&CK Framework 12 | * MITRE ICS ATT&CK Framework 13 | 14 | By default, `subtechniques` are accessible under each technique object. 15 | 16 | As an example, the default behavior looks like the following example: 17 | 18 | ```python 19 | from pyattck import Attck 20 | 21 | attack = Attck() 22 | 23 | for technique in attack.enterprise.techniques: 24 | print(technique.id) 25 | print(technique.name) 26 | for subtechnique in technique.techniques: 27 | print(subtechnique.id) 28 | print(subtechnique.name) 29 | ``` 30 | 31 | You can turn this behavior off by passing `nested_techniques=False` when creating your `Attck` object. When turning this feature off you can access subtechniques on the same level as all other techniques. Here's an example: 32 | 33 | ```python 34 | from pyattck import Attck 35 | 36 | attack = Attck() 37 | 38 | for technique in attack.enterprise.techniques: 39 | print(technique.id) 40 | print(technique.name) 41 | print(f"checking if technique is subtechnique: {technique.techniques}") 42 | ``` 43 | 44 | ## Attck Class 45 | 46 | ```eval_rst 47 | .. autoclass:: pyattck.attck.Attck 48 | :members: 49 | :undoc-members: 50 | :show-inheritance: 51 | ``` 52 | 53 | ```eval_rst 54 | .. toctree:: 55 | 56 | configuration 57 | datasets 58 | ``` -------------------------------------------------------------------------------- /docs/technique.md: -------------------------------------------------------------------------------- 1 | # Technique 2 | 3 | This documentation provides details about the `Technique` class within the `pyattck` package. 4 | 5 | > The `Technique` object is based on the following [data model](https://github.com/swimlane/pyattck-data-models/blob/main/src/pyattck_data_models/technique.py) 6 | 7 | This class provides information about the techniques found under each tactic (columns) within the MITRE ATT&CK Frameworks. 8 | Additionally, a `Technique` object allows the user to access additional relationships within the MITRE ATT&CK Frameworks: 9 | 10 | * Tactics a technique is found in 11 | * Mitigation suggestions for a given technique 12 | * Actor or Group(s) identified as using this technique 13 | * Tools used with a given technique 14 | * Malware used with a given technique 15 | * Subtechniques of a technique if `nested_techniques` is set to `True` 16 | * NIST 800-53 Controls related to a technique 17 | * Data Components of a technique 18 | * Data Sources of a technique 19 | 20 | Each technique enables you to access the following properties on the object: 21 | 22 | * command_list - A list of commands associated with a technique 23 | * commands = A list of dictionary objects containing source, command, and provided name associated with a technique 24 | * queries = A list of dictionary objects containing product, query, and name associated with a technique 25 | * datasets = A list of raw datasets associated with a technique 26 | * possible_detections = A list of raw datasets containing possible detection methods for a technique 27 | * data_sources = A list of raw datasets containing data sources listed for the technique 28 | 29 | 30 | ## Technique Class 31 | 32 | ```eval_rst 33 | .. autoclass:: pyattck_data_models.technique.Technique 34 | :members: 35 | :undoc-members: 36 | :show-inheritance: 37 | ``` -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | .DS_Store 7 | 8 | # C extensions 9 | *.so 10 | 11 | # Remove generate docs code 12 | generateattckdocs/ 13 | 14 | # Distribution / packaging 15 | .Python 16 | build/ 17 | develop-eggs/ 18 | dist/ 19 | downloads/ 20 | eggs/ 21 | .eggs/ 22 | lib/ 23 | lib64/ 24 | parts/ 25 | sdist/ 26 | var/ 27 | wheels/ 28 | *.egg-info/ 29 | .installed.cfg 30 | *.egg 31 | MANIFEST 32 | 33 | # PyInstaller 34 | # Usually these files are written by a python script from a template 35 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 36 | *.manifest 37 | *.spec 38 | 39 | # Installer logs 40 | pip-log.txt 41 | pip-delete-this-directory.txt 42 | 43 | # Unit test / coverage reports 44 | htmlcov/ 45 | .tox/ 46 | .coverage 47 | .coverage.* 48 | .cache 49 | nosetests.xml 50 | coverage.xml 51 | *.cover 52 | .hypothesis/ 53 | .pytest_cache/ 54 | 55 | # Translations 56 | *.mo 57 | *.pot 58 | 59 | # Django stuff: 60 | *.log 61 | local_settings.py 62 | db.sqlite3 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | target/ 76 | 77 | # Jupyter Notebook 78 | .ipynb_checkpoints 79 | 80 | # pyenv 81 | .python-version 82 | 83 | # celery beat schedule file 84 | celerybeat-schedule 85 | 86 | # SageMath parsed files 87 | *.sage.py 88 | 89 | # Environments 90 | .env 91 | .venv 92 | env/ 93 | venv/ 94 | ENV/ 95 | env.bak/ 96 | venv.bak/ 97 | 98 | # Spyder project settings 99 | .spyderproject 100 | .spyproject 101 | 102 | # Rope project settings 103 | .ropeproject 104 | 105 | # mkdocs documentation 106 | /site 107 | 108 | # mypy 109 | .mypy_cache/ 110 | -------------------------------------------------------------------------------- /docs/tools.md: -------------------------------------------------------------------------------- 1 | # Tools 2 | 3 | This documentation provides details about the `Tool` class within the `pyattck` package. 4 | 5 | > The `Tool` object is based on the following [data model](https://github.com/swimlane/pyattck-data-models/blob/main/src/pyattck_data_models/tool.py) 6 | 7 | You can also access external data properties. The following properties are generated using external data: 8 | 9 | 1. additional_names 10 | 2. attribution_links 11 | 3. additional_comments 12 | 4. family 13 | 14 | You can retrieve the entire dataset using the `external_dataset` property. 15 | 16 | You can also access external data properties from the C2 Matrix project. The following properties are generated using C2 Matrix external data: 17 | 18 | - HTTP 19 | - Implementation 20 | - Custom Profile 21 | - DomainFront 22 | - Multi-User 23 | - SMB 24 | - Kill Date 25 | - macOS 26 | - GitHub 27 | - Key Exchange 28 | - Chaining 29 | - Price 30 | - TCP 31 | - Proxy Aware 32 | - HTTP3 33 | - HTTP2 34 | - Date 35 | - Evaluator 36 | - Working Hours 37 | - Slack 38 | - FTP 39 | - Version Reviewed 40 | - Logging 41 | - Name 42 | - License 43 | - Windows 44 | - Stego 45 | - Notes 46 | - Server 47 | - Actively Maint. 48 | - Dashboard 49 | - DNS 50 | - Popular Site 51 | - ICMP 52 | - IMAP 53 | - DoH 54 | - Jitter 55 | - How-To 56 | - ATT&CK Mapping 57 | - Kali 58 | - Twitter 59 | - MAPI 60 | - Site 61 | - Agent 62 | - API 63 | - UI 64 | - Linux 65 | 66 | You can retrieve the entire dataset using the `c2_data` property. 67 | 68 | This class provides information about the tools used by actors or groups within the MITRE ATT&CK Frameworks. 69 | Additionally, a `Tool` object allows the user to access additional relationships within the MITRE ATT&CK Frameworks: 70 | 71 | * Techniques that the specified tool is used within 72 | * Actor or Group(s) using a specified tool 73 | 74 | ## Tool Class 75 | 76 | ```eval_rst 77 | .. autoclass:: pyattck_data_models.tool.Tool 78 | :members: 79 | :undoc-members: 80 | :show-inheritance: 81 | ``` -------------------------------------------------------------------------------- /pyattck/preattck.py: -------------------------------------------------------------------------------- 1 | from pyattck_data.attack import MitreAttck 2 | 3 | from .base import Base 4 | 5 | 6 | class PreAttck(Base): 7 | """An interface to the MITRE ATT&CK Pre-Attck Framework. 8 | 9 | This class creates an interface to all data points in the 10 | MITRE ATT&CK Pre-Attck framework. 11 | 12 | This interface enables you to retrieve all properties within 13 | each item in the MITRE ATT&CK Pre-Attck Framework. 14 | 15 | The following categorical items can be accessed using this class: 16 | 17 | 1. Actors 18 | 2. Tactics 19 | 3. Techniques 20 | 21 | As of pyattck 6.0.0, MITRE ATT&CK Frameworks are merged with generated datasets. 22 | These can be found [here](https://github.com/swimlane/pyattck-data) 23 | """ 24 | 25 | __tactics = [] 26 | __techniques = [] 27 | __actors = [] 28 | __attck = MitreAttck(**Base.config.get_data("pre_attck_json")) 29 | 30 | @property 31 | def actors(self): 32 | """Retrieves Actor objects. 33 | 34 | Returns: 35 | (Actor) -- (Returns a list of Actor objects) 36 | """ 37 | if not self.__actors: 38 | self.__actors = [x for x in self.__attck.objects if x.type == "intrusion-set"] 39 | return self.__actors 40 | 41 | @property 42 | def tactics(self): 43 | """Retrieves Tactic objects. 44 | 45 | Returns: 46 | (Tactic) -- (Returns a list of Tactic objects) 47 | """ 48 | if not self.__tactics: 49 | self.__tactics = [x for x in self.__attck.objects if x.type == "x-mitre-tactic"] 50 | return self.__tactics 51 | 52 | @property 53 | def techniques(self): 54 | """Retrieves Technique objects. 55 | 56 | Returns: 57 | (Technique) -- Returns a list of Technique objects 58 | """ 59 | if not self.__techniques: 60 | for item in self.__attck.objects: 61 | if item.type == "attack-pattern": 62 | if item.techniques and not Base.config.nested_techniques: 63 | for i in item.techniques: 64 | self.__techniques.append(i) 65 | self.__techniques.append(item) 66 | return self.__techniques 67 | -------------------------------------------------------------------------------- /.github/workflows/quality.yml: -------------------------------------------------------------------------------- 1 | name: Quality Check 2 | on: [push] 3 | 4 | jobs: 5 | code-quality: 6 | strategy: 7 | fail-fast: false 8 | matrix: 9 | python-version: ["3.7"] 10 | poetry-version: ["1.3.2"] 11 | os: [ubuntu-latest] 12 | runs-on: ${{ matrix.os }} 13 | steps: 14 | - uses: actions/checkout@v2 15 | - uses: actions/setup-python@v2 16 | with: 17 | python-version: ${{ matrix.python-version }} 18 | - name: Upgrade pip 19 | run: | 20 | pip install --constraint=.github/workflows/constraints.txt pip 21 | pip --version 22 | - name: Run image 23 | uses: abatilo/actions-poetry@v2.2.0 24 | with: 25 | poetry-version: ${{ matrix.poetry-version }} 26 | - name: Install dependencies 27 | run: | 28 | poetry run pip install --upgrade pip 29 | poetry install 30 | - name: Run black 31 | run: poetry run black ./pyattck --line-length 120 32 | - name: Run isort 33 | run: poetry run isort ./pyattck --check-only --profile "black" 34 | - name: Run flake8 35 | run: poetry run flake8 ./pyattck 36 | - name: Run bandit 37 | run: poetry run bandit ./pyattck 38 | - name: Run saftey 39 | run: poetry run safety check --ignore=47794 40 | test: 41 | needs: code-quality 42 | strategy: 43 | fail-fast: false 44 | matrix: 45 | python-version: ['3.7', '3.8', '3.9', '3.10'] 46 | poetry-version: ["1.3.2"] 47 | os: [ubuntu-latest,macos-latest,windows-latest] 48 | runs-on: ${{ matrix.os }} 49 | steps: 50 | - uses: actions/checkout@v2 51 | - uses: actions/setup-python@v2 52 | with: 53 | python-version: ${{ matrix.python-version }} 54 | - name: Upgrade pip 55 | run: | 56 | pip install --constraint=.github/workflows/constraints.txt pip 57 | pip --version 58 | - name: Run image 59 | uses: abatilo/actions-poetry@v2.2.0 60 | with: 61 | poetry-version: ${{ matrix.poetry-version }} 62 | - name: Install dependencies 63 | run: | 64 | poetry run pip install --upgrade pip 65 | poetry run pip install --upgrade setuptools 66 | poetry install 67 | - name: Run tests 68 | run: poetry run coverage run -m pytest && poetry run coverage report 69 | - name: Upload coverage to Codecov 70 | uses: codecov/codecov-action@v1 71 | -------------------------------------------------------------------------------- /images/macos_support.svg: -------------------------------------------------------------------------------- 1 | python - macOSpython - macOS3.6, 3.7, 3.8, 3.93.6, 3.7, 3.8, 3.9 -------------------------------------------------------------------------------- /images/ubuntu_support.svg: -------------------------------------------------------------------------------- 1 | python - Ubuntupython - Ubuntu3.6, 3.7, 3.8, 3.93.6, 3.7, 3.8, 3.9 -------------------------------------------------------------------------------- /images/windows_support.svg: -------------------------------------------------------------------------------- 1 | python - Windowspython - Windows3.6, 3.7, 3.8, 3.93.6, 3.7, 3.8, 3.9 -------------------------------------------------------------------------------- /docs/images/macos_support.svg: -------------------------------------------------------------------------------- 1 | python - macOSpython - macOS2.7, 3.5, 3.6, 3.7, 3.82.7, 3.5, 3.6, 3.7, 3.8 -------------------------------------------------------------------------------- /docs/images/ubuntu_support.svg: -------------------------------------------------------------------------------- 1 | python - Ubuntupython - Ubuntu2.7, 3.5, 3.6, 3.7, 3.82.7, 3.5, 3.6, 3.7, 3.8 -------------------------------------------------------------------------------- /docs/images/windows_support.svg: -------------------------------------------------------------------------------- 1 | python - Windowspython - Windows2.7, 3.5, 3.6, 3.7, 3.82.7, 3.5, 3.6, 3.7, 3.8 -------------------------------------------------------------------------------- /tests/test_actors.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | 4 | @pytest.mark.parametrize("target_attribute", ["enterprise", "mobile", "preattack"]) 5 | def test_actors_have_tools(attck_fixture, target_attribute): 6 | """ 7 | All MITRE ATT&CK Frameworks Actors should have tools 8 | 9 | Args: 10 | attck_fixture ([type]): our default MITRE ATT&CK JSON fixture 11 | """ 12 | for actor in getattr(attck_fixture, target_attribute).actors: 13 | if actor.tools: 14 | assert getattr(actor, "tools") 15 | 16 | 17 | @pytest.mark.parametrize("target_attribute", ["enterprise", "mobile"]) 18 | def test_actors_have_malwares(attck_fixture, target_attribute): 19 | """ 20 | All MITRE ATT&CK Framework Actors should have malwares 21 | 22 | Args: 23 | attck_fixture ([type]): our default MITRE ATT&CK JSON fixture 24 | """ 25 | for actor in getattr(attck_fixture, target_attribute).actors: 26 | if actor.malwares: 27 | assert getattr(actor, "malwares") 28 | 29 | 30 | @pytest.mark.parametrize("target_attribute", ["enterprise", "mobile", "preattack"]) 31 | def test_actors_have_techniques(attck_fixture, target_attribute): 32 | """ 33 | All MITRE ATT&CK Actors should have techniques 34 | 35 | Args: 36 | attck_fixture ([type]): our default MITRE ATT&CK JSON fixture 37 | """ 38 | for actor in getattr(attck_fixture, target_attribute).actors: 39 | if actor.techniques: 40 | assert getattr(actor, "techniques") 41 | 42 | 43 | @pytest.mark.parametrize("target_attribute", ["enterprise", "mobile", "preattack"]) 44 | def test_some_actors_have_generated_datasets_properties(attck_fixture, target_attribute): 45 | """ 46 | Some MITRE Enterprise ATT&CK Actors should have generated datasets properties 47 | 48 | Args: 49 | attck_fixture ([type]): our default MITRE Enterprise ATT&CK JSON fixture 50 | """ 51 | country_count = 0 52 | operations_count = 0 53 | attribution_links_count = 0 54 | known_tools_count = 0 55 | targets_count = 0 56 | additional_comments_count = 0 57 | external_description_count = 0 58 | for actor in getattr(attck_fixture, target_attribute).actors: 59 | if hasattr(actor, "country"): 60 | country_count += 1 61 | if hasattr(actor, "operations"): 62 | operations_count += 1 63 | if hasattr(actor, "attribution_links"): 64 | attribution_links_count += 1 65 | if hasattr(actor, "known_tools"): 66 | known_tools_count += 1 67 | if hasattr(actor, "targets"): 68 | targets_count += 1 69 | if hasattr(actor, "additional_comments"): 70 | additional_comments_count += 1 71 | if hasattr(actor, "external_description"): 72 | external_description_count += 1 73 | 74 | if ( 75 | country_count >= 1 76 | and operations_count >= 1 77 | and attribution_links_count >= 1 78 | and known_tools_count >= 1 79 | and targets_count >= 1 80 | and additional_comments_count >= 1 81 | and external_description_count >= 1 82 | ): 83 | assert True 84 | -------------------------------------------------------------------------------- /tests/test_tools.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | 4 | @pytest.mark.parametrize("target_attribute", ["enterprise", "mobile"]) 5 | def test_tools_have_techniques(attck_fixture, target_attribute): 6 | """ 7 | All MITRE ATT&CK Tools should have Techniques 8 | 9 | Args: 10 | attck_fixture ([type]): our default MITRE ATT&CK JSON fixture 11 | """ 12 | for tool in getattr(attck_fixture, target_attribute).tools: 13 | if tool.techniques: 14 | assert getattr(tool, "techniques") 15 | 16 | 17 | @pytest.mark.parametrize("target_attribute", ["enterprise", "mobile"]) 18 | def test_tools_have_actors(attck_fixture, target_attribute): 19 | """ 20 | All Mitre ATT&CK Tools should have Actors 21 | 22 | Args: 23 | attck_fixture ([type]): our default MITRE ATT&CK JSON fixture 24 | """ 25 | for tool in getattr(attck_fixture, target_attribute).tools: 26 | if tool.actors: 27 | assert getattr(tool, "actors") 28 | 29 | 30 | @pytest.mark.parametrize("target_attribute", ["enterprise", "mobile"]) 31 | def test_some_tools_have_c2_data(attck_fixture, target_attribute): 32 | """ 33 | Some MITRE ATT&CK Tools should have C2 Matrix Data 34 | 35 | Args: 36 | attck_fixture ([type]): our default MITRE ATT&CK JSON fixture 37 | """ 38 | count = 0 39 | for tool in getattr(attck_fixture, target_attribute).tools: 40 | if hasattr(tool, "c2_data"): 41 | count += 1 42 | if count >= 1: 43 | assert True 44 | 45 | 46 | @pytest.mark.parametrize("target_attribute", ["enterprise", "mobile"]) 47 | def test_some_tools_have_generated_datasets(attck_fixture, target_attribute): 48 | """ 49 | Some MITRE ATT&CK Tools should have generated datasets 50 | 51 | Args: 52 | attck_fixture ([type]): our default MITRE ATT&CK JSON fixture 53 | """ 54 | count = 0 55 | for tool in getattr(attck_fixture, target_attribute).tools: 56 | if hasattr(tool, "external_dataset"): 57 | count += 1 58 | if count >= 1: 59 | assert True 60 | 61 | 62 | @pytest.mark.parametrize("target_attribute", ["enterprise", "mobile"]) 63 | def test_some_tools_have_generated_datasets_properties(attck_fixture, target_attribute): 64 | """ 65 | Some MITRE ATT&CK Tools should have generated datasets properties 66 | 67 | Args: 68 | attck_fixture ([type]): our default MITRE ATT&CK JSON fixture 69 | """ 70 | additional_names_count = 0 71 | attribution_links_count = 0 72 | additional_comments_count = 0 73 | family_count = 0 74 | 75 | for tool in getattr(attck_fixture, target_attribute).tools: 76 | if hasattr(tool, "additional_names"): 77 | additional_names_count += 1 78 | if hasattr(tool, "additional_comments"): 79 | additional_comments_count += 1 80 | if hasattr(tool, "attribution_links"): 81 | attribution_links_count += 1 82 | if hasattr(tool, "family"): 83 | family_count += 1 84 | 85 | if ( 86 | additional_names_count >= 1 87 | and additional_comments_count >= 1 88 | and attribution_links_count >= 1 89 | and family_count >= 1 90 | ): 91 | assert True 92 | -------------------------------------------------------------------------------- /tests/test_attck.py: -------------------------------------------------------------------------------- 1 | import os 2 | import random 3 | import tempfile 4 | 5 | import pytest 6 | 7 | default_config_data = { 8 | "data_path": os.path.abspath(os.path.expanduser(os.path.expandvars("~/pyattck/data"))), 9 | "enterprise_attck_json": "https://swimlane-pyattck.s3.us-west-2.amazonaws.com/merged_enterprise_attck_v1.json", 10 | "pre_attck_json": "https://swimlane-pyattck.s3.us-west-2.amazonaws.com/merged_pre_attck_v1.json", 11 | "mobile_attck_json": "https://swimlane-pyattck.s3.us-west-2.amazonaws.com/merged_mobile_attck_v1.json", 12 | "ics_attck_json": "https://swimlane-pyattck.s3.us-west-2.amazonaws.com/merged_ics_attck_v1.json", 13 | "nist_controls_json": "https://swimlane-pyattck.s3.us-west-2.amazonaws.com/merged_nist_controls_v1.json", 14 | "generated_nist_json": "https://swimlane-pyattck.s3.us-west-2.amazonaws.com/attck_to_nist_controls.json", 15 | "config_file_path": os.path.abspath(os.path.expanduser(os.path.expandvars("~/pyattck/config.yml"))), 16 | } 17 | 18 | 19 | def get_random_file_or_url(): 20 | if random.choice(["file", "url"]) == "file": 21 | return tempfile.NamedTemporaryFile().name 22 | else: 23 | return random.choice( 24 | ["https://letsautomate.it/article/index.xml", "https://google.com", "https://github.com/swimlane/pyattck"] 25 | ) 26 | 27 | 28 | @pytest.mark.parametrize( 29 | "target_attribute", 30 | [ 31 | "enterprise_attck_json", 32 | "pre_attck_json", 33 | "mobile_attck_json", 34 | "ics_attck_json", 35 | "nist_controls_json", 36 | "generated_nist_json", 37 | ], 38 | ) 39 | def test_setting_json_locations(target_attribute): 40 | from pyattck import Attck 41 | 42 | enterprise_temp_value = get_random_file_or_url() 43 | pre_attck_temp_value = get_random_file_or_url() 44 | mobile_temp_value = get_random_file_or_url() 45 | ics_temp_value = get_random_file_or_url() 46 | nist_controls_temp_value = get_random_file_or_url() 47 | generated_nist_temp_value = get_random_file_or_url() 48 | 49 | attck = Attck(enterprise_attck_json=enterprise_temp_value) 50 | assert attck.config.config.enterprise_attck_json == enterprise_temp_value 51 | 52 | attck = Attck(pre_attck_json=pre_attck_temp_value) 53 | assert attck.config.config.pre_attck_json == pre_attck_temp_value 54 | 55 | attck = Attck(mobile_attck_json=mobile_temp_value) 56 | assert attck.config.config.mobile_attck_json == mobile_temp_value 57 | 58 | attck = Attck(ics_attck_json=ics_temp_value) 59 | assert attck.config.config.ics_attck_json == ics_temp_value 60 | 61 | attck = Attck(nist_controls_json=nist_controls_temp_value) 62 | assert attck.config.config.nist_controls_json == nist_controls_temp_value 63 | 64 | attck = Attck(generated_nist_json=generated_nist_temp_value) 65 | assert attck.config.config.generated_nist_json == generated_nist_temp_value 66 | 67 | 68 | def test_passed_kwargs(): 69 | from pyattck import Attck 70 | 71 | attck = Attck() 72 | args = { 73 | "verify": False, 74 | "proxies": { 75 | "http": "http://10.10.1.10:3128", 76 | "https": "http://10.10.1.10:1080", 77 | }, 78 | } 79 | attck = Attck(**args) 80 | assert attck.config.kwargs == args 81 | -------------------------------------------------------------------------------- /pyattck/mobile.py: -------------------------------------------------------------------------------- 1 | from pyattck_data.attack import MitreAttck 2 | 3 | from .base import Base 4 | 5 | 6 | class MobileAttck(Base): 7 | """An interface to the MITRE ATT&CK Mobile Framework. 8 | 9 | This class creates an interface to all data points in the 10 | MITRE ATT&CK Mobile framework. 11 | 12 | This interface enables you to retrieve all properties within 13 | each item in the MITRE ATT&CK Mobile Framework. 14 | 15 | The following categorical items can be accessed using this class: 16 | 17 | 1. Actors 18 | 2. Malware 19 | 3. Mitigations 20 | 4. Tactics 21 | 5. Techniques 22 | 6. Tools 23 | 24 | As of pyattck 6.0.0, MITRE ATT&CK Frameworks are merged with generated datasets. 25 | These can be found [here](https://github.com/swimlane/pyattck-data) 26 | """ 27 | 28 | __tactics = [] 29 | __techniques = [] 30 | __mitigations = [] 31 | __actors = [] 32 | __tools = [] 33 | __malwares = [] 34 | __attck = MitreAttck(**Base.config.get_data("mobile_attck_json")) 35 | 36 | @property 37 | def actors(self): 38 | """Retrieves Actor objects. 39 | 40 | Returns: 41 | (Actor) -- (Returns a list of Actor objects) 42 | """ 43 | if not self.__actors: 44 | self.__actors = [x for x in self.__attck.objects if x.type == "intrusion-set"] 45 | return self.__actors 46 | 47 | @property 48 | def malwares(self): 49 | """Retrieves Malware objects. 50 | 51 | Returns: 52 | (Malware) -- Returns a list of Malware objects 53 | """ 54 | if not self.__malwares: 55 | self.__malwares = [x for x in self.__attck.objects if x.type == "malware"] 56 | return self.__malwares 57 | 58 | @property 59 | def mitigations(self): 60 | """Retrieves Mitigation objects. 61 | 62 | Returns: 63 | (Mitigation) -- (Returns a list of Mitigation objects) 64 | """ 65 | if not self.__mitigations: 66 | self.__mitigations = [x for x in self.__attck.objects if x.type == "course-of-action"] 67 | return self.__mitigations 68 | 69 | @property 70 | def tactics(self): 71 | """Retrieves Tactic objects. 72 | 73 | Returns: 74 | (Tactic) -- (Returns a list of Tactic objects) 75 | """ 76 | if not self.__tactics: 77 | self.__tactics = [x for x in self.__attck.objects if x.type == "x-mitre-tactic"] 78 | return self.__tactics 79 | 80 | @property 81 | def techniques(self): 82 | """Retrieves Technique objects. 83 | 84 | Returns: 85 | (Technique) -- Returns a list of Technique objects 86 | """ 87 | if not self.__techniques: 88 | for item in self.__attck.objects: 89 | if item.type == "attack-pattern": 90 | if item.techniques and not Base.config.nested_techniques: 91 | for i in item.techniques: 92 | self.__techniques.append(i) 93 | self.__techniques.append(item) 94 | return self.__techniques 95 | 96 | @property 97 | def tools(self): 98 | """Retrieves Tool objects. 99 | 100 | Returns: 101 | (Tool) -- Returns a list of Tool objects 102 | """ 103 | if not self.__tools: 104 | self.__tools = [x for x in self.__attck.objects if x.type == "tool"] 105 | return self.__tools 106 | -------------------------------------------------------------------------------- /docs/configuration.md: -------------------------------------------------------------------------------- 1 | # Configuration 2 | 3 | `pyattck` allows you to configure if you store external data and as well as where it is stored. Below shows all available parameters when instantiating the `Attck` object. 4 | 5 | ```python 6 | from pyattck import Attck 7 | 8 | attck = Attck( 9 | nested_techniques=True, 10 | use_config=False, 11 | save_config=False, 12 | config_file_path='~/pyattck/config.yml', 13 | data_path='~/pyattck/data', 14 | enterprise_attck_json="https://swimlane-pyattck.s3.us-west-2.amazonaws.com/merged_enterprise_attck_v1.json", 15 | pre_attck_json="https://swimlane-pyattck.s3.us-west-2.amazonaws.com/merged_pre_attck_v1.json", 16 | mobile_attck_json="https://swimlane-pyattck.s3.us-west-2.amazonaws.com/merged_mobile_attck_v1.json", 17 | ics_attck_json="https://swimlane-pyattck.s3.us-west-2.amazonaws.com/merged_ics_attck_v1.json", 18 | nist_controls_json="https://swimlane-pyattck.s3.us-west-2.amazonaws.com/merged_nist_controls_v1.json", 19 | generated_nist_json="https://swimlane-pyattck.s3.us-west-2.amazonaws.com/attck_to_nist_controls.json", 20 | **kwargs 21 | ) 22 | ``` 23 | 24 | By default, `pyattck` will pull the latest external data from their respective locations using HTTP GET requests. `pyattck` currently pulls from the following locations: 25 | 26 | * enterprise_attck_json="https://swimlane-pyattck.s3.us-west-2.amazonaws.com/merged_enterprise_attck_v1.json" 27 | * pre_attck_json="https://swimlane-pyattck.s3.us-west-2.amazonaws.com/merged_pre_attck_v1.json" 28 | * mobile_attck_json="https://swimlane-pyattck.s3.us-west-2.amazonaws.com/merged_mobile_attck_v1.json" 29 | * ics_attck_json="https://swimlane-pyattck.s3.us-west-2.amazonaws.com/merged_ics_attck_v1.json" 30 | * nist_controls_json="https://swimlane-pyattck.s3.us-west-2.amazonaws.com/merged_nist_controls_v1.json" 31 | * generated_nist_json="https://swimlane-pyattck.s3.us-west-2.amazonaws.com/attck_to_nist_controls.json" 32 | 33 | You have several options when instantiating the `Attck` object. As of `4.0.0` you can now specify any of the following options: 34 | 35 | * use_config - When you specify this argument as `True` pyattck will attempt to retrieve the configuration specified in the `config_file_path` location. If this file is corrupted or cannot be found, we will default to retrieving data from the specified `*_attck_json` locations. 36 | * save_config - When you specify this argument as `True` pyattck will save the configuration file to the specified location set by `config_file_path`. Additionally, we will save all downloaded files to the `data_path` location specified. If you have specified a local path location instead of a download URL for any of the `*_attck_json` parameters we will save this location in our configuration and reference this location going forward. 37 | * config_file_path - The path to store a configuration file. Default is `~/pyattck/config.yml` 38 | * data_path - The path to store any data files downloaded to the local system. Default is `~/pyattck/data` 39 | 40 | ### JSON Locations 41 | 42 | Additionally, you can specify the location for each individual `*_attck_json` files by passing in either a URI or a local file path. If you have passed in a local file path, we will simply read from this file. 43 | 44 | If you have used the default values or specified an alternative URI location to retrieve these JSON files from, you can additionally pass in `**kwargs` that will be passed along to the `Requests` python package when performing any HTTP requests. 45 | 46 | ## Config Class 47 | 48 | ```eval_rst 49 | .. autoclass:: pyattck.config.Config 50 | :members: 51 | :undoc-members: 52 | :show-inheritance: 53 | ``` -------------------------------------------------------------------------------- /docs/preattck.md: -------------------------------------------------------------------------------- 1 | # PreAttck 2 | 3 | This documentation provides details about the `PreAttck` class within the `pyattck` package. 4 | 5 | > The `MitreAttck` object is based on the following [data model](https://github.com/swimlane/pyattck-data-models/blob/main/src/pyattck_data_models/attack.py) 6 | 7 | The `PreAttck` class provides detailed information about data within the MITRE PRE-ATT&CK framework 8 | 9 | Each of the main properties can return a json object of the entire object or you can access each property individually. An example of this is here: 10 | 11 | ```python 12 | from pyattck import Attck 13 | 14 | attack = Attck() 15 | 16 | # accessing techniques and their properties 17 | for technique in attack.preattack.techniques: 18 | # if you want to return individual properties of this object you call them directly 19 | print(technique.id) 20 | print(technique.name) 21 | print(technique.alias) 22 | print(technique.description) 23 | print(technique.stix) 24 | print(technique.platforms) 25 | print(technique.permissions) 26 | print(technique.wiki) 27 | ..... 28 | ``` 29 | 30 | The following is only a small sample of the available properties on each object and each object type (actors, tactics, and techniques) will have different properties that you can access. 31 | 32 | 33 | * Every data point has exposed properties that allow the user to retrieve additional data based on relationships: 34 | * [Actor](actor.md) 35 | * Relationship Objects 36 | * Techniques this Actor or Group uses 37 | * External Data 38 | * country which this actor or group may be associated with (attribution is hard) 39 | * operations 40 | * attribution_links 41 | * known_tools 42 | * targets 43 | * additional_comments 44 | * external_description 45 | * [Tactic](tactic.md) 46 | * Techniques found in a specific Tactic (phase) 47 | * [Technique](technique.md) 48 | * Relationship Objects 49 | * Tactics a technique is found in 50 | * Actor or Group(s) identified as using this technique 51 | 52 | 53 | Below shows you how you can access each of object types and their properties. Additionally, you can access related object types associated with this selected object type: 54 | 55 | ```python 56 | from pyattck import Attck 57 | 58 | attack = Attck() 59 | 60 | for actor in attack.preattack.actors: 61 | print(actor.id) 62 | print(actor.name) 63 | 64 | # accessing techniques used by an actor or group 65 | for technique in actor.techniques: 66 | print(technique.id) 67 | print(technique.name) 68 | 69 | # accessing tactics 70 | for tactic in attack.preattack.tactics: 71 | print(tactic.id) 72 | print(tactic.name) 73 | 74 | # accessing techniques related to this tactic 75 | for technique in tactic.techniques: 76 | print(technique.id) 77 | print(technique.name) 78 | 79 | # accessing techniques 80 | for technique in attack.preattack.techniques: 81 | print(technique.id) 82 | print(technique.name) 83 | 84 | # accessing tactics that this technique belongs to 85 | for tactic in technique.tactics: 86 | print(tactic.id) 87 | print(tactic.name) 88 | 89 | # accessing actors using this technique 90 | for actor in technique.actors: 91 | print(actor.id) 92 | print(actor.name) 93 | ``` 94 | 95 | ## PreAttck Class 96 | 97 | ```eval_rst 98 | .. autoclass:: pyattck.preattck.PreAttck 99 | :members: 100 | :undoc-members: 101 | :show-inheritance: 102 | ``` 103 | 104 | 105 | ```eval_rst 106 | .. toctree:: 107 | 108 | actor 109 | tactic 110 | technique 111 | ``` 112 | -------------------------------------------------------------------------------- /pyattck/utils/menu.py: -------------------------------------------------------------------------------- 1 | """Menu for interactive console within pyattck.""" 2 | import base64 3 | import os 4 | from typing import List 5 | 6 | from ..base import Base 7 | 8 | 9 | class Menu(Base): 10 | """Menu to control flow of the interactive menu.""" 11 | 12 | def __init__(self): 13 | """Creates a new instance of a Menu class object.""" 14 | self.options = {} 15 | self._option_number = 0 16 | self._selected_option = None 17 | self.prompt = "Please choose an option:" 18 | self.error_text = "Error! Please enter a valid option." 19 | self._logo_displayed = False 20 | 21 | @property 22 | def prompt(self) -> str: 23 | """The prompt question to use when using this menu.""" 24 | return self._prompt 25 | 26 | @prompt.setter 27 | def prompt(self, value: str) -> None: 28 | """Sets the prompt question to use when using this menu.""" 29 | self._prompt = value 30 | 31 | @property 32 | def error_text(self) -> str: 33 | """The error text for this menu.""" 34 | return self._error_text 35 | 36 | @error_text.setter 37 | def error_text(self, value: str) -> None: 38 | """Sets the error text for the current menu.""" 39 | self._error_text = value 40 | 41 | @property 42 | def selected_option(self) -> List[str]: 43 | """Returns the currently selected option.""" 44 | if self._selected_option: 45 | return self.options[self._selected_option] 46 | return None 47 | 48 | @selected_option.setter 49 | def selected_option(self, value: int) -> None: 50 | """Sets the currently selected option from the options list.""" 51 | self._selected_option = value 52 | 53 | def cls(self) -> None: 54 | """Clears the current screen.""" 55 | os.system("cls" if os.name == "nt" else "clear") 56 | 57 | def display_error(self) -> None: 58 | """Displays the defined error message with padding as needed.""" 59 | print(f"\n{self.error_text}\n") 60 | 61 | def display_menu(self, clear_screen: bool = False) -> None: 62 | """Displays the current menu options.""" 63 | if clear_screen: 64 | self.cls() 65 | print(self.prompt) 66 | for i in range(1, len(self.options) + 1): 67 | print(f"{i} - {self.options[i][0]}") 68 | 69 | def add_option(self, name: str, option: object or None, triggers_exit: bool = False) -> None: 70 | """Adds an option to the menu option list.""" 71 | self._option_number += 1 72 | self.options[self._option_number] = [name, option, triggers_exit] 73 | 74 | def run(self) -> None: 75 | """Main method to display the current menu, as well as all provided sub-menus.""" 76 | user_input = "" 77 | if not self._logo_displayed: 78 | self.cls() 79 | print(base64.b64decode(self.LOGO).decode("ascii")) 80 | self._logo_displayed = True 81 | self.display_menu() 82 | while True: 83 | user_input = input("\nYour selection: ") 84 | try: 85 | user_input = int(user_input) 86 | if user_input <= 0 or user_input > len(self.options): 87 | self.display_error() 88 | else: 89 | if callable(self.options[user_input][1]): 90 | self.selected_option = user_input 91 | self.options[user_input][1]() 92 | if self.options[user_input][2]: 93 | return 94 | else: 95 | self.display_menu() 96 | elif self.options[user_input][1]: 97 | self.selected_option = user_input 98 | self.options[user_input][1].run() 99 | self.display_menu(clear_screen=True) 100 | else: 101 | return 102 | except ValueError: 103 | self.display_error() 104 | -------------------------------------------------------------------------------- /tests/test_techniques.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | 4 | @pytest.mark.parametrize("target_attribute", ["enterprise", "mobile", "preattack", "ics"]) 5 | def test_techniques_have_tactics(attck_fixture, target_attribute): 6 | """ 7 | All MITRE ATT&CK Techniques should have tactics 8 | 9 | Args: 10 | attck_fixture ([type]): our default MITRE ATT&CK JSON fixture 11 | """ 12 | for technique in getattr(attck_fixture, target_attribute).techniques: 13 | if technique.tactics: 14 | assert getattr(technique, "tactics") 15 | 16 | 17 | @pytest.mark.parametrize("target_attribute", ["enterprise", "mobile", "ics"]) 18 | def test_techniques_have_mitigations(attck_fixture, target_attribute): 19 | """ 20 | Some MITRE ATT&CK Techniques should have mitigations 21 | 22 | Args: 23 | attck_fixture ([type]): our default MITRE ATT&CK JSON fixture 24 | """ 25 | count = 0 26 | for technique in getattr(attck_fixture, target_attribute).techniques: 27 | if not hasattr(technique, "mitigations"): 28 | if technique.mitigations: 29 | count += 1 30 | if count >= 1: 31 | assert True 32 | 33 | 34 | @pytest.mark.parametrize("target_attribute", ["enterprise", "mobile", "preattack"]) 35 | def test_techniques_have_actors(attck_fixture, target_attribute): 36 | """ 37 | All MITRE ATT&CK Techniques should have Actors 38 | 39 | Args: 40 | attck_fixture ([type]): our default MITRE ATT&CK JSON fixture 41 | """ 42 | count = 0 43 | for technique in getattr(attck_fixture, target_attribute).techniques: 44 | if not hasattr(technique, "actors"): 45 | if technique.actors: 46 | count += 1 47 | if count >= 1: 48 | assert True 49 | 50 | 51 | @pytest.mark.parametrize("target_attribute", ["enterprise", "mobile", "preattack", "ics"]) 52 | def test_some_techniques_have_generated_datasets_properties(attck_fixture, target_attribute): 53 | """ 54 | Some MITRE ATT&CK Techniques should have generated datasets properties 55 | 56 | Args: 57 | attck_fixture ([type]): our default MITRE ATT&CK JSON fixture 58 | """ 59 | command_list_count = 0 60 | commands_count = 0 61 | queries_count = 0 62 | datasets_count = 0 63 | possible_detections_count = 0 64 | 65 | for technique in getattr(attck_fixture, target_attribute).techniques: 66 | if hasattr(technique, "command_list"): 67 | command_list_count += 1 68 | if hasattr(technique, "commands"): 69 | commands_count += 1 70 | if hasattr(technique, "queries"): 71 | queries_count += 1 72 | if hasattr(technique, "datasets"): 73 | datasets_count += 1 74 | if hasattr(technique, "possible_detections"): 75 | possible_detections_count += 1 76 | if ( 77 | command_list_count >= 1 78 | and commands_count >= 1 79 | and queries_count >= 1 80 | and datasets_count >= 1 81 | and possible_detections_count >= 1 82 | ): 83 | assert True 84 | 85 | 86 | @pytest.mark.parametrize("target_attribute", ["enterprise", "ics"]) 87 | def test_some_techniques_have_compliance_controls(attck_fixture, target_attribute): 88 | """ 89 | Some MITRE ATT&CK Techniques should have compliance controls 90 | 91 | Args: 92 | attck_fixture ([type]): our default MITRE ATT&CK JSON fixture 93 | """ 94 | count = 0 95 | for technique in getattr(attck_fixture, target_attribute).techniques: 96 | if technique.controls: 97 | for control in technique.controls: 98 | if control: 99 | count += 1 100 | if count >= 600: 101 | assert True 102 | 103 | 104 | @pytest.mark.parametrize("target_attribute", ["enterprise", "ics", "mobile"]) 105 | def test_techniques_have_malwares(attck_fixture, target_attribute): 106 | """ 107 | Some MITRE ATT&CK Techniques should have malwares 108 | 109 | Args: 110 | attck_fixture ([type]): our default MITRE ATT&CK JSON fixture 111 | """ 112 | count = 0 113 | for technique in getattr(attck_fixture, target_attribute).techniques: 114 | if not hasattr(technique, "malwares"): 115 | if technique.malwares: 116 | count += 1 117 | if count >= 1: 118 | assert True 119 | -------------------------------------------------------------------------------- /tests/test_configuration.py: -------------------------------------------------------------------------------- 1 | import os 2 | import random 3 | import tempfile 4 | 5 | import pytest 6 | 7 | 8 | def get_random_file_or_url(): 9 | if random.choice(["file", "url"]) == "file": 10 | return tempfile.NamedTemporaryFile().name 11 | else: 12 | return random.choice( 13 | ["https://letsautomate.it/article/index.xml", "https://google.com", "https://github.com/swimlane/pyattck"] 14 | ) 15 | 16 | 17 | default_config_data = { 18 | "data_path": os.path.abspath(os.path.expanduser(os.path.expandvars("~/pyattck/data"))), 19 | "enterprise_attck_json": "https://swimlane-pyattck.s3.us-west-2.amazonaws.com/merged_enterprise_attck_v1.json", 20 | "pre_attck_json": "https://swimlane-pyattck.s3.us-west-2.amazonaws.com/merged_pre_attck_v1.json", 21 | "mobile_attck_json": "https://swimlane-pyattck.s3.us-west-2.amazonaws.com/merged_mobile_attck_v1.json", 22 | "ics_attck_json": "https://swimlane-pyattck.s3.us-west-2.amazonaws.com/merged_ics_attck_v1.json", 23 | "nist_controls_json": "https://swimlane-pyattck.s3.us-west-2.amazonaws.com/merged_nist_controls_v1.json", 24 | "generated_nist_json": "https://swimlane-pyattck.s3.us-west-2.amazonaws.com/attck_to_nist_controls.json", 25 | "config_file_path": os.path.abspath(os.path.expanduser(os.path.expandvars("~/pyattck/config.yml"))), 26 | "save_config": False, 27 | "use_config": False, 28 | "kwargs": {}, 29 | "config_data": {}, 30 | } 31 | 32 | ### Testing Default Configuration Settings 33 | 34 | 35 | def test_default_configuration_settings_set(attck_configuration): 36 | assert attck_configuration().use_config == False 37 | assert attck_configuration().save_config == False 38 | assert os.path.abspath( 39 | os.path.expanduser(os.path.expandvars(attck_configuration().config_file_path)) 40 | ) == os.path.abspath(os.path.expanduser(os.path.expandvars("~/pyattck/config.yml"))) 41 | assert os.path.abspath( 42 | os.path.expanduser(os.path.expandvars(attck_configuration().config.data_path)) 43 | ) == os.path.abspath(os.path.expanduser(os.path.expandvars(default_config_data.get("data_path")))) 44 | 45 | 46 | def test_configuration_save_config(attck_configuration): 47 | from pyattck import Attck, Configuration 48 | 49 | attck = Attck(save_config=True) 50 | assert attck.config.save_config == True 51 | assert isinstance(attck.config.config, Configuration) 52 | assert ( 53 | os.path.abspath(os.path.expanduser(os.path.expandvars(attck.config.config.data_path))) 54 | == attck_configuration().config.data_path 55 | ) 56 | 57 | 58 | @pytest.mark.parametrize( 59 | "target_attribute", 60 | [ 61 | "enterprise_attck_json", 62 | "pre_attck_json", 63 | "mobile_attck_json", 64 | "ics_attck_json", 65 | "nist_controls_json", 66 | "generated_nist_json", 67 | ], 68 | ) 69 | def test_default_configuration_settings_jsons(attck_configuration, target_attribute): 70 | from pyattck import Attck 71 | 72 | attck = Attck() 73 | assert getattr(attck.config.config, target_attribute) == default_config_data[target_attribute] 74 | 75 | 76 | def test_configuration_data_can_be_file_path_location(): 77 | from pyattck import Configuration, Options 78 | 79 | enterprise_temp_value = get_random_file_or_url() 80 | pre_attck_temp_value = get_random_file_or_url() 81 | mobile_temp_value = get_random_file_or_url() 82 | ics_temp_value = get_random_file_or_url() 83 | nist_controls_temp_value = get_random_file_or_url() 84 | generated_nist_temp_value = get_random_file_or_url() 85 | 86 | opt = Options( 87 | use_config=False, 88 | save_config=False, 89 | config=Configuration( 90 | enterprise_attck_json=enterprise_temp_value, 91 | pre_attck_json=pre_attck_temp_value, 92 | mobile_attck_json=mobile_temp_value, 93 | ics_attck_json=ics_temp_value, 94 | nist_controls_json=nist_controls_temp_value, 95 | generated_nist_json=generated_nist_temp_value, 96 | ), 97 | ) 98 | 99 | assert opt.config.enterprise_attck_json == enterprise_temp_value 100 | assert opt.config.pre_attck_json == pre_attck_temp_value 101 | assert opt.config.mobile_attck_json == mobile_temp_value 102 | assert opt.config.ics_attck_json == ics_temp_value 103 | assert opt.config.nist_controls_json == nist_controls_temp_value 104 | assert opt.config.generated_nist_json == generated_nist_temp_value 105 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 7 | 8 | ## [7.1.2](https://github.com/swimlane/pyattck/compare/7.1.1...7.1.2) (2023-05-16) 9 | 10 | 11 | ### Bug Fixes 12 | 13 | * Updating test.py examples ([cbfd1f3](https://github.com/swimlane/pyattck/commit/cbfd1f33d586dfcd9a15b3cbbc38daa39e20ccd1)) 14 | 15 | ## [7.1.1](https://github.com/swimlane/pyattck/compare/7.1.0...7.1.1) (2023-03-06) 16 | 17 | 18 | ### Bug Fixes 19 | 20 | * Added relationship property to the enterprise framework. Fixes [#131](https://github.com/swimlane/pyattck/issues/131) ([09c8c02](https://github.com/swimlane/pyattck/commit/09c8c02916540fc2d02e8e032214a9b5c1615bab)) 21 | 22 | ## [7.1.0](https://github.com/swimlane/pyattck/compare/7.0.0...7.1.0) (2023-03-06) 23 | 24 | 25 | ### Features 26 | 27 | * Adding campaigns attribute to Enterprise attack and related entities ([ecf5eba](https://github.com/swimlane/pyattck/commit/ecf5ebad4bd3f6e5639dc796051a582ef7aa7381)) 28 | 29 | 30 | ### Bug Fixes 31 | 32 | * Adding tests for campaigns ([40809bc](https://github.com/swimlane/pyattck/commit/40809bca9985a44101c558ef84ba4f926909a02c)) 33 | * Adding warning of deprecation of PreAttack framework since it is no longer officially supported by MITRE. Fixes [#126](https://github.com/swimlane/pyattck/issues/126) ([085828e](https://github.com/swimlane/pyattck/commit/085828e1ffd1a9ebc1f81b1c7cd26b1c145fc4af)) 34 | * Bumping minor version ([2d89017](https://github.com/swimlane/pyattck/commit/2d8901717e7edc26d6aecef7789b9ade1973e661)) 35 | * Incorporating fix [#129](https://github.com/swimlane/pyattck/issues/129) ([59ed9db](https://github.com/swimlane/pyattck/commit/59ed9dba5fd72ab64e4b3b7ed85d8dcbbb1b0586)) 36 | * Updated readme ([48528e1](https://github.com/swimlane/pyattck/commit/48528e18f3a37a9d863586bd779bcfe906b158d7)) 37 | * Updating campaigns tests ([58a2c3d](https://github.com/swimlane/pyattck/commit/58a2c3da2863a6628c6490f75a4cd31861adbb1a)) 38 | 39 | ## 7.0.0 - 2022-08-18 40 | 41 | - Added an interactive console menu system. You can access it by using the --interactive flag. 42 | 43 | ## 6.1.0 - 2022-06-13 44 | 45 | - Updated to pyattck-data 2.1.0 46 | 47 | ## 6.0.0 - 2022-06-09 48 | 49 | - BREAKING CHANGE RELEASE 50 | - Complete revamp and removed 70% of code base 51 | - CONSIDER THIS A NEW VERSION 52 | 53 | ## 5.4.0 - 2022-04-04 54 | 55 | - Added access to malwares from techniques (thanks aacienfuegos) 56 | - Access deprecated attribute from all MITRE ATT&CK objects (thanks aacienfuegos) 57 | - Updated documentation 58 | - Improved support of ICS framework (thanks cohmoti) 59 | - Bumped minor version 60 | 61 | ## 5.0.0 - 2021-10-22 62 | 63 | - Added new V10 data sources support 64 | - Added ICS Framework 65 | - Documentation updates 66 | 67 | ## 2.1.0 - 2020-08-25 68 | 69 | - Fixed issue with mitigations not being accessible in enterprise techniques 70 | - Added ability to access nested subtechniques (or not) using 71 | nested_techniques parameter when instantiating Attck object 72 | 73 | ## 2.0.5 - 2020-05-19 74 | 75 | - Fixed relationship links in enterprise malwares and techniques 76 | - Fixed retrieval of id property in preattack actors 77 | - Updated methods _set_wiki, _set_id, and _set_reference in each frameworks base classes 78 | 79 | ## 2.0.4 - 2020-05-15 80 | 81 | - Updated pendulum requirements version to have max version 82 | 83 | ## 2.0.3 - 2020-05-15 84 | 85 | - Updating pendulum requirements version 86 | 87 | ## 2.0.2 - 2020-05-08 88 | 89 | - Updated and modified docstrings across package 90 | 91 | ## 2.0.1 - 2020-05-06 92 | 93 | - Fixed issue with pre-attack and mobile attack technique id mappings 94 | 95 | ## 2.0.0 - 2020-02-14 96 | 97 | - Major update which includes external datasets to add additional context to MITRE ATT&CK 98 | - Restructured and created enterprise object type for future expansion into other MITRE ATT&CK Frameworks 99 | - Improved access and speed when accessing relationship objects 100 | - Added configuration settings and optional loading of datasets from local file paths 101 | 102 | ## 1.0.3 103 | 104 | - Fixed issue with appending techniques correctly 105 | 106 | ## 1.0.2 107 | 108 | - Updated Documentation 109 | 110 | ## 1.0.1 111 | 112 | - Updating Documentation with new reference links 113 | 114 | ## 1.0.0 115 | 116 | - Initial release of pyattck to PyPi 117 | -------------------------------------------------------------------------------- /pyattck/ics.py: -------------------------------------------------------------------------------- 1 | from pyattck_data.attack import MitreAttck 2 | from pyattck_data.nist import NistControls 3 | 4 | from .base import Base 5 | 6 | 7 | class ICSAttck(Base): 8 | """An interface to the MITRE ATT&CK ICS (Industrial Control Systems) Framework. 9 | 10 | This class creates an interface to all data points in the 11 | MITRE ATT&CK ICS framework. 12 | 13 | This interface enables you to retrieve all properties within 14 | each item in the MITRE ATT&CK ICS Framework. 15 | 16 | The following categorical items can be accessed using this class: 17 | 18 | 1. Tactics (Tactics are the phases defined by MITRE ATT&CK) 19 | 2. Techniques (Techniques are the individual actions which can 20 | accomplish a tactic) 21 | 3. Mitigations (Mitigations are recommendations to prevent or 22 | protect against a technique) 23 | 4. Malwares (Malwares are specific pieces of malware used by actors 24 | (or in general) to accomplish a technique) 25 | 5. Controls (Controls related to mitigations and techniques) 26 | 6. Data sources 27 | 7. Data Components 28 | 29 | As of pyattck 6.0.0, MITRE ATT&CK Frameworks are merged with generated datasets. 30 | These can be found [here](https://github.com/swimlane/pyattck-data) 31 | 32 | """ 33 | 34 | __tactics = [] 35 | __techniques = [] 36 | __mitigations = [] 37 | __malwares = [] 38 | __controls = [] 39 | __data_sources = [] 40 | __data_components = [] 41 | __nist_controls_json = NistControls(**Base.config.get_data("nist_controls_json")) 42 | __attck = MitreAttck(**Base.config.get_data("ics_attck_json")) 43 | 44 | @property 45 | def controls(self): 46 | """Retrieves Control objects. 47 | 48 | Returns: 49 | (Control) -- Returns a list of Control objects 50 | """ 51 | if not self.__controls: 52 | if self.__nist_controls_json.objects: 53 | self.__controls = [x for x in self.__nist_controls_json.objects if x.type == "course-of-action"] 54 | return self.__controls 55 | 56 | @property 57 | def data_components(self): 58 | """Retrieves DataComponent objects. 59 | 60 | Returns: 61 | (DataComponent) -- Returns a list of DataComponent objects 62 | """ 63 | if not self.__data_components: 64 | self.__data_components = [x for x in self.__attck.objects if x.type == "x-mitre-data-component"] 65 | return self.__data_components 66 | 67 | @property 68 | def data_sources(self): 69 | """Retrieves DataSource objects. 70 | 71 | Returns: 72 | (DataSource) -- Returns a list of DataSource objects 73 | """ 74 | if not self.__data_sources: 75 | self.__data_sources = [x for x in self.__attck.objects if x.type == "x-mitre-data-source"] 76 | return self.__data_sources 77 | 78 | @property 79 | def malwares(self): 80 | """Retrieves Malware objects. 81 | 82 | Returns: 83 | (Malware) -- Returns a list of Malware objects 84 | """ 85 | if not self.__malwares: 86 | self.__malwares = [x for x in self.__attck.objects if x.type == "malware"] 87 | return self.__malwares 88 | 89 | @property 90 | def mitigations(self): 91 | """Retrieves Mitigation objects. 92 | 93 | Returns: 94 | (Mitigation) -- (Returns a list of Mitigation objects) 95 | """ 96 | if not self.__mitigations: 97 | self.__mitigations = [x for x in self.__attck.objects if x.type == "course-of-action"] 98 | return self.__mitigations 99 | 100 | @property 101 | def tactics(self): 102 | """Retrieves Tactic objects. 103 | 104 | Returns: 105 | (Tactic) -- (Returns a list of Tactic objects) 106 | """ 107 | if not self.__tactics: 108 | self.__tactics = [x for x in self.__attck.objects if x.type == "x-mitre-tactic"] 109 | return self.__tactics 110 | 111 | @property 112 | def techniques(self): 113 | """Retrieves Technique objects. 114 | 115 | Returns: 116 | (Technique) -- Returns a list of Technique objects 117 | """ 118 | if not self.__techniques: 119 | for item in self.__attck.objects: 120 | if item.type == "attack-pattern": 121 | if item.techniques and not Base.config.nested_techniques: 122 | for i in item.techniques: 123 | self.__techniques.append(i) 124 | self.__techniques.append(item) 125 | return self.__techniques 126 | -------------------------------------------------------------------------------- /docs/ics.md: -------------------------------------------------------------------------------- 1 | # ICS (Industrial Control Systems) 2 | 3 | This documentation provides details about the ICSAttck class within the `pyattck` package. 4 | 5 | > The `MitreAttck` object is based on the following [data model](https://github.com/swimlane/pyattck-data-models/blob/main/src/pyattck_data_models/attack.py) 6 | 7 | The `ICSAttck` class provides detailed information about data within the ICS MITRE ATT&CK framework 8 | 9 | Each of the `main` properties (above) can return a json object of the entire object or you can access each property individually. An example of this is here: 10 | 11 | ```python 12 | from pyattck import Attck 13 | 14 | attack = Attck() 15 | 16 | # accessing techniques and their properties 17 | for technique in attack.ics.techniques: 18 | # if you want to return individual properties of this object you call them directly 19 | print(technique.id) 20 | print(technique.name) 21 | print(technique.alias) 22 | print(technique.description) 23 | print(technique.stix) 24 | print(technique.platforms) 25 | print(technique.permissions) 26 | print(technique.wiki) 27 | ..... 28 | ``` 29 | 30 | The following is only a small sample of the available properties on each object and each object type (malware, mitigations, tactics, and techniques) will have different properties that you can access. 31 | 32 | * Every data point has exposed properties that allow the user to retrieve additional data based on relationships: 33 | * [Malware](malware.md) 34 | * Techniques this malware is used with 35 | * [Mitigation](mitigation.md) 36 | * Techniques related to a specific set of mitigation suggestions 37 | * [Tactic](tactic.md) 38 | * Techniques found in a specific Tactic (phase) 39 | * [Technique](technique.md) 40 | * Relationship Objects 41 | * Tactics a technique is found in 42 | * Mitigation suggestions for a given technique 43 | * External Data 44 | * command_list - A list of commands from multiple open-source tools and repositories that contain potential commands used by a technique 45 | * commands - A list of property objects that contain the `Name`, `Source, and `Command` dataset 46 | * queries - A list of potential queries for different products to identify threats within your environment by technique 47 | * datasets - A list of the datasets as it relates to a technique 48 | * possible_detections - A list of potential detections for different products (e.g. NSM rules) as it relates to a technique 49 | * For more detailed information about these features, please view the following [External Datasets](https://github.com/swimlane/pyattck-data) 50 | 51 | 52 | Below shows you how you can access each of object types and their properties. Additionally, you can access related object types associated with this selected object type: 53 | 54 | ```python 55 | from pyattck import Attck 56 | 57 | attack = Attck() 58 | 59 | # accessing malware 60 | for malware in attack.ics.malwares: 61 | print(malware.id) 62 | print(malware.name) 63 | 64 | # accessing techniques that this malware is used in 65 | for technique in malware.techniques: 66 | print(technique.id) 67 | print(technique.name) 68 | 69 | # accessing mitigation 70 | for mitigation in attack.ics.mitigations: 71 | print(mitigation.id) 72 | print(mitigation.name) 73 | 74 | # accessing techniques related to mitigation recommendations 75 | for technique in mitigation.techniques: 76 | print(technique.id) 77 | print(technique.name) 78 | # you can also access generated data sets on aa technique 79 | print(technique.command_list) 80 | print(technique.commands) 81 | print(technique.queries) 82 | print(technique.datasets) 83 | print(technique.possible_detections) 84 | 85 | # accessing tactics 86 | for tactic in attack.ics.tactics: 87 | print(tactic.id) 88 | print(tactic.name) 89 | 90 | # accessing techniques related to this tactic 91 | for technique in tactic.techniques: 92 | print(technique.id) 93 | print(technique.name) 94 | # you can also access generated data sets on aa technique 95 | print(technique.command_list) 96 | print(technique.commands) 97 | print(technique.queries) 98 | print(technique.datasets) 99 | print(technique.possible_detections) 100 | 101 | # accessing techniques 102 | for technique in attack.ics.techniques: 103 | print(technique.id) 104 | print(technique.name) 105 | # you can also access generated data sets on aa technique 106 | print(technique.command_list) 107 | print(technique.commands) 108 | print(technique.queries) 109 | print(technique.datasets) 110 | print(technique.possible_detections) 111 | 112 | # accessing tactics that this technique belongs to 113 | for tactic in technique.tactics: 114 | print(tactic.id) 115 | print(tactic.name) 116 | 117 | # accessing mitigation recommendations for this technique 118 | for mitigation in technique.mitigations: 119 | print(mitigation.id) 120 | print(mitigation.name) 121 | ``` 122 | 123 | ## ICSAttck Class 124 | 125 | ```eval_rst 126 | .. autoclass:: pyattck.ics.ICSAttck 127 | :members: 128 | :undoc-members: 129 | :show-inheritance: 130 | ``` 131 | 132 | 133 | ```eval_rst 134 | .. toctree:: 135 | 136 | control 137 | malware 138 | mitigation 139 | tactic 140 | technique 141 | ``` 142 | -------------------------------------------------------------------------------- /pyattck/enterprise.py: -------------------------------------------------------------------------------- 1 | from pyattck_data.attack import MitreAttck 2 | from pyattck_data.nist import NistControls 3 | 4 | from .base import Base 5 | 6 | 7 | class EnterpriseAttck(Base): 8 | """An interface to the MITRE ATT&CK Enterprise Framework. 9 | 10 | This class creates an interface to all data points in the 11 | MITRE ATT&CK Enterprise framework. 12 | 13 | This interface enables you to retrieve all properties within 14 | each item in the MITRE ATT&CK Enterprise Framework. 15 | 16 | The following categorical items can be accessed using this class: 17 | 18 | 1. Actors 19 | 2. Campaigns 20 | 3. Controls 21 | 4. Data Sources 22 | 5. Data Components 23 | 6. Malware 24 | 7. Mitigations 25 | 8. Tactics 26 | 9. Techniques 27 | 10. Tools 28 | 29 | As of pyattck 6.0.0, MITRE ATT&CK Frameworks are merged with generated datasets. 30 | These can be found [here](https://github.com/swimlane/pyattck-data) 31 | """ 32 | 33 | __tactics = [] 34 | __techniques = [] 35 | __mitigations = [] 36 | __relationships = [] 37 | __actors = [] 38 | __campaigns = [] 39 | __tools = [] 40 | __malwares = [] 41 | __controls = [] 42 | __data_sources = [] 43 | __data_components = [] 44 | __nist_controls_json = NistControls(**Base.config.get_data("nist_controls_json")) 45 | __attck = MitreAttck(**Base.config.get_data("enterprise_attck_json")) 46 | 47 | @property 48 | def actors(self): 49 | """Retrieves Actor objects. 50 | 51 | Returns: 52 | (Actor) -- (Returns a list of Actor objects) 53 | """ 54 | if not self.__actors: 55 | self.__actors = [x for x in self.__attck.objects if x.type == "intrusion-set"] 56 | return self.__actors 57 | 58 | @property 59 | def campaigns(self): 60 | """Retrieves Campaign objects. 61 | 62 | Returns: 63 | (Campaign) -- (Returns a list of Campaign objects) 64 | """ 65 | if not self.__campaigns: 66 | self.__campaigns = [x for x in self.__attck.objects if x.type == "campaign"] 67 | return self.__campaigns 68 | 69 | @property 70 | def controls(self): 71 | """Retrieves Control objects. 72 | 73 | Returns: 74 | (Control) -- Returns a list of Control objects 75 | """ 76 | if not self.__controls: 77 | if self.__nist_controls_json.objects: 78 | self.__controls = [x for x in self.__nist_controls_json.objects if x.type == "course-of-action"] 79 | return self.__controls 80 | 81 | @property 82 | def data_components(self): 83 | """Retrieves DataComponent objects. 84 | 85 | Returns: 86 | (DataComponent) -- Returns a list of DataComponent objects 87 | """ 88 | if not self.__data_components: 89 | self.__data_components = [x for x in self.__attck.objects if x.type == "x-mitre-data-component"] 90 | return self.__data_components 91 | 92 | @property 93 | def data_sources(self): 94 | """Retrieves DataSource objects. 95 | 96 | Returns: 97 | (DataSource) -- Returns a list of DataSource objects 98 | """ 99 | if not self.__data_sources: 100 | self.__data_sources = [x for x in self.__attck.objects if x.type == "x-mitre-data-source"] 101 | return self.__data_sources 102 | 103 | @property 104 | def malwares(self): 105 | """Retrieves Malware objects. 106 | 107 | Returns: 108 | (Malware) -- Returns a list of Malware objects 109 | """ 110 | if not self.__malwares: 111 | self.__malwares = [x for x in self.__attck.objects if x.type == "malware"] 112 | return self.__malwares 113 | 114 | @property 115 | def mitigations(self): 116 | """Retrieves Mitigation objects. 117 | 118 | Returns: 119 | (Mitigation) -- (Returns a list of Mitigation objects) 120 | """ 121 | if not self.__mitigations: 122 | self.__mitigations = [x for x in self.__attck.objects if x.type == "course-of-action"] 123 | return self.__mitigations 124 | 125 | @property 126 | def relationships(self): 127 | """Retrieves Relationship objects. 128 | 129 | Returns: 130 | (Relationship) -- (Returns a list of Relationship objects) 131 | """ 132 | if not self.__relationships: 133 | self.__relationships = [x for x in self.__attck.objects if x.type == "relationship" and hasattr(x, 'relationship_type') and x.relationship_type == 'uses'] 134 | return self.__relationships 135 | 136 | @property 137 | def tactics(self): 138 | """Retrieves Tactic objects. 139 | 140 | Returns: 141 | (Tactic) -- (Returns a list of Tactic objects) 142 | """ 143 | if not self.__tactics: 144 | self.__tactics = [x for x in self.__attck.objects if x.type == "x-mitre-tactic"] 145 | return self.__tactics 146 | 147 | @property 148 | def techniques(self): 149 | """Retrieves Technique objects. 150 | 151 | Returns: 152 | (Technique) -- Returns a list of Technique objects 153 | """ 154 | if not self.__techniques: 155 | for item in self.__attck.objects: 156 | if item.type == "attack-pattern": 157 | if item.techniques and not Base.config.nested_techniques: 158 | for i in item.techniques: 159 | self.__techniques.append(i) 160 | self.__techniques.append(item) 161 | return self.__techniques 162 | 163 | @property 164 | def tools(self): 165 | """Retrieves Tool objects. 166 | 167 | Returns: 168 | (Tool) -- Returns a list of Tool objects 169 | """ 170 | if not self.__tools: 171 | self.__tools = [x for x in self.__attck.objects if x.type == "tool"] 172 | return self.__tools 173 | -------------------------------------------------------------------------------- /pyattck/configuration.py: -------------------------------------------------------------------------------- 1 | import json 2 | import os 3 | import warnings 4 | 5 | import yaml 6 | from attrs import asdict, define, field 7 | from pydantic import DirectoryPath, FilePath, HttpUrl 8 | from requests.api import request 9 | 10 | from .utils.exceptions import UnknownFileError 11 | from .utils.utils import get_absolute_path, is_path, is_url 12 | 13 | 14 | @define 15 | class Configuration: 16 | data_path: DirectoryPath = field(default="~/pyattck/data", converter=get_absolute_path) 17 | enterprise_attck_json: HttpUrl or FilePath = field( 18 | default="https://swimlane-pyattck.s3.us-west-2.amazonaws.com/merged_enterprise_attck_v1.json", 19 | converter=get_absolute_path, 20 | ) 21 | pre_attck_json: HttpUrl or FilePath = field( 22 | default="https://swimlane-pyattck.s3.us-west-2.amazonaws.com/merged_pre_attck_v1.json", 23 | converter=get_absolute_path, 24 | ) 25 | mobile_attck_json: HttpUrl or FilePath = field( 26 | default="https://swimlane-pyattck.s3.us-west-2.amazonaws.com/merged_mobile_attck_v1.json", 27 | converter=get_absolute_path, 28 | ) 29 | ics_attck_json: HttpUrl or FilePath = field( 30 | default="https://swimlane-pyattck.s3.us-west-2.amazonaws.com/merged_ics_attck_v1.json", 31 | converter=get_absolute_path, 32 | ) 33 | nist_controls_json: HttpUrl or FilePath = field( 34 | default="https://swimlane-pyattck.s3.us-west-2.amazonaws.com/merged_nist_controls_v1.json", 35 | converter=get_absolute_path, 36 | ) 37 | generated_nist_json: HttpUrl or FilePath = field( 38 | default="https://swimlane-pyattck.s3.us-west-2.amazonaws.com/attck_to_nist_controls.json", 39 | converter=get_absolute_path, 40 | ) 41 | 42 | @enterprise_attck_json.validator 43 | @pre_attck_json.validator 44 | @mobile_attck_json.validator 45 | @ics_attck_json.validator 46 | @nist_controls_json.validator 47 | @generated_nist_json.validator 48 | def _validate_json_value(self, attribute, value): 49 | path_valid = False 50 | url_valid = False 51 | path_valid = is_path(value) 52 | url_valid = is_url(value) 53 | if not path_valid and not url_valid: 54 | raise Exception("The provided value is neither a URL or file path") 55 | 56 | 57 | @define(frozen=True) 58 | class Options: 59 | nested_techniques: bool = field(default=False) 60 | use_config: bool = field(default=False) 61 | save_config: bool = field(default=False) 62 | config_file_path: FilePath = field(default="~/pyattck/config.yml", converter=get_absolute_path) 63 | config: Configuration = field(factory=Configuration) 64 | kwargs: dict = field(factory=dict) 65 | 66 | def _download_url_data(self, url): 67 | response = request("GET", url, **self.kwargs) 68 | if response.status_code == 200: 69 | return response.json() 70 | return {} 71 | 72 | def _read_from_disk(self, path): 73 | if os.path.exists(path) and os.path.isfile(path): 74 | try: 75 | with open(path) as f: 76 | if path.endswith(".json"): 77 | return json.load(f) 78 | elif path.endswith(".yml") or path.endswith(".yaml"): 79 | return Configuration(**yaml.load(f, Loader=yaml.SafeLoader)) 80 | else: 81 | raise UnknownFileError(provided_value=path, known_values=[".json", ".yml", ".yaml"]) 82 | except Exception as e: 83 | warnings.warn( 84 | message=f"The provided config file {path} is not in the correct format. " 85 | "Using default values instead." 86 | ) 87 | pass 88 | elif os.path.isdir(path): 89 | raise Exception(f"The provided path is a directory and must be a file: {path}") 90 | 91 | def _save_to_disk(self, path, data): 92 | try: 93 | if not os.path.exists(os.path.dirname(path)): 94 | try: 95 | os.makedirs(os.path.dirname(path)) 96 | except Exception as e: 97 | raise Exception( 98 | "pyattck attempted to create the provided directories but was unable to: {}".format(path) 99 | ) 100 | with open(path, "w+") as f: 101 | if path.endswith(".json"): 102 | json.dump(data, f) 103 | elif path.endswith(".yml") or path.endswith(".yaml"): 104 | yaml.dump(data, f) 105 | else: 106 | raise UnknownFileError(provided_value=path, known_values=[".json", ".yml", ".yaml"]) 107 | except IsADirectoryError as ie: 108 | raise Exception(f"The provided path is a directory: {path}") 109 | 110 | def _save_json_data(self, force: bool = False) -> None: 111 | if not os.path.exists(self.config.data_path): 112 | try: 113 | os.makedirs(self.config.data_path) 114 | except Exception as e: 115 | raise Exception("Unable to save data to the provided location: {}".format(self.config.data_path)) 116 | for json_data in [ 117 | "enterprise_attck_json", 118 | "pre_attck_json", 119 | "mobile_attck_json", 120 | "ics_attck_json", 121 | "nist_controls_json", 122 | "generated_nist_json", 123 | ]: 124 | if is_url(getattr(self.config, json_data)): 125 | try: 126 | path = os.path.join(self.config.data_path, f"{json_data}.json") 127 | if not os.path.exists(path) or force: 128 | data = self._download_url_data(getattr(self.config, json_data)) 129 | self._save_to_disk(path, data) 130 | except Exception as e: 131 | raise Warning(f"Unable to download data from {json_data}") 132 | return True 133 | 134 | def get_data(self, value: str) -> dict: 135 | """Retrieves saved data based on key value in config. 136 | 137 | Args: 138 | value (str): A key value in our configuration file. 139 | 140 | Returns: 141 | dict: The dictionary object which was retrieved. 142 | """ 143 | data = getattr(self.config, value) 144 | if is_url(data): 145 | return self._download_url_data(data) 146 | else: 147 | return self._read_from_disk(getattr(self.config, value)) 148 | 149 | def _save_config(self, config_file_path: str, config_dict: dict) -> None: 150 | """Saves the config to the provided path.""" 151 | self._save_to_disk(config_file_path, config_dict) 152 | self._save_json_data() 153 | 154 | def __attrs_post_init__(self): 155 | """Contains options and configuration for pyattck.""" 156 | if self.save_config: 157 | self._save_config(config_file_path=self.config_file_path, config_dict=asdict(self.config)) 158 | if self.use_config: 159 | self._save_config(config_file_path=self.config_file_path, config_dict=asdict(self.config)) 160 | object.__setattr__(self, "config", self._read_from_disk(self.config_file_path)) 161 | -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # Configuration file for the Sphinx documentation builder. 4 | # 5 | # This file does only contain a selection of the most common options. For a 6 | # full list see the documentation: 7 | # http://www.sphinx-doc.org/en/master/config 8 | 9 | # -- Path setup -------------------------------------------------------------- 10 | 11 | # If extensions (or modules to document with autodoc) are in another directory, 12 | # add these directories to sys.path here. If the directory is relative to the 13 | # documentation root, use os.path.abspath to make it absolute, like shown here. 14 | # 15 | 16 | import os 17 | import sys 18 | 19 | os.chdir(os.path.dirname(__file__)) 20 | sys.path.insert(0, os.path.abspath("..")) 21 | 22 | import pyattck 23 | 24 | # -- Project information ----------------------------------------------------- 25 | 26 | project = "pyattck" 27 | copyright = "2021, Swimlane" 28 | author = "Swimlane, Josh Rickard (MSAdministrator)" 29 | 30 | # The short X.Y version 31 | version = "2.0" 32 | # The full version, including alpha/beta/rc tags 33 | release = "2.0.0" 34 | 35 | # import pyattck 36 | 37 | # from pyattck import Attck 38 | # from pyattck import (actor, attckobject, malware, mitigation, tactic, technique, tools) 39 | # -- General configuration --------------------------------------------------- 40 | 41 | # If your documentation needs a minimal Sphinx version, state it here. 42 | # 43 | # needs_sphinx = '1.0' 44 | 45 | # Add any Sphinx extension module names here, as strings. They can be 46 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 47 | # ones. 48 | extensions = [ 49 | "sphinx.ext.autodoc", 50 | "sphinx.ext.todo", 51 | "sphinx.ext.coverage", 52 | "sphinx.ext.viewcode", 53 | "sphinx.ext.githubpages", 54 | "recommonmark", 55 | "sphinx.ext.intersphinx", 56 | ] 57 | 58 | 59 | # Add any paths that contain templates here, relative to this directory. 60 | templates_path = ["_templates"] 61 | 62 | # The suffix(es) of source filenames. 63 | # You can specify multiple suffix as a list of string: 64 | # 65 | # source_suffix = ['.rst', '.md'] 66 | source_suffix = ".rst" 67 | 68 | # The master toctree document. 69 | master_doc = "index" 70 | 71 | # The language for content autogenerated by Sphinx. Refer to documentation 72 | # for a list of supported languages. 73 | # 74 | # This is also used if you do content translation via gettext catalogs. 75 | # Usually you set "language" from the command line for these cases. 76 | language = "en" 77 | 78 | # List of patterns, relative to source directory, that match files and 79 | # directories to ignore when looking for source files. 80 | # This pattern also affects html_static_path and html_extra_path. 81 | exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"] 82 | 83 | # The name of the Pygments (syntax highlighting) style to use. 84 | pygments_style = None 85 | 86 | 87 | # -- Options for HTML output ------------------------------------------------- 88 | 89 | # The theme to use for HTML and HTML Help pages. See the documentation for 90 | # a list of builtin themes. 91 | # 92 | html_theme = "alabaster" 93 | 94 | # Theme options are theme-specific and customize the look and feel of a theme 95 | # further. For a list of options available for each theme, see the 96 | # documentation. 97 | # 98 | # html_theme_options = {} 99 | 100 | # Add any paths that contain custom static files (such as style sheets) here, 101 | # relative to this directory. They are copied after the builtin static files, 102 | # so a file named "default.css" will overwrite the builtin "default.css". 103 | html_static_path = ["_static"] 104 | 105 | # Custom sidebar templates, must be a dictionary that maps document names 106 | # to template names. 107 | # 108 | # The default sidebars (for documents that don't match any pattern) are 109 | # defined by theme itself. Builtin themes are using these templates by 110 | # default: ``['localtoc.html', 'relations.html', 'sourcelink.html', 111 | # 'searchbox.html']``. 112 | # 113 | # html_sidebars = {} 114 | 115 | 116 | # -- Options for HTMLHelp output --------------------------------------------- 117 | 118 | # Output file base name for HTML help builder. 119 | htmlhelp_basename = "pyattckdoc" 120 | 121 | 122 | # -- Options for LaTeX output ------------------------------------------------ 123 | 124 | latex_elements = { 125 | # The paper size ('letterpaper' or 'a4paper'). 126 | # 127 | # 'papersize': 'letterpaper', 128 | # The font size ('10pt', '11pt' or '12pt'). 129 | # 130 | # 'pointsize': '10pt', 131 | # Additional stuff for the LaTeX preamble. 132 | # 133 | # 'preamble': '', 134 | # Latex figure (float) alignment 135 | # 136 | # 'figure_align': 'htbp', 137 | } 138 | 139 | # Grouping the document tree into LaTeX files. List of tuples 140 | # (source start file, target name, title, 141 | # author, documentclass [howto, manual, or own class]). 142 | latex_documents = [ 143 | (master_doc, "pyattck.tex", "pyattck Documentation", "Swimlane, Josh Rickard (MSAdministrator)", "manual"), 144 | ] 145 | 146 | 147 | # -- Options for manual page output ------------------------------------------ 148 | 149 | # One entry per manual page. List of tuples 150 | # (source start file, name, description, authors, manual section). 151 | man_pages = [(master_doc, "pyattck", "pyattck Documentation", [author], 1)] 152 | 153 | 154 | # -- Options for Texinfo output ---------------------------------------------- 155 | 156 | # Grouping the document tree into Texinfo files. List of tuples 157 | # (source start file, target name, title, author, 158 | # dir menu entry, description, category) 159 | texinfo_documents = [ 160 | ( 161 | master_doc, 162 | "pyattck", 163 | "pyattck Documentation", 164 | author, 165 | "pyattck", 166 | "One line description of project.", 167 | "Miscellaneous", 168 | ), 169 | ] 170 | 171 | 172 | # -- Options for Epub output ------------------------------------------------- 173 | 174 | # Bibliographic Dublin Core info. 175 | epub_title = project 176 | 177 | # The unique identifier of the text. This can be a ISBN number 178 | # or the project homepage. 179 | # 180 | # epub_identifier = '' 181 | 182 | # A unique identification for the text. 183 | # 184 | # epub_uid = '' 185 | 186 | # A list of files that should not be packed into the epub file. 187 | epub_exclude_files = ["search.html"] 188 | 189 | 190 | # -- Extension configuration ------------------------------------------------- 191 | 192 | from recommonmark.transform import AutoStructify 193 | 194 | github_doc_root = "https://github.com/rtfd/recommonmark/tree/master/doc/" 195 | 196 | 197 | def setup(app): 198 | app.add_config_value( 199 | "recommonmark_config", 200 | { 201 | "url_resolver": lambda url: github_doc_root + url, 202 | "auto_toc_tree_section": "Contents", 203 | }, 204 | True, 205 | ) 206 | app.add_transform(AutoStructify) 207 | 208 | 209 | # from recommonmark.parser import CommonMarkParser 210 | 211 | # source_parsers = { 212 | # '.md': CommonMarkParser, 213 | # } 214 | 215 | source_suffix = [".rst", ".md"] 216 | 217 | # -- Options for todo extension ---------------------------------------------- 218 | 219 | # If true, `todo` and `todoList` produce output, else they produce nothing. 220 | todo_include_todos = True 221 | 222 | autoclass_content = "class" 223 | autodoc_class_signature = "separated" 224 | -------------------------------------------------------------------------------- /pyattck/utils/layout.py: -------------------------------------------------------------------------------- 1 | """Used to format interactive layout display for individual attack component types.""" 2 | from datetime import datetime 3 | from time import sleep 4 | 5 | from rich import box 6 | from rich.align import Align 7 | from rich.console import Group 8 | from rich.layout import Layout 9 | from rich.live import Live 10 | from rich.panel import Panel 11 | from rich.table import Table 12 | from rich.text import Text 13 | 14 | from ..base import Base 15 | 16 | 17 | class Footer(Base): 18 | """Display footer references.""" 19 | 20 | def __init__(self, item): 21 | """Provided the object type item we generate references and display them on the footer. 22 | 23 | The following attack types are expected: 24 | 25 | "actors","controls","data_components","data_sources","malwares","mitigations","tactics","techniques","tools" 26 | 27 | Args: 28 | item (Any): A attack type object. 29 | """ 30 | self.item = item 31 | 32 | def __rich__(self) -> Panel: 33 | """Generates a Panel object with the correct relationships. 34 | 35 | Returns: 36 | Panel: A footer relationship Panel object. 37 | """ 38 | grid = Table.grid(expand=True) 39 | grid.add_column(justify="center", ratio=1) 40 | grid.add_column(justify="right") 41 | panel_list = [] 42 | for item in dir(self.item): 43 | if not item.startswith("_") and item in self.ATTCK_TYPES: 44 | if getattr(self.item, item): 45 | name_list = set() 46 | for i in getattr(self.item, item): 47 | name_list.add(getattr(i, "name")) 48 | panel_list.append( 49 | Panel( 50 | ", ".join([x for x in list(name_list)]), 51 | title=f"[b]{item}", 52 | border_style="red", 53 | padding=(1, 2), 54 | ), 55 | ) 56 | grid.add_row(*panel_list) 57 | return Panel(grid, style="white on blue") 58 | 59 | 60 | class Header: 61 | """Display header with clock.""" 62 | 63 | def __init__(self, item): 64 | """Provided the object type item we generate references and display them on the footer. 65 | 66 | The following attack types are expected: 67 | 68 | "actors","controls","data_components","data_sources","malwares","mitigations","tactics","techniques","tools" 69 | 70 | Args: 71 | item (Any): A attack type object. 72 | """ 73 | self.item = item 74 | 75 | def __rich__(self) -> Panel: 76 | """Returns the header which contains the items name and any aliases. 77 | 78 | Returns: 79 | Panel: A header Panel object. 80 | """ 81 | grid = Table.grid(expand=True) 82 | grid.add_column(justify="center", ratio=1) 83 | grid.add_column(justify="right") 84 | if hasattr(self.item, "aliases"): 85 | text = f"[b]{self.item.name}[/b] - Known Aliases: {', '.join([x for x in self.item.aliases])}" 86 | else: 87 | text = f"[b]{self.item.name}[/b]" 88 | 89 | grid.add_row( 90 | text, 91 | datetime.now().ctime().replace(":", "[blink]:[/]"), 92 | ) 93 | return Panel(grid, style="white on blue") 94 | 95 | 96 | class CustomLayout(Base): 97 | """Used to generate a custom layout for pyattck object items.""" 98 | 99 | def __init__(self, item): 100 | """Provided the object type item we generate references and display them on the footer. 101 | 102 | The following attack types are expected: 103 | 104 | "actors","controls","data_components","data_sources","malwares","mitigations","tactics","techniques","tools" 105 | 106 | Args: 107 | item (Any): A attack type object. 108 | """ 109 | self.item = item 110 | layout = self.make_layout() 111 | layout["header"].update(Header(self.item)) 112 | layout["body"].update(self.make_general_information()) 113 | layout["side"].update(Panel(self.make_top_left_box(), title="Details", border_style="red")) 114 | layout["footer"].update(Footer(self.item)) 115 | self.layout = layout 116 | 117 | def run(self): 118 | """Main callable for the CustomLayout class. 119 | 120 | This method gets called within the Menu class when a wrapped object is passed to display this 121 | custom layout within the console. 122 | """ 123 | with Live(self.layout, refresh_per_second=10, screen=True, redirect_stderr=False) as live: 124 | try: 125 | while True: 126 | sleep(1) 127 | except KeyboardInterrupt: 128 | pass 129 | 130 | def make_layout(self) -> Layout: 131 | """Defines the console layout.""" 132 | layout = Layout(name="root") 133 | 134 | layout.split( 135 | Layout(name="header", size=3), 136 | Layout(name="main", ratio=1), 137 | Layout(name="footer", size=7), 138 | ) 139 | layout["main"].split_row( 140 | Layout(name="side"), 141 | Layout(name="body", ratio=2, minimum_size=60), 142 | ) 143 | return layout 144 | 145 | def _get_external_id(self, attck_object) -> str: 146 | """Retrieves the official MITRE ID from an objects external references. 147 | 148 | Args: 149 | attck_object (Any): A MITRE ATTCK object type. 150 | 151 | Returns: 152 | str: The official designated MITRE ATT&CK ID. 153 | """ 154 | if hasattr(attck_object, "external_references"): 155 | for item in getattr(attck_object, "external_references"): 156 | if ( 157 | hasattr(item, "external_id") 158 | and getattr(item, "external_id") 159 | and not getattr(item, "external_id").startswith("CAPEC") 160 | ): 161 | return getattr(item, "external_id") 162 | 163 | def make_top_left_box(self): 164 | """Creates text string used in the Details section.""" 165 | return f""" 166 | [bold cyan1]ID: [/]{self._get_external_id(self.item)} 167 | [bold cyan1]Revoked: [/]{self.item.revoked if hasattr(self.item, 'revoked') else 'False'} 168 | [bold cyan1]Type: [/]{self.item.type} 169 | [bold cyan1]STIX: [/]{self.item.id} 170 | [bold cyan1]Created: [/]{self.item.created} 171 | [bold cyan1]Modified: [/]{self.item.modified} 172 | """ 173 | 174 | def make_general_information(self) -> Panel: 175 | """Generates the Panel containing the general information section.""" 176 | sponsor_message = Table.grid(padding=1) 177 | sponsor_message.add_column(style="green", justify="right") 178 | sponsor_message.add_column(no_wrap=True) 179 | 180 | for ref in self.item.external_references: 181 | if ref.url: 182 | sponsor_message.add_row( 183 | f"{ref.source_name} - {ref.url} - {ref.description}", 184 | f"[u blue link={ref.url}]", 185 | ) 186 | 187 | if self.item.description: 188 | intro_message = Text.from_markup(self.item.description.replace("[", "")) 189 | else: 190 | intro_message = Text.from_markup("UNKNOWN") 191 | 192 | message = Table.grid(padding=1) 193 | message.add_column() 194 | message.add_column(no_wrap=True) 195 | message.add_row(intro_message, sponsor_message) 196 | 197 | message_panel = Panel( 198 | Align.center( 199 | Group(intro_message, "\n", Align.center(sponsor_message)), 200 | vertical="middle", 201 | ), 202 | box=box.ROUNDED, 203 | padding=(1, 2), 204 | title="[b red]General Information", 205 | border_style="bright_blue", 206 | ) 207 | return message_panel 208 | -------------------------------------------------------------------------------- /docs/mobile.md: -------------------------------------------------------------------------------- 1 | # MobileAttck 2 | 3 | This documentation provides details about the `MobileAttck` class within the `pyattck` package. 4 | 5 | > The `MitreAttck` object is based on the following [data model](https://github.com/swimlane/pyattck-data-models/blob/main/src/pyattck_data_models/attack.py) 6 | 7 | The `MobileAttck` class provides detailed information about data within the MITRE ATT&CK Mobile framework 8 | 9 | Each of the main properties can return a json object of the entire object or you can access each property individually. An example of this is here: 10 | 11 | ```python 12 | from pyattck import Attck 13 | 14 | attack = Attck() 15 | 16 | # accessing techniques and their properties 17 | for technique in attack.mobile.techniques: 18 | # if you want to return individual properties of this object you call them directly 19 | print(technique.id) 20 | print(technique.name) 21 | print(technique.alias) 22 | print(technique.description) 23 | print(technique.stix) 24 | print(technique.platforms) 25 | ..... 26 | ``` 27 | 28 | The following is only a small sample of the available properties on each object and each object type (actors, malware, mitigations, tactics, techniques, and tools) will have different properties that you can access. 29 | 30 | 31 | * Every data point has exposed properties that allow the user to retrieve additional data based on relationships: 32 | * [Actor](actor.md) 33 | * Relationship Objects 34 | * Tools used by the Actor or Group 35 | * Malware used by the Actor or Group 36 | * Techniques this Actor or Group uses 37 | * External Data 38 | * country which this actor or group may be associated with (attribution is hard) 39 | * operations 40 | * attribution_links 41 | * known_tools 42 | * targets 43 | * additional_comments 44 | * external_description 45 | * [Malware](malware.md) 46 | * Actor or Group(s) using this malware 47 | * Techniques this malware is used with 48 | * [Mitigation](mitigation.md) 49 | * Techniques related to a specific set of mitigation suggestions 50 | * [Tactic](tactic.md) 51 | * Techniques found in a specific Tactic (phase) 52 | * [Technique](technique.md) 53 | * Relationship Objects 54 | * Tactics a technique is found in 55 | * Mitigation suggestions for a given technique 56 | * Actor or Group(s) identified as using this technique 57 | * External Data 58 | * command_list - A list of commands from multiple open-source tools and repositories that contain potential commands used by a technique 59 | * commands - A list of property objects that contain the `Name`, `Source, and `Command` dataset 60 | * queries - A list of potential queries for different products to identify threats within your environment by technique 61 | * datasets - A list of the datasets as it relates to a technique 62 | * possible_detections - A list of potential detections for different products (e.g. NSM rules) as it relates to a technique 63 | * For more detailed information about these features, please view the following [External Datasets](https://github.com/swimlane/pyattck-data) 64 | * [Tools](tools.md) 65 | * Relationship Objects 66 | * Techniques that the specified tool is used within 67 | * Actor or Group(s) using a specified tool 68 | * External Data 69 | * additional_names for the specified tool 70 | * attribution_links associated with the specified tool 71 | * additional_comments about the specified tool 72 | * family of the specified tool 73 | 74 | 75 | 76 | Below shows you how you can access each of object types and their properties. Additionally, you can access related object types associated with this selected object type: 77 | 78 | ```python 79 | from pyattck import Attck 80 | 81 | attack = Attck() 82 | 83 | for actor in attack.mobile.actors: 84 | print(actor.id) 85 | print(actor.name) 86 | 87 | # accessing malware used by an actor or group 88 | for malware in actor.malwares: 89 | print(malware.id) 90 | print(malware.name) 91 | 92 | # accessing tools used by an actor or group 93 | for tool in actor.tools: 94 | print(tool.id) 95 | print(tool.name) 96 | 97 | # accessing techniques used by an actor or group 98 | for technique in actor.techniques: 99 | print(technique.id) 100 | print(technique.name) 101 | # you can also access generated data sets on aa technique 102 | print(technique.command_list) 103 | print(technique.commands) 104 | print(technique.queries) 105 | print(technique.datasets) 106 | print(technique.possible_detections) 107 | 108 | # accessing malware 109 | for malware in attack.mobile.malwares: 110 | print(malware.id) 111 | print(malware.name) 112 | 113 | # accessing actor or groups using this malware 114 | for actor in malware.actors: 115 | print(actor.id) 116 | print(actor.name) 117 | 118 | # accessing techniques that this malware is used in 119 | for technique in malware.techniques: 120 | print(technique.id) 121 | print(technique.name) 122 | 123 | # accessing mitigation 124 | for mitigation in attack.mobile.mitigations: 125 | print(mitigation.id) 126 | print(mitigation.name) 127 | 128 | # accessing techniques related to mitigation recommendations 129 | for technique in mitigation.techniques: 130 | print(technique.id) 131 | print(technique.name) 132 | # you can also access generated data sets on aa technique 133 | print(technique.command_list) 134 | print(technique.commands) 135 | print(technique.queries) 136 | print(technique.datasets) 137 | print(technique.possible_detections) 138 | 139 | # accessing tactics 140 | for tactic in attack.mobile.tactics: 141 | print(tactic.id) 142 | print(tactic.name) 143 | 144 | # accessing techniques related to this tactic 145 | for technique in tactic.techniques: 146 | print(technique.id) 147 | print(technique.name) 148 | # you can also access generated data sets on aa technique 149 | print(technique.command_list) 150 | print(technique.commands) 151 | print(technique.queries) 152 | print(technique.datasets) 153 | print(technique.possible_detections) 154 | 155 | # accessing techniques 156 | for technique in attack.mobile.techniques: 157 | print(technique.id) 158 | print(technique.name) 159 | # you can also access generated data sets on aa technique 160 | print(technique.command_list) 161 | print(technique.commands) 162 | print(technique.queries) 163 | print(technique.datasets) 164 | print(technique.possible_detections) 165 | 166 | # accessing tactics that this technique belongs to 167 | for tactic in technique.tactics: 168 | print(tactic.id) 169 | print(tactic.name) 170 | 171 | # accessing mitigation recommendations for this technique 172 | for mitigation in technique.mitigations: 173 | print(mitigation.id) 174 | print(mitigation.name) 175 | 176 | # accessing actors using this technique 177 | for actor in technique.actors: 178 | print(actor.id) 179 | print(actor.name) 180 | 181 | # accessing tools 182 | for tool in attack.mobile.tools: 183 | print(tool.id) 184 | print(tool.name) 185 | 186 | # accessing techniques this tool is used in 187 | for technique in tool.techniques: 188 | print(technique.id) 189 | print(technique.name) 190 | # you can also access generated data sets on aa technique 191 | print(technique.command_list) 192 | print(technique.commands) 193 | print(technique.queries) 194 | print(technique.datasets) 195 | print(technique.possible_detections) 196 | 197 | # accessing actor or groups using this tool 198 | for actor in tool.actors: 199 | print(actor.id) 200 | print(actor.name) 201 | ``` 202 | 203 | ## MobileAttck Class 204 | 205 | ```eval_rst 206 | .. autoclass:: pyattck.mobile.MobileAttck 207 | :members: 208 | :undoc-members: 209 | :show-inheritance: 210 | ``` 211 | 212 | 213 | ```eval_rst 214 | .. toctree:: 215 | 216 | actor 217 | malware 218 | mitigation 219 | tactic 220 | technique 221 | tools 222 | ``` 223 | -------------------------------------------------------------------------------- /docs/enterprise.md: -------------------------------------------------------------------------------- 1 | # Enterprise 2 | 3 | This documentation provides details about the Enterprise class within the `pyattck` package. 4 | 5 | > The `MitreAttck` object is based on the following [data model](https://github.com/swimlane/pyattck-data-models/blob/main/src/pyattck_data_models/attack.py) 6 | 7 | The `Enterprise` class provides detailed information about data within the Enterprise MITRE ATT&CK framework 8 | 9 | Each of the main properties can return a json object of the entire object or you can access each property individually. An example of this is here: 10 | 11 | ```python 12 | from pyattck import Attck 13 | 14 | attack = Attck() 15 | 16 | # accessing techniques and their properties 17 | for technique in attack.enterprise.techniques: 18 | # if you want to return individual properties of this object you call them directly 19 | print(technique.id) 20 | print(technique.name) 21 | print(technique.alias) 22 | print(technique.description) 23 | print(technique.stix) 24 | print(technique.platforms) 25 | print(technique.permissions) 26 | print(technique.wiki) 27 | ..... 28 | ``` 29 | 30 | The following is only a small sample of the available properties on each object and each object type (actors, malware, mitigations, tactics, techniques, and tools) will have different properties that you can access. 31 | 32 | 33 | * Every data point has exposed properties that allow the user to retrieve additional data based on relationships: 34 | * [Actor](actor.md) 35 | * Relationship Objects 36 | * Tools used by the Actor or Group 37 | * Malware used by the Actor or Group 38 | * Techniques this Actor or Group uses 39 | * External Data 40 | * country which this actor or group may be associated with (attribution is hard) 41 | * operations 42 | * attribution_links 43 | * known_tools 44 | * targets 45 | * additional_comments 46 | * external_description 47 | * [Malware](malware.md) 48 | * Actor or Group(s) using this malware 49 | * Techniques this malware is used with 50 | * [Mitigation](mitigation.md) 51 | * Techniques related to a specific set of mitigation suggestions 52 | * [Tactic](tactic.md) 53 | * Techniques found in a specific Tactic (phase) 54 | * [Technique](technique.md) 55 | * Relationship Objects 56 | * Tactics a technique is found in 57 | * Mitigation suggestions for a given technique 58 | * Actor or Group(s) identified as using this technique 59 | * External Data 60 | * command_list - A list of commands from multiple open-source tools and repositories that contain potential commands used by a technique 61 | * commands - A list of property objects that contain the `Name`, `Source, and `Command` dataset 62 | * queries - A list of potential queries for different products to identify threats within your environment by technique 63 | * datasets - A list of the datasets as it relates to a technique 64 | * possible_detections - A list of potential detections for different products (e.g. NSM rules) as it relates to a technique 65 | * For more detailed information about these features, please view the following [External Datasets](https://github.com/swimlane/pyattck-data) 66 | * [Tools](tools.md) 67 | * Relationship Objects 68 | * Techniques that the specified tool is used within 69 | * Actor or Group(s) using a specified tool 70 | * External Data 71 | * additional_names for the specified tool 72 | * attribution_links associated with the specified tool 73 | * additional_comments about the specified tool 74 | * family of the specified tool 75 | 76 | Below shows you how you can access each of object types and their properties. Additionally, you can access related object types associated with this selected object type: 77 | 78 | ```python 79 | from pyattck import Attck 80 | 81 | attack = Attck() 82 | 83 | for actor in attack.enterprise.actors: 84 | print(actor.id) 85 | print(actor.name) 86 | 87 | # accessing malware used by an actor or group 88 | for malware in actor.malwares: 89 | print(malware.id) 90 | print(malware.name) 91 | 92 | # accessing tools used by an actor or group 93 | for tool in actor.tools: 94 | print(tool.id) 95 | print(tool.name) 96 | 97 | # accessing techniques used by an actor or group 98 | for technique in actor.techniques: 99 | print(technique.id) 100 | print(technique.name) 101 | # you can also access generated data sets on aa technique 102 | print(technique.command_list) 103 | print(technique.commands) 104 | print(technique.queries) 105 | print(technique.datasets) 106 | print(technique.possible_detections) 107 | 108 | # accessing malware 109 | for malware in attack.enterprise.malwares: 110 | print(malware.id) 111 | print(malware.name) 112 | 113 | # accessing actor or groups using this malware 114 | for actor in malware.actors: 115 | print(actor.id) 116 | print(actor.name) 117 | 118 | # accessing techniques that this malware is used in 119 | for technique in malware.techniques: 120 | print(technique.id) 121 | print(technique.name) 122 | 123 | # accessing mitigation 124 | for mitigation in attack.enterprise.mitigations: 125 | print(mitigation.id) 126 | print(mitigation.name) 127 | 128 | # accessing techniques related to mitigation recommendations 129 | for technique in mitigation.techniques: 130 | print(technique.id) 131 | print(technique.name) 132 | # you can also access generated data sets on aa technique 133 | print(technique.command_list) 134 | print(technique.commands) 135 | print(technique.queries) 136 | print(technique.datasets) 137 | print(technique.possible_detections) 138 | 139 | # accessing tactics 140 | for tactic in attack.enterprise.tactics: 141 | print(tactic.id) 142 | print(tactic.name) 143 | 144 | # accessing techniques related to this tactic 145 | for technique in tactic.techniques: 146 | print(technique.id) 147 | print(technique.name) 148 | # you can also access generated data sets on aa technique 149 | print(technique.command_list) 150 | print(technique.commands) 151 | print(technique.queries) 152 | print(technique.datasets) 153 | print(technique.possible_detections) 154 | 155 | # accessing techniques 156 | for technique in attack.enterprise.techniques: 157 | print(technique.id) 158 | print(technique.name) 159 | # you can also access generated data sets on aa technique 160 | print(technique.command_list) 161 | print(technique.commands) 162 | print(technique.queries) 163 | print(technique.datasets) 164 | print(technique.possible_detections) 165 | 166 | # accessing tactics that this technique belongs to 167 | for tactic in technique.tactics: 168 | print(tactic.id) 169 | print(tactic.name) 170 | 171 | # accessing mitigation recommendations for this technique 172 | for mitigation in technique.mitigations: 173 | print(mitigation.id) 174 | print(mitigation.name) 175 | 176 | # accessing actors using this technique 177 | for actor in technique.actors: 178 | print(actor.id) 179 | print(actor.name) 180 | 181 | # accessing tools 182 | for tool in attack.enterprise.tools: 183 | print(tool.id) 184 | print(tool.name) 185 | 186 | # accessing techniques this tool is used in 187 | for technique in tool.techniques: 188 | print(technique.id) 189 | print(technique.name) 190 | # you can also access generated data sets on aa technique 191 | print(technique.command_list) 192 | print(technique.commands) 193 | print(technique.queries) 194 | print(technique.datasets) 195 | print(technique.possible_detections) 196 | 197 | # accessing actor or groups using this tool 198 | for actor in tool.actors: 199 | print(actor.id) 200 | print(actor.name) 201 | ``` 202 | 203 | ## Enterprise Class 204 | 205 | ```eval_rst 206 | .. autoclass:: pyattck.enterprise.Enterprise 207 | :members: 208 | :undoc-members: 209 | :show-inheritance: 210 | ``` 211 | 212 | 213 | ```eval_rst 214 | .. toctree:: 215 | 216 | actor 217 | control 218 | malware 219 | mitigation 220 | tactic 221 | technique 222 | tools 223 | ``` 224 | -------------------------------------------------------------------------------- /pyattck/attck.py: -------------------------------------------------------------------------------- 1 | import warnings 2 | 3 | from .base import Base 4 | from .configuration import Configuration, Options 5 | 6 | 7 | class Attck(Base): 8 | 9 | """Interface to all MITRE ATT&CK frameworks. 10 | 11 | Currently, this class enables access to the Enterprise & PRE-ATT&CK 12 | frameworks with others coming soon. To access each framework, use 13 | the following properties 14 | 15 | * enterprise 16 | * preattack 17 | * ics 18 | * mobile 19 | 20 | This interface enables you to retrieve all properties within each 21 | item in the MITRE ATT&CK Frameworks (as applicable). 22 | 23 | The following categorical items can be accessed using this class: 24 | 25 | 1. Tactics (Tactics are the phases defined by MITRE ATT&CK) 26 | 2. Techniques (Techniques are the individual actions which can 27 | accomplish a tactic) 28 | 3. Mitigations (Mitigations are recommendations to prevent or 29 | protect against a technique) 30 | 4. Actors (Actors or Groups are identified malicious 31 | actors/groups which have been identified and documented by 32 | MITRE & third-parties) 33 | 5. Tools (Tools are software used to perform techniques) 34 | 6. Malwares (Malwares are specific pieces of malware used by 35 | actors (or in general) to accomplish a technique) 36 | 37 | You can access additional datasets related to a technique. 38 | These datasets are [documented here](https://github.com/swimlane/pyattck-data). 39 | 40 | Example: 41 | Once an `Attck` object is instantiated, you can access each object 42 | type as a list of objects (e.g. techniques, tactics, actors, etc.) 43 | 44 | You can iterate over each object list and access specific properties 45 | and relationship properties of each. 46 | 47 | The following relationship properties are accessible: 48 | 1. Actors 49 | 1. Tools used by the Actor or Group 50 | 2. Malware used by the Actor or Group 51 | 3. Techniques this Actor or Group uses 52 | 2. Malwares 53 | 1. Actor or Group(s) using this malware 54 | 2. Techniques this malware is used with 55 | 3. Mitigations 56 | 1. Techniques related to a specific set of mitigation suggestions 57 | 4. Tactics 58 | 1. Techniques found in a specific Tactic (phase) 59 | 5. Techniques 60 | 1. Tactics a technique is found in 61 | 2. Mitigation suggestions for a given technique 62 | 3. Actor or Group(s) identified as using this technique 63 | 6. Tools 64 | 1. Techniques that the specified tool is used within 65 | 2. Actor or Group(s) using a specified tool 66 | 67 | 1. To iterate over a list, do the following: 68 | 69 | .. code-block:: python 70 | 71 | from pyattck import Attck 72 | 73 | attck = Attck() 74 | 75 | for technique in attck.enterprise.techniques: 76 | print(technique.id) 77 | print(technique.name) 78 | print(technique.description) 79 | # etc. 80 | for mitigation in attck.enterprise.mitigations: 81 | print(mitigation.id) 82 | print(mitigation.name) 83 | print(mitigation.description) 84 | # etc. 85 | 86 | 2. To access relationship properties, do the following: 87 | 88 | .. code-block:: python 89 | 90 | from pyattck import Attck 91 | 92 | attck = Attck() 93 | 94 | for technique in attck.enterprise.techniques: 95 | print(technique.id) 96 | print(technique.name) 97 | print(technique.description) 98 | # etc. 99 | 100 | for actor in technique.enterprise.actors: 101 | print(actor.id) 102 | print(actor.name) 103 | print(actor.description) 104 | # etc. 105 | 106 | for mitigation in attck.enterprise.mitigations: 107 | print(mitigation.id) 108 | print(mitigation.name) 109 | print(mitigation.description) 110 | # etc. 111 | 112 | for technique in mitigation.techniques: 113 | print(technique.name) 114 | print(technique.description) 115 | # etc. 116 | 117 | Arguments: 118 | nested_techniques (bool, optional): Whether not to iterate over nested subtechniques. Defaults to True. 119 | use_config (bool, optional): Specifies if a configuration file should be used or not. Defaults to False. 120 | save_config (bool, optional): Specifies if pyattck should save a configuration file based on the provided 121 | values. Defaults to False. 122 | config_file_path (str, optional): Path to a yaml configuration file which contains two key value pairs. 123 | Defaults to '~/pyattck/config.yml'. 124 | data_path (str, optional): Path to store the external data locally on your system. Defaults to '~/pyattck/data'. 125 | enterprise_attck_json (str, optional): A URL or local file path to the MITRE ATT&CK Json file. 126 | Defaults to https://swimlane-pyattck.s3.us-west-2.amazonaws.com/merged_enterprise_attck_v1.json. 127 | pre_attck_json (str, optional): A URL or local file path to the MITRE Pre-ATT&CK Json file. 128 | Defaults to https://swimlane-pyattck.s3.us-west-2.amazonaws.com/merged_pre_attck_v1.json. 129 | mobile_attck_json (str, optional): A URL or local file path to the MITRE Mobile ATT&CK Json file. 130 | Defaults to https://swimlane-pyattck.s3.us-west-2.amazonaws.com/merged_mobile_attck_v1.json. 131 | ics_attck_json (str, optional): A URL or local file path to the MITRE ICS ATT&CK JSON file. 132 | Defaults to https://swimlane-pyattck.s3.us-west-2.amazonaws.com/merged_ics_attck_v1.json. 133 | nist_controls_json (str, optional): A URL or local file path to the NIST Controls Json file. 134 | Defaults to https://swimlane-pyattck.s3.us-west-2.amazonaws.com/merged_nist_controls_v1.json. 135 | generated_nist_json (str, optional): A URL or local file path to the Generated NIST Controls Mapping Json file. 136 | Defaults to https://swimlane-pyattck.s3.us-west-2.amazonaws.com/attck_to_nist_controls.json. 137 | interactive (bool, optional): If True, runs the interactive menu within pyattck. Default is False. 138 | kwargs (dict, optional): Provided kwargs will be passed to any HTTP requests using the Requests library. 139 | Defaults to None. 140 | 141 | Returns: 142 | [Attck]: Returns a Attck object that contains all data from MITRE ATT&CK Frameworks 143 | """ 144 | 145 | def __init__( 146 | self, 147 | nested_techniques=True, 148 | use_config=False, 149 | save_config=False, 150 | config_file_path="~/pyattck/config.yml", 151 | data_path="~/pyattck/data", 152 | enterprise_attck_json="https://swimlane-pyattck.s3.us-west-2.amazonaws.com/merged_enterprise_attck_v1.json", 153 | pre_attck_json="https://swimlane-pyattck.s3.us-west-2.amazonaws.com/merged_pre_attck_v1.json", 154 | mobile_attck_json="https://swimlane-pyattck.s3.us-west-2.amazonaws.com/merged_mobile_attck_v1.json", 155 | ics_attck_json="https://swimlane-pyattck.s3.us-west-2.amazonaws.com/merged_ics_attck_v1.json", 156 | nist_controls_json="https://swimlane-pyattck.s3.us-west-2.amazonaws.com/merged_nist_controls_v1.json", 157 | generated_nist_json="https://swimlane-pyattck.s3.us-west-2.amazonaws.com/attck_to_nist_controls.json", 158 | interactive=False, 159 | **kwargs 160 | ): 161 | """ 162 | The main entry point for pyattck. 163 | 164 | When instantiating an Attck object you can specify if you want the 165 | new subtechniques to be nested underneath their parent techniques 166 | or not. 167 | 168 | Setting nested_techniques to False will result in all techniques 169 | accessible under the techniques property. If using the default value 170 | of True, subtechniques will be accessible underneath 171 | technique.techniques. 172 | 173 | When instantiating an Attck object you can access either the 174 | Enterprise, PRE-ATT&CK, or Mobile MITRE Frameworks. Specify 175 | one of the following properties to access the frameworks specific 176 | data: 177 | 178 | * enterprise 179 | * preattack 180 | * mobile 181 | * ics 182 | 183 | You can specify an alternate location of a local copy of the 184 | following objects: 185 | 186 | 1. config_file_path = Path to a yaml configuration file 187 | which contains two key value pairs 188 | Example content: 189 | 190 | config_file_path: /Users/user.name/pyattck/config.yml 191 | data_path: /Users/user.name/pyattck/data 192 | enterprise_attck_json: https://swimlane-pyattck.s3.us-west-2.amazonaws.com/merged_enterprise_attck_v1.json 193 | generated_nist_json: https://swimlane-pyattck.s3.us-west-2.amazonaws.com/attck_to_nist_controls.json 194 | mobile_attck_json: https://swimlane-pyattck.s3.us-west-2.amazonaws.com/merged_mobile_attck_v1.json 195 | ics_attck_json: https://swimlane-pyattck.s3.us-west-2.amazonaws.com/merged_ics_attck_v1.json 196 | nist_controls_json: https://swimlane-pyattck.s3.us-west-2.amazonaws.com/merged_nist_controls_v1.json 197 | pre_attck_json: https://swimlane-pyattck.s3.us-west-2.amazonaws.com/merged_pre_attck_v1.json 198 | 199 | 2. data_path = The path to hold the external data locally on your system. 200 | The default is your user home path. 201 | 202 | Args: 203 | nested_techniques (bool, optional): Whether not to iterate over nested subtechniques. Defaults to True. 204 | use_config (bool, optional): Specifies if a configuration file should be used or not. Defaults to False. 205 | save_config (bool, optional): Specifies if pyattck should save a configuration file based on the 206 | provided values. Defaults to False. 207 | config_file_path (str, optional): Path to a yaml configuration file which contains two key value pairs. 208 | Defaults to '~/pyattck/config.yml'. 209 | data_path (str, optional): Path to store the external data locally on your system. 210 | Defaults to '~/pyattck/data'. 211 | enterprise_attck_json (str, optional): A URL or local file path to the MITRE ATT&CK Json file. 212 | Defaults to https://swimlane-pyattck.s3.us-west-2.amazonaws.com/merged_enterprise_attck_v1.json. 213 | pre_attck_json (str, optional): A URL or local file path to the MITRE Pre-ATT&CK Json file. 214 | Defaults to https://swimlane-pyattck.s3.us-west-2.amazonaws.com/merged_pre_attck_v1.json. 215 | mobile_attck_json (str, optional): A URL or local file path to the MITRE Mobile ATT&CK Json file. 216 | Defaults to https://swimlane-pyattck.s3.us-west-2.amazonaws.com/merged_mobile_attck_v1.json. 217 | ics_attck_json (str, optional): A URL or local file path to the MITRE ICS ATT&CK JSON file. 218 | Defaults to https://swimlane-pyattck.s3.us-west-2.amazonaws.com/merged_ics_attck_v1.json. 219 | nist_controls_json (str, optional): A URL or local file path to the NIST Controls Json file. 220 | Defaults to https://swimlane-pyattck.s3.us-west-2.amazonaws.com/merged_nist_controls_v1.json 221 | generated_nist_json (str, optional): A URL or local file path to the Generated NIST Controls Mapping Json 222 | file. Defaults to https://swimlane-pyattck.s3.us-west-2.amazonaws.com/attck_to_nist_controls.json. 223 | interactive (bool, optional): If True, runs the interactive menu within pyattck. Default is False. 224 | kwargs (dict, optional): Provided kwargs will be passed to any HTTP requests using the Requests library. 225 | Defaults to None. 226 | """ 227 | Base.config = Options( 228 | nested_techniques=nested_techniques, 229 | use_config=use_config, 230 | save_config=save_config, 231 | config_file_path=config_file_path, 232 | kwargs=kwargs, 233 | config=Configuration( 234 | data_path=data_path, 235 | enterprise_attck_json=enterprise_attck_json, 236 | pre_attck_json=pre_attck_json, 237 | mobile_attck_json=mobile_attck_json, 238 | ics_attck_json=ics_attck_json, 239 | nist_controls_json=nist_controls_json, 240 | generated_nist_json=generated_nist_json, 241 | ), 242 | ) 243 | if interactive: 244 | from .utils.interactive import Interactive 245 | 246 | Interactive(self).generate() 247 | 248 | @property 249 | def enterprise(self): 250 | """Retrieve objects from the Enterprise MITRE ATT&CK Framework. 251 | 252 | Returns: 253 | Enterprise: Returns an Enterprise object 254 | """ 255 | from .enterprise import EnterpriseAttck 256 | 257 | self.__logger.debug("Calling MITRE Enterprise ATT&CK Framework") 258 | return EnterpriseAttck() 259 | 260 | @property 261 | def preattack(self): 262 | """Retrieve objects from the MITRE PRE-ATT&CK Framework. 263 | 264 | Returns: 265 | PreAttack: Returns an PreAttack object 266 | """ 267 | from .preattck import PreAttck 268 | 269 | self.__logger.debug("Calling MITRE Pre-ATT&CK Framework") 270 | warnings.warn( 271 | "MITRE has deprecated the Pre-ATT&CK Framework. " 272 | "Please use the Enterprise Framework instead and the PreAttack framework will no longer be supported." 273 | ) 274 | return PreAttck() 275 | 276 | @property 277 | def mobile(self): 278 | """Retrieve objects from the MITRE Mobile ATT&CK Framework. 279 | 280 | Returns: 281 | PreAttack: Returns an MobileAttack object 282 | """ 283 | from .mobile import MobileAttck 284 | 285 | self.__logger.debug("Calling MITRE Mobile ATT&CK Framework") 286 | return MobileAttck() 287 | 288 | @property 289 | def ics(self): 290 | """Retrieve objects from the MITRE ICS ATT&CK Framework. 291 | 292 | Returns: 293 | PreAttack: Returns an ICSAttck object 294 | """ 295 | from .ics import ICSAttck 296 | 297 | self.__logger.debug("Calling MITRE ICS ATT&CK Framework") 298 | return ICSAttck() 299 | 300 | def update(self) -> bool: 301 | """Updates the local cached JSON files.""" 302 | return True if Base.config._save_json_data(force=True) else False 303 | -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | ![pyattck](https://github.com/swimlane/pyattck/workflows/Testing%20pyattck/badge.svg) 2 | ![](images/ubuntu_support.svg) 3 | ![](images/macos_support.svg) 4 | ![](images/windows_support.svg) 5 | ![](images/code_coverage.svg) 6 | 7 | # Welcome to pyattck's Documentation 8 | 9 | ``` 10 | .______ ____ ____ ___ .___________.___________. ______ __ ___ 11 | | _ \ \ \ / / / \ | | | / || |/ / 12 | | |_) | \ \/ / / ^ \ `---| |----`---| |----`| ,----'| ' / 13 | | ___/ \_ _/ / /_\ \ | | | | | | | < 14 | | | | | / _____ \ | | | | | `----.| . \ 15 | | _| |__| /__/ \__\ |__| |__| \______||__|\__\ 16 | 17 | ``` 18 | A Python package to interact with MITRE ATT&CK Frameworks 19 | 20 | > Current Version is 6.1.0 21 | 22 | **pyattck** is a light-weight framework for MITRE ATT&CK Frameworks. This package extracts details from the MITRE Enterprise, PRE-ATT&CK, Mobile, and ICS Frameworks. 23 | 24 | ## Why? 25 | 26 | `pyattck` assist organizations and individuals with accessing MITRE ATT&CK Framework(s) in a programmatic way. Meaning, you can access all defined actors, malwares, mitigations, tactics, techniques, and tools defined by the Enterprise, Mobile, Pre-Attck, and ICS frameworks via a command-line utility or embedding into your own code base. 27 | 28 | There are many reasons why you would want to access this data in an automated (scripted/coded) way but a few examples are: 29 | 30 | * Generate reports with additional details about a technique (or any object defined in the framework) 31 | * A build pipeline of detection rules with additional MITRE ATT&CK details for categorization 32 | * Quickly searching for specific details about a technique without navigating a web page 33 | 34 | There are other benefits that `pyattck` provide as well which includes the ability to provide additional contextual data. You can find more information about this data [here](https://github.com/swimlane/pyattck-data) but the basics are that `pyattck` utilizes multiple open-source repositories to gather additional contextual data like commands used to execute a technique, country and other details about a malicious actor, other variants of malware similar to a defined tool/malware, etc. 35 | 36 | This additional context is what makes `pyattck` truly powerful and enables people to build more robust testing and validation of their detection rules, validates testing assumptions, etc. Truly there are countless ways that `pyattck` could be utilized to help blue, red, and purple teams defend organizations (and themselves). 37 | 38 | ## Features 39 | 40 | The **pyattck** package retrieves all Tactics, Techniques, Actors, Malware, Tools, and Mitigations from the MITRE ATT&CK Frameworks as well as any defined relationships within the MITRE ATT&CK dataset (including sub-techniques). 41 | 42 | In addition, Techniques, Actors, and Tools (if applicable) now have collected data from third-party resources that are accessible via properties on a technique. For more detailed information about these features, see [External Datasets](https://github.com/swimlane/pyattck-data). 43 | 44 | The **pyattck** package allows you to: 45 | 46 | * Specify a URL or local file path for the MITRE ATT&CK Enterprise Framework json, generated dataset, and/or a config.yml file. 47 | * Access data from the MITRE PRE-ATT&CK Framework 48 | * Access data from the MITRE Mobile ATT&CK Framework 49 | * Access data from the MITRE ICS ATT&CK Framework 50 | * Access sub-techniques as nested objects or you can turn it off and access as normal technique 51 | * Access compliance controls (currently NIST 800-53 v5) related to a MITRE ATT&CK Technique 52 | * pyattck now utilizes structured data models. More information can be found at [pyattck-data-models](https://github.com/swimlane/pyattck-data) 53 | 54 | # Table of Contents 55 | 56 | 1. [Installation](#installation) 57 | 2. [Usage Example](#usage-example) 58 | 3. [Configuration](#configuration) 59 | 4. [Notes](#note) 60 | 61 | ## Installation 62 | 63 | You can install **pyattck** on OS X, Linux, or Windows. You can also install it directly from the source. To install, see the commands under the relevant operating system heading, below. 64 | 65 | ### macOS, Linux and Windows: 66 | 67 | ```bash 68 | pip install pyattck 69 | ``` 70 | 71 | ### Installing from source 72 | 73 | ```bash 74 | git clone https://github.com/swimlane/pyattck.git 75 | cd pyattck 76 | python setup.py install 77 | ``` 78 | 79 | ## Usage example 80 | 81 | To use **pyattck** you must instantiate an **Attck** object. Although you can interact directly with each class, the intended use is through a **Attck** object: 82 | 83 | ```python 84 | from pyattck import Attck 85 | 86 | attack = Attck() 87 | ``` 88 | 89 | By default, `sub-techniques` are accessible under each technique object. You can turn this behavior off by passing `nested_techniques=False` when creating your `Attck` object. 90 | 91 | As an example, the default behavior looks like the following example: 92 | 93 | ```python 94 | from pyattck import Attck 95 | 96 | attack = Attck() 97 | 98 | for technique in attack.enterprise.techniques: 99 | print(technique.id) 100 | print(technique.name) 101 | for subtechnique in technique.techniques: 102 | print(subtechnique.id) 103 | print(subtechnique.name) 104 | ``` 105 | 106 | You can access the following `main` properties on your **Attck** object: 107 | 108 | * enterprise 109 | * preattack 110 | * mobile 111 | * ics 112 | 113 | Once you specify the MITRE ATT&CK Framework, you can access additional properties. 114 | 115 | Here are the accessible objects under the [Enterprise](enterprise.md) property: 116 | 117 | * [actors](actor.md) 118 | * [controls](control.md) 119 | * [malwares](malware.md) 120 | * [mitigations](mitigation.md) 121 | * [tactics](tactic.md) 122 | * [techniques](technique.md) 123 | * [tools](tools.md) 124 | 125 | For more information on object types under the `enterprise` property, see [Enterprise](enterprise.md). 126 | 127 | Here are the accessible objects under the [PreAttck](preattck.md) property: 128 | 129 | * [actors](actor.md) 130 | * [tactics](tactic.md) 131 | * [techniques](technique.md) 132 | 133 | For more information on object types under the `preattck` property, see [PreAttck](preattck.md). 134 | 135 | Here are the accessible objects under the [Mobile](mobile.md) property: 136 | 137 | * [actors](actor.md) 138 | * [malwares](malware.md) 139 | * [mitigations](mitigation.md) 140 | * [tactics](tactic.md) 141 | * [techniques](technique.md) 142 | * [tools](tools.md) 143 | 144 | For more information on object types under the `mobile` property, see [Mobile](mobile.md). 145 | 146 | Here are the accessible objects under the [ICS](ics.md) property: 147 | 148 | * [controls](control.md) 149 | * [malwares](malware.md) 150 | * [mitigations](mitigation.md) 151 | * [tactics](tactic.md) 152 | * [techniques](technique.md) 153 | 154 | For more information on object types under the `ics` property, see [ICS](ics.md). 155 | 156 | ## Interactive Menu Usage 157 | 158 | To utilize the new interactive menu system within pyattck, you must set `interactive` to `True`. By doing so, it will launch the interactive console menu system. 159 | 160 | Using a script your can launch this by running: 161 | 162 | ```python 163 | from pyattck import Attck 164 | 165 | Attck(interactive=True) 166 | ``` 167 | 168 | Or you can also run interactive mode on the command line: 169 | 170 | ```bash 171 | pyattck --interactive 172 | ``` 173 | 174 | Checkout a gif example below: 175 | 176 | ![](images/pyattck_interactive_menu.gif) 177 | 178 | ## Configuration 179 | 180 | `pyattck` allows you to configure if you store external data and where it is stored. 181 | 182 | ```python 183 | from pyattck import Attck 184 | 185 | attck = Attck( 186 | nested_techniques=True, 187 | use_config=False, 188 | save_config=False, 189 | config_file_path='~/pyattck/config.yml', 190 | data_path='~/pyattck/data', 191 | enterprise_attck_json="https://swimlane-pyattck.s3.us-west-2.amazonaws.com/merged_enterprise_attck_v1.json", 192 | pre_attck_json="https://swimlane-pyattck.s3.us-west-2.amazonaws.com/merged_pre_attck_v1.json", 193 | mobile_attck_json="https://swimlane-pyattck.s3.us-west-2.amazonaws.com/merged_mobile_attck_v1.json, 194 | ics_attck_json="https://swimlane-pyattck.s3.us-west-2.amazonaws.com/merged_ics_attck_v1.json", 195 | nist_controls_json="https://swimlane-pyattck.s3.us-west-2.amazonaws.com/merged_nist_controls_v1.json", 196 | generated_nist_json="https://swimlane-pyattck.s3.us-west-2.amazonaws.com/attck_to_nist_controls.json", 197 | **kwargs 198 | ) 199 | ``` 200 | 201 | By default, `pyattck` will (now) pull the latest external data from their respective locations using HTTP GET requests. `pyattck` currently pulls from the following locations: 202 | 203 | * enterprise_attck_json="https://swimlane-pyattck.s3.us-west-2.amazonaws.com/merged_enterprise_attck_v1.json" 204 | * pre_attck_json="https://swimlane-pyattck.s3.us-west-2.amazonaws.com/merged_pre_attck_v1.json" 205 | * mobile_attck_json="https://swimlane-pyattck.s3.us-west-2.amazonaws.com/merged_mobile_attck_v1.json" 206 | * ics_attck_json="https://swimlane-pyattck.s3.us-west-2.amazonaws.com/merged_ics_attck_v1.json" 207 | * nist_controls_json="https://swimlane-pyattck.s3.us-west-2.amazonaws.com/merged_nist_controls_v1.json" 208 | * generated_nist_json="https://swimlane-pyattck.s3.us-west-2.amazonaws.com/attck_to_nist_controls.json" 209 | 210 | You have several options when instantiating the `Attck` object. As of `4.0.0` you can now specify any of the following options: 211 | 212 | * use_config - When you specify this argument as `True` pyattck will attempt to retrieve the configuration specified in the `config_file_path` location. If this file is corrupted or cannot be found, we will default to retrieving data from the specified `*_attck_json` locations. 213 | * save_config - When you specify this argument as `True` pyattck will save the configuration file to the specified location set by `config_file_path`. Additionally, we will save all downloaded files to the `data_path` location specified. If you have specified a local path location instead of a download URL for any of the `*_attck_json` parameters we will save this location in our configuration and reference this location going forward. 214 | * config_file_path - The path to store a configuration file. Default is `~/pyattck/config.yml` 215 | * data_path - The path to store any data files downloaded to the local system. Default is `~/pyattck/data` 216 | 217 | ### JSON Locations 218 | 219 | Additionally, you can specify the location for each individual `*_attck_json` files by passing in either a URI or a local file path. If you have passed in a local file path, we will simply read from this file. 220 | 221 | If you have used the default values or specified an alternative URI location to retrieve these JSON files from, you can additionally pass in `**kwargs` that will be passed along to the `Requests` python package when performing any HTTP requests. 222 | 223 | ## Note 224 | 225 | We understand that there are many different open-source projects being released, even on a daily basis, but we wanted to provide a straightforward Python package that allowed the user to identify known relationships between all verticals of the MITRE ATT&CK Framework. 226 | 227 | If you are unfamiliar with the MITRE ATT&CK Framework, there are a few key components to ensure you have a firm grasp around. The first is Tactics & Techniques. When looking at the [MITRE ATT&CK Framework](https://attack.mitre.org/), the Tactics are the columns and represent the different phases of an attack. 228 | 229 | > The MITRE ATT&CK Framework is NOT an all encompassing/defacto security coverage map - it is rather a FRAMEWORK and additional avenues should also be considered when assessing your security posture. 230 | 231 | Techniques are the rows of the framework and are categorized underneath specific Tactics (columns). They are data points within the framework that provides guidance when assessing your security gaps. Additionally, (most) Techniques contain mitigation guidance in addition to information about their relationship to tools, malware, and even actors/groups that have used this technique during recorded attacks. 232 | 233 | This means, if your organization is focused on TTPs (Tactics Techniques and Procedures) used by certain actors/groups then MITRE ATT&CK Framework is perfect for you. If you are not at this security maturing within your organization, no worries! The ATT&CK Framework still provides really good guidance in a simple and straightforward layout, but programmatically it is not straightforward--especially if you wanted to measure (or map) your security controls using the framework. 234 | 235 | ### Developing and Testing 236 | 237 | You can add features or bugs or run the code in a development environment. 238 | 239 | 1. To get a development and testing environment up and running, use this [Dockerfile](https://github.com/swimlane/pyattck/blob/master/Dockerfile). 240 | 241 | 2. To use the `Dockerfile` run, cd to this repository directory and run: 242 | 243 | ``` 244 | docker build --force-rm -t pyattck . 245 | ``` 246 | 247 | 3. Next, run the docker container: 248 | 249 | ``` 250 | docker run pyattck 251 | ``` 252 | 253 | Running this calls the test python file in [bin/test.py](https://github.com/swimlane/pyattck/blob/master/bin/test.py). 254 | 255 | 4. Modify the test python file for additional testing and development. 256 | 257 | ## Running the tests 258 | 259 | Tests within this project should cover all available properties and methods. As this project grows the tests will become more robust but for now we are testing that they exist and return outputs. 260 | 261 | ## Contributing 262 | 263 | Please read [CONTRIBUTING.md](https://github.com/swimlane/pyattck/blob/master/CONTRIBUTING.md) for details on our code of conduct, and the process for submitting pull requests to us. 264 | 265 | ## Versioning 266 | 267 | We use [SemVer](http://semver.org/) for versioning. 268 | 269 | ## Change Log 270 | 271 | For details on features for a specific version of `pyattck`, see the [CHANGELOG.md](https://github.com/swimlane/pyattck/blob/master/CHANGELOG.md). 272 | 273 | ## Authors 274 | 275 | * Josh Rickard - *Initial work* - [MSAdministrator](https://github.com/msadministrator) 276 | 277 | See also the list of [contributors](https://github.com/swimlane/pyattck/contributors). 278 | 279 | ## License 280 | 281 | This project is licensed under the [MIT License](https://github.com/swimlane/pyattck/blob/master/LICENSE.md). 282 | 283 | ## Acknowledgments 284 | 285 | First of all, I would like to thank everyone who contributes to open-source projects, especially the maintainers and creators of these projects. Without them, this capability would not be possible. 286 | 287 | This data set is generated from many different sources. As we continue to add more sources, we will continue to add them here. Again thank you to all of these projects. In no particular order, `pyattck` utilizes data from the following projects: 288 | 289 | * [Mitre ATT&CK APT3 Adversary Emulation Field Manual](https://attack.mitre.org/docs/APT3_Adversary_Emulation_Field_Manual.xlsx) 290 | * [Atomic Red Team (by Red Canary)](https://github.com/redcanaryco/atomic-red-team) 291 | * [Atomic Threat Coverage](https://github.com/atc-project/atomic-threat-coverage) 292 | * [attck_empire (by dstepanic)](https://github.com/dstepanic/attck_empire) 293 | * [sentinel-attack (by BlueTeamLabs)](https://github.com/BlueTeamLabs/sentinel-attack) 294 | * [Litmus_test (by Kirtar22)](https://github.com/Kirtar22/Litmus_Test) 295 | * [nsm-attack (by oxtf)](https://github.com/0xtf/nsm-attack) 296 | * [osquery-attck (by teoseller)](https://github.com/teoseller/osquery-attck) 297 | * [Mitre Stockpile](https://github.com/mitre/stockpile) 298 | * [SysmonHunter (by baronpan)](https://github.com/baronpan/SysmonHunter) 299 | * [ThreatHunting-Book (by 12306Bro)](https://github.com/12306Bro/Threathunting-book) 300 | * [threat_hunting_tables (by dwestgard)](https://github.com/dwestgard/threat_hunting_tables) 301 | * [APT Groups & Operations](https://docs.google.com/spreadsheets/d/1H9_xaxQHpWaa4O_Son4Gx0YOIzlcBWMsdvePFX68EKU/edit#gid=1864660085) 302 | * [C2Matrix (by @jorgeorchilles, @brysonbort, @adam_mashinchi)](https://www.thec2matrix.com/) 303 | * [Elemental](https://github.com/Elemental-attack/Elemental) 304 | * [MalwareArchaeology - ATTACK](https://github.com/MalwareArchaeology/ATTACK) 305 | * [Attack-Technique-Dataset](https://github.com/NewBee119/Attack-Technique-Dataset) 306 | 307 | 308 | ```eval_rst 309 | .. toctree:: 310 | :titlesonly: 311 | 312 | configuration 313 | attck 314 | Dataset 315 | Data Models 316 | enterprise 317 | preattck 318 | mobile 319 | ics 320 | ``` 321 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![pyattck](https://github.com/swimlane/pyattck/workflows/Testing%20pyattck/badge.svg) 2 | ![](./images/ubuntu_support.svg) 3 | ![](./images/macos_support.svg) 4 | ![](./images/windows_support.svg) 5 | ![](./images/code_coverage.svg) 6 | 7 | # Welcome to pyattck's Documentation 8 | 9 | ``` 10 | .______ ____ ____ ___ .___________.___________. ______ __ ___ 11 | | _ \ \ \ / / / \ | | | / || |/ / 12 | | |_) | \ \/ / / ^ \ `---| |----`---| |----`| ,----'| ' / 13 | | ___/ \_ _/ / /_\ \ | | | | | | | < 14 | | | | | / _____ \ | | | | | `----.| . \ 15 | | _| |__| /__/ \__\ |__| |__| \______||__|\__\ 16 | 17 | ``` 18 | A Python package to interact with MITRE ATT&CK Frameworks 19 | 20 | > Current Version is 7.1.1 21 | 22 | **pyattck** is a light-weight framework for MITRE ATT&CK Frameworks. This package extracts details from the MITRE Enterprise, PRE-ATT&CK, Mobile, and ICS Frameworks. 23 | 24 | ## Why? 25 | 26 | `pyattck` assist organizations and individuals with accessing MITRE ATT&CK Framework(s) in a programmatic way. Meaning, you can access all defined actors, malwares, mitigations, tactics, techniques, and tools defined by the Enterprise, Mobile, Pre-Attck, and ICS frameworks via a command-line utility or embedding into your own code base. 27 | 28 | There are many reasons why you would want to access this data in an automated (scripted/coded) way but a few examples are: 29 | 30 | * Generate reports with additional details about a technique (or any object defined in the framework) 31 | * A build pipeline of detection rules with additional MITRE ATT&CK details for categorization 32 | * Quickly searching for specific details about a technique without navigating a web page 33 | 34 | There are other benefits that `pyattck` provide as well which includes the ability to provide additional contextual data. You can find more information about this data [here](https://github.com/swimlane/pyattck-data) but the basics are that `pyattck` utilizes multiple open-source repositories to gather additional contextual data like commands used to execute a technique, country and other details about a malicious actor, other variants of malware similar to a defined tool/malware, etc. 35 | 36 | This additional context is what makes `pyattck` truly powerful and enables people to build more robust testing and validation of their detection rules, validates testing assumptions, etc. Truly there are countless ways that `pyattck` could be utilized to help blue, red, and purple teams defend organizations (and themselves). 37 | 38 | ## Features 39 | 40 | The **pyattck** package retrieves all Tactics, Techniques, Actors, Malware, Tools, and Mitigations from the MITRE ATT&CK Frameworks as well as any defined relationships within the MITRE ATT&CK dataset (including sub-techniques). 41 | 42 | In addition, Techniques, Actors, and Tools (if applicable) now have collected data from third-party resources that are accessible via different properties. For more detailed information about these features, see [External Datasets](https://github.com/swimlane/pyattck-data). 43 | 44 | The **pyattck** package allows you to: 45 | 46 | * Specify a URL or local file path for the MITRE ATT&CK Enterprise Framework json, generated dataset, and/or a config.yml file. 47 | * Access data from the MITRE PRE-ATT&CK Framework 48 | * Access data from the MITRE Mobile ATT&CK Framework 49 | * Access data from the MITRE ICS ATT&CK Framework 50 | * Access sub-techniques as nested objects or you can turn it off and access as normal technique 51 | * Access compliance controls (currently NIST 800-53 v5) related to a MITRE ATT&CK Technique 52 | * pyattck now utilizes structured data models. More information can be found at [pyattck-data](https://github.com/swimlane/pyattck-data) 53 | * Run an interactive console menu system to access pyattck data 54 | 55 | # Table of Contents 56 | 57 | 1. [Installation](#installation) 58 | 2. [Usage Example](#usage-example) 59 | 3. [Configuration](#configuration) 60 | 4. [Notes](#note) 61 | 62 | ## Installation 63 | 64 | You can install **pyattck** on OS X, Linux, or Windows. You can also install it directly from the source. To install, see the commands under the relevant operating system heading, below. 65 | 66 | ### macOS, Linux and Windows: 67 | 68 | ```bash 69 | pip install pyattck 70 | ``` 71 | 72 | ### Installing from source 73 | 74 | ```bash 75 | git clone https://github.com/swimlane/pyattck.git 76 | cd pyattck 77 | python setup.py install 78 | ``` 79 | 80 | ## Usage example 81 | 82 | To use **pyattck** you must instantiate an **Attck** object. Although you can interact directly with each class, the intended use is through a **Attck** object: 83 | 84 | ```python 85 | from pyattck import Attck 86 | 87 | attack = Attck() 88 | ``` 89 | 90 | By default, `sub-techniques` are accessible under each technique object. You can turn this behavior off by passing `nested_techniques=False` when creating your `Attck` object. 91 | 92 | As an example, the default behavior looks like the following example: 93 | 94 | ```python 95 | from pyattck import Attck 96 | 97 | attack = Attck() 98 | 99 | for technique in attack.enterprise.techniques: 100 | print(technique.id) 101 | print(technique.name) 102 | for subtechnique in technique.techniques: 103 | print(subtechnique.id) 104 | print(subtechnique.name) 105 | ``` 106 | 107 | You can access the following `main` properties on your **Attck** object: 108 | 109 | * enterprise 110 | * preattack 111 | * mobile 112 | * ics 113 | 114 | Once you specify the MITRE ATT&CK Framework, you can access additional properties. 115 | 116 | Here are the accessible objects under the [Enterprise](docs/enterprise.md) property: 117 | 118 | * [actors](docs/actor.md) 119 | * [controls](docs/control.md) 120 | * [malwares](docs/malware.md) 121 | * [mitigations](docs/mitigation.md) 122 | * [tactics](docs/tactic.md) 123 | * [techniques](docs/technique.md) 124 | * [tools](docs/tools.md) 125 | 126 | For more information on object types under the `enterprise` property, see [Enterprise](docs/enterprise.md). 127 | 128 | Here are the accessible objects under the [PreAttck](docs/preattck.md) property: 129 | 130 | * [actors](docs/actor.md) 131 | * [tactics](docs/tactic.md) 132 | * [techniques](docs/technique.md) 133 | 134 | For more information on object types under the `preattck` property, see [PreAttck](docs/preattck.md). 135 | 136 | Here are the accessible objects under the [Mobile](docs/mobile.md) property: 137 | 138 | * [actors](docs/actor.md) 139 | * [malwares](docs/malware.md) 140 | * [mitigations](docs/mitigation.md) 141 | * [tactics](docs/tactic.md) 142 | * [techniques](docs/technique.md) 143 | * [tools](docs/tools.md) 144 | 145 | For more information on object types under the `mobile` property, see [Mobile](docs/mobile.md). 146 | 147 | Here are the accessible objects under the [ICS](docs/ics.md) property: 148 | 149 | * [controls](docs/control.md) 150 | * [malwares](docs/malware.md) 151 | * [mitigations](docs/mitigation.md) 152 | * [tactics](docs/tactic.md) 153 | * [techniques](docs/technique.md) 154 | 155 | For more information on object types under the `ics` property, see [ICS](docs/ics.md). 156 | 157 | ## Interactive Menu Usage 158 | 159 | To utilize the new interactive menu system within pyattck, you must set `interactive` to `True`. By doing so, it will launch the interactive console menu system. 160 | 161 | Using a script your can launch this by running: 162 | 163 | ```python 164 | from pyattck import Attck 165 | 166 | Attck(interactive=True) 167 | ``` 168 | 169 | Or you can also run interactive mode on the command line: 170 | 171 | ```bash 172 | pyattck --interactive 173 | ``` 174 | 175 | Checkout a gif example below: 176 | 177 | ![](images/pyattck_interactive_menu.gif) 178 | 179 | ## Configuration 180 | 181 | `pyattck` allows you to configure if you store external data and where it is stored. 182 | 183 | ```python 184 | from pyattck import Attck 185 | 186 | attck = Attck( 187 | nested_techniques=True, 188 | use_config=False, 189 | save_config=False, 190 | config_file_path='~/pyattck/config.yml', 191 | data_path='~/pyattck/data', 192 | enterprise_attck_json="https://swimlane-pyattck.s3.us-west-2.amazonaws.com/merged_enterprise_attck_v1.json", 193 | pre_attck_json="https://swimlane-pyattck.s3.us-west-2.amazonaws.com/merged_pre_attck_v1.json", 194 | mobile_attck_json="https://swimlane-pyattck.s3.us-west-2.amazonaws.com/merged_mobile_attck_v1.json, 195 | ics_attck_json="https://swimlane-pyattck.s3.us-west-2.amazonaws.com/merged_ics_attck_v1.json", 196 | nist_controls_json="https://swimlane-pyattck.s3.us-west-2.amazonaws.com/merged_nist_controls_v1.json", 197 | generated_nist_json="https://swimlane-pyattck.s3.us-west-2.amazonaws.com/attck_to_nist_controls.json", 198 | **kwargs 199 | ) 200 | ``` 201 | 202 | By default, `pyattck` will (now) pull the latest external data from their respective locations using HTTP GET requests. `pyattck` currently pulls from the following locations: 203 | 204 | * enterprise_attck_json="https://swimlane-pyattck.s3.us-west-2.amazonaws.com/merged_enterprise_attck_v1.json" 205 | * pre_attck_json="https://swimlane-pyattck.s3.us-west-2.amazonaws.com/merged_pre_attck_v1.json" 206 | * mobile_attck_json="https://swimlane-pyattck.s3.us-west-2.amazonaws.com/merged_mobile_attck_v1.json" 207 | * ics_attck_json="https://swimlane-pyattck.s3.us-west-2.amazonaws.com/merged_ics_attck_v1.json" 208 | * nist_controls_json="https://swimlane-pyattck.s3.us-west-2.amazonaws.com/merged_nist_controls_v1.json" 209 | * generated_nist_json="https://swimlane-pyattck.s3.us-west-2.amazonaws.com/attck_to_nist_controls.json" 210 | 211 | You have several options when instantiating the `Attck` object. As of `4.0.0` you can now specify any of the following options: 212 | 213 | * use_config - When you specify this argument as `True` pyattck will attempt to retrieve the configuration specified in the `config_file_path` location. If this file is corrupted or cannot be found, we will default to retrieving data from the specified `*_attck_json` locations. 214 | * save_config - When you specify this argument as `True` pyattck will save the configuration file to the specified location set by `config_file_path`. Additionally, we will save all downloaded files to the `data_path` location specified. If you have specified a local path location instead of a download URL for any of the `*_attck_json` parameters we will save this location in our configuration and reference this location going forward. 215 | * config_file_path - The path to store a configuration file. Default is `~/pyattck/config.yml` 216 | * data_path - The path to store any data files downloaded to the local system. Default is `~/pyattck/data` 217 | 218 | ### JSON Locations 219 | 220 | Additionally, you can specify the location for each individual `*_attck_json` files by passing in either a URI or a local file path. If you have passed in a local file path, we will simply read from this file. 221 | 222 | If you have used the default values or specified an alternative URI location to retrieve these JSON files from, you can additionally pass in `**kwargs` that will be passed along to the `Requests` python package when performing any HTTP requests. 223 | 224 | ## Note 225 | 226 | We understand that there are many different open-source projects being released, even on a daily basis, but we wanted to provide a straightforward Python package that allowed the user to identify known relationships between all verticals of the MITRE ATT&CK Framework. 227 | 228 | If you are unfamiliar with the MITRE ATT&CK Framework, there are a few key components to ensure you have a firm grasp around. The first is Tactics & Techniques. When looking at the [MITRE ATT&CK Framework](https://attack.mitre.org/), the Tactics are the columns and represent the different phases of an attack. 229 | 230 | > The MITRE ATT&CK Framework is NOT an all encompassing/defacto security coverage map - it is rather a FRAMEWORK and additional avenues should also be considered when assessing your security posture. 231 | 232 | Techniques are the rows of the framework and are categorized underneath specific Tactics (columns). They are data points within the framework that provides guidance when assessing your security gaps. Additionally, (most) Techniques contain mitigation guidance in addition to information about their relationship to tools, malware, and even actors/groups that have used this technique during recorded attacks. 233 | 234 | This means, if your organization is focused on TTPs (Tactics Techniques and Procedures) used by certain actors/groups then MITRE ATT&CK Framework is perfect for you. If you are not at this security maturing within your organization, no worries! The ATT&CK Framework still provides really good guidance in a simple and straightforward layout, but programmatically it is not straightforward--especially if you wanted to measure (or map) your security controls using the framework. 235 | 236 | ### Developing and Testing 237 | 238 | You can add features or bugs or run the code in a development environment. 239 | 240 | 1. To get a development and testing environment up and running, use this [Dockerfile](https://github.com/swimlane/pyattck/blob/master/Dockerfile). 241 | 242 | 2. To use the `Dockerfile` run, cd to this repository directory and run: 243 | 244 | ``` 245 | docker build --force-rm -t pyattck . 246 | ``` 247 | 248 | 3. Next, run the docker container: 249 | 250 | ``` 251 | docker run pyattck 252 | ``` 253 | 254 | Running this calls the test python file in [bin/test.py](https://github.com/swimlane/pyattck/blob/master/bin/test.py). 255 | 256 | 4. Modify the test python file for additional testing and development. 257 | 258 | ## Running the tests 259 | 260 | Tests within this project should cover all available properties and methods. As this project grows the tests will become more robust but for now we are testing that they exist and return outputs. 261 | 262 | ## Contributing 263 | 264 | Please read [CONTRIBUTING.md](https://github.com/swimlane/pyattck/blob/master/CONTRIBUTING.md) for details on our code of conduct, and the process for submitting pull requests to us. 265 | 266 | ## Versioning 267 | 268 | We use [SemVer](http://semver.org/) for versioning. 269 | 270 | ## Change Log 271 | 272 | For details on features for a specific version of `pyattck`, see the [CHANGELOG.md](https://github.com/swimlane/pyattck/blob/master/CHANGELOG.md). 273 | 274 | ## Authors 275 | 276 | * Josh Rickard - *Initial work* - [MSAdministrator](https://github.com/msadministrator) 277 | 278 | See also the list of [contributors](https://github.com/swimlane/pyattck/contributors). 279 | 280 | ## License 281 | 282 | This project is licensed under the [MIT License](https://github.com/swimlane/pyattck/blob/master/LICENSE.md). 283 | 284 | ## Acknowledgments 285 | 286 | First of all, I would like to thank everyone who contributes to open-source projects, especially the maintainers and creators of these projects. Without them, this capability would not be possible. 287 | 288 | This data set is generated from many different sources. As we continue to add more sources, we will continue to add them here. Again thank you to all of these projects. In no particular order, `pyattck` utilizes data from the following projects: 289 | 290 | * [Mitre ATT&CK APT3 Adversary Emulation Field Manual](https://attack.mitre.org/docs/APT3_Adversary_Emulation_Field_Manual.xlsx) 291 | * [Atomic Red Team (by Red Canary)](https://github.com/redcanaryco/atomic-red-team) 292 | * [Atomic Threat Coverage](https://github.com/atc-project/atomic-threat-coverage) 293 | * [attck_empire (by dstepanic)](https://github.com/dstepanic/attck_empire) 294 | * [sentinel-attack (by BlueTeamLabs)](https://github.com/BlueTeamLabs/sentinel-attack) 295 | * [Litmus_test (by Kirtar22)](https://github.com/Kirtar22/Litmus_Test) 296 | * [nsm-attack (by oxtf)](https://github.com/0xtf/nsm-attack) 297 | * [osquery-attck (by teoseller)](https://github.com/teoseller/osquery-attck) 298 | * [Mitre Stockpile](https://github.com/mitre/stockpile) 299 | * [SysmonHunter (by baronpan)](https://github.com/baronpan/SysmonHunter) 300 | * [ThreatHunting-Book (by 12306Bro)](https://github.com/12306Bro/Threathunting-book) 301 | * [threat_hunting_tables (by dwestgard)](https://github.com/dwestgard/threat_hunting_tables) 302 | * [APT Groups & Operations](https://docs.google.com/spreadsheets/d/1H9_xaxQHpWaa4O_Son4Gx0YOIzlcBWMsdvePFX68EKU/edit#gid=1864660085) 303 | * [C2Matrix (by @jorgeorchilles, @brysonbort, @adam_mashinchi)](https://www.thec2matrix.com/) 304 | * [Elemental](https://github.com/Elemental-attack/Elemental) 305 | * [MalwareArchaeology - ATTACK](https://github.com/MalwareArchaeology/ATTACK) 306 | * [Attack-Technique-Dataset](https://github.com/NewBee119/Attack-Technique-Dataset) 307 | 308 | 309 | ```eval_rst 310 | .. toctree:: 311 | :titlesonly: 312 | 313 | configuration 314 | pyattck/attck 315 | Dataset 316 | Data Models 317 | enterprise/enterprise 318 | preattck/preattck 319 | mobile/mobileattck 320 | ics/icsattck 321 | ``` 322 | -------------------------------------------------------------------------------- /bin/test.py: -------------------------------------------------------------------------------- 1 | from pyattck import Attck 2 | 3 | attack = Attck(nested_techniques=False, save_config=True) 4 | 5 | # Examples of MITRE Enterprise ATT&CK using nested techniques 6 | 7 | for relationship in attack.enterprise.relationships: 8 | print(relationship.id) 9 | print(relationship.description) 10 | 11 | for actor in attack.enterprise.actors: 12 | print(actor.id) 13 | print(actor.name) 14 | 15 | # accessing malware used by an actor or group 16 | for malware in actor.malwares: 17 | print(malware.id) 18 | print(malware.name) 19 | 20 | # accessing tools used by an actor or group 21 | for tool in actor.tools: 22 | print(tool.id) 23 | print(tool.name) 24 | 25 | # accessing techniques used by an actor or group 26 | for technique in actor.techniques: 27 | print(technique.id) 28 | print(technique.name) 29 | print(technique.data_sources) 30 | # you can also access generated data sets on aa technique 31 | print(technique.command_list) 32 | print(technique.commands) 33 | print(technique.queries) 34 | print(technique.parsed_datasets) 35 | print(technique.possible_detections) 36 | # You can access techniques nested under techniques by default 37 | for subtechnique in technique.techniques: 38 | print(subtechnique.id) 39 | print(subtechnique.name) 40 | # etc. 41 | # To access all techniques under a single technique object set 42 | # Attck(nested_techniques=False) 43 | 44 | # to get a count of controls for a technique do the following 45 | print(len(technique.controls)) 46 | 47 | # below will print each controls properties & values 48 | for control in technique.controls: 49 | print(control.__dict__) 50 | 51 | # below will print the id, name and description of a control 52 | for control in technique.controls: 53 | print(control.id) 54 | print(control.name) 55 | print(control.description) 56 | 57 | # accessing data_sources 58 | for data_source in attack.enterprise.data_sources: 59 | print(data_source.name) 60 | print(data_source.id) 61 | for technique in data_source.techniques: 62 | print(technique.id) 63 | print(technique.name) 64 | for component in data_source.data_components: 65 | print(component.name) 66 | print(component.id) 67 | 68 | # accessing malware 69 | for malware in attack.enterprise.malwares: 70 | print(malware.id) 71 | print(malware.name) 72 | 73 | # accessing actor or groups using this malware 74 | for actor in malware.actors: 75 | print(actor.id) 76 | print(actor.name) 77 | 78 | # accessing techniques that this malware is used in 79 | for technique in malware.techniques: 80 | print(technique.id) 81 | print(technique.name) 82 | 83 | # to get a count of controls for a technique do the following 84 | print(len(technique.controls)) 85 | 86 | # below will print each controls properties & values 87 | for control in technique.controls: 88 | print(control.__dict__) 89 | 90 | # below will print the id, name and description of a control 91 | for control in technique.controls: 92 | print(control.id) 93 | print(control.name) 94 | print(control.description) 95 | 96 | # accessing mitigation 97 | for mitigation in attack.enterprise.mitigations: 98 | print(mitigation.id) 99 | print(mitigation.name) 100 | 101 | # accessing techniques related to mitigation recommendations 102 | for technique in mitigation.techniques: 103 | print(technique.id) 104 | print(technique.name) 105 | # you can also access generated data sets on aa technique 106 | print(technique.command_list) 107 | print(technique.commands) 108 | print(technique.queries) 109 | print(technique.parsed_datasets) 110 | print(technique.possible_detections) 111 | 112 | # to get a count of controls for a technique do the following 113 | print(len(technique.controls)) 114 | 115 | # below will print each controls properties & values 116 | for control in technique.controls: 117 | print(control.__dict__) 118 | 119 | # below will print the id, name and description of a control 120 | for control in technique.controls: 121 | print(control.id) 122 | print(control.name) 123 | print(control.description) 124 | 125 | # accessing tactics 126 | for tactic in attack.enterprise.tactics: 127 | print(tactic.id) 128 | print(tactic.name) 129 | 130 | # accessing techniques related to this tactic 131 | for technique in tactic.techniques: 132 | print(technique.id) 133 | print(technique.name) 134 | # you can also access generated data sets on aa technique 135 | print(technique.command_list) 136 | print(technique.commands) 137 | print(technique.queries) 138 | print(technique.parsed_datasets) 139 | print(technique.possible_detections) 140 | 141 | # to get a count of controls for a technique do the following 142 | print(len(technique.controls)) 143 | 144 | # below will print each controls properties & values 145 | for control in technique.controls: 146 | print(control.__dict__) 147 | 148 | # below will print the id, name and description of a control 149 | for control in technique.controls: 150 | print(control.id) 151 | print(control.name) 152 | print(control.description) 153 | 154 | for technique in attack.enterprise.techniques: 155 | print(technique.id) 156 | print(technique.stix) 157 | print(technique.name) 158 | 159 | # to get a count of controls for a technique do the following 160 | print(len(technique.controls)) 161 | 162 | # below will print each controls properties & values 163 | for control in technique.controls: 164 | print(control.__dict__) 165 | 166 | # below will print the id, name and description of a control 167 | for control in technique.controls: 168 | print(control.id) 169 | print(control.name) 170 | print(control.description) 171 | 172 | # you can also access generated data sets on aa technique 173 | print(technique.command_list) 174 | print(technique.commands) 175 | print(technique.queries) 176 | print(technique.parsed_datasets) 177 | print(technique.possible_detections) 178 | 179 | # Access all subtechnique objects 180 | print(technique.techniques) 181 | 182 | # iterate through techniques 183 | for subtechnique in technique.techniques: 184 | print(subtechnique.name) 185 | print(subtechnique.id) 186 | 187 | # accessing data_sources that this technique has 188 | for data_source in technique.data_sources: 189 | print(data_source.id) 190 | print(data_source.name) 191 | 192 | # accessing tactics that this technique belongs to 193 | for tactic in technique.tactics: 194 | print(tactic.id) 195 | print(tactic.name) 196 | 197 | # accessing mitigation recommendations for this technique 198 | for mitigation in technique.mitigations: 199 | print(mitigation.id) 200 | print(mitigation.name) 201 | 202 | # accessing actors using this technique 203 | for actor in technique.actors: 204 | print(actor.id) 205 | print(actor.name) 206 | 207 | # accessing tools 208 | for tool in attack.enterprise.tools: 209 | print(tool.id) 210 | print(tool.name) 211 | 212 | # accessing techniques this tool is used in 213 | for technique in tool.techniques: 214 | print(technique.id) 215 | print(technique.name) 216 | # you can also access generated data sets on aa technique 217 | print(technique.command_list) 218 | print(technique.commands) 219 | print(technique.queries) 220 | print(technique.parsed_datasets) 221 | print(technique.possible_detections) 222 | 223 | # accessing actor or groups using this tool 224 | for actor in tool.actors: 225 | print(actor.id) 226 | print(actor.name) 227 | 228 | # Examples of MITRE PRE-ATT&CK 229 | 230 | for actor in attack.preattack.actors: 231 | print(actor.id) 232 | print(actor.name) 233 | 234 | # accessing techniques used by an actor or group 235 | for technique in actor.techniques: 236 | print(technique.id) 237 | print(technique.name) 238 | 239 | # accessing tactics 240 | for tactic in attack.preattack.tactics: 241 | print(tactic.id) 242 | print(tactic.name) 243 | 244 | # accessing techniques related to this tactic 245 | for technique in tactic.techniques: 246 | print(technique.id) 247 | print(technique.name) 248 | 249 | 250 | # accessing techniques 251 | for technique in attack.preattack.techniques: 252 | print(technique.id) 253 | print(technique.name) 254 | 255 | # accessing tactics that this technique belongs to 256 | for tactic in technique.tactics: 257 | print(tactic.id) 258 | print(tactic.name) 259 | 260 | # accessing actors using this technique 261 | for actor in technique.actors: 262 | print(actor.id) 263 | print(actor.name) 264 | 265 | # Examples of MITRE Mobile ATT&CK 266 | 267 | for actor in attack.mobile.actors: 268 | print(actor.id) 269 | print(actor.name) 270 | 271 | # accessing malware used by an actor or group 272 | for malware in actor.malwares: 273 | print(malware.id) 274 | print(malware.name) 275 | 276 | # accessing tools used by an actor or group 277 | for tool in actor.tools: 278 | print(tool.id) 279 | print(tool.name) 280 | 281 | # accessing techniques used by an actor or group 282 | for technique in actor.techniques: 283 | print(technique.id) 284 | print(technique.name) 285 | # you can also access generated data sets on aa technique 286 | print(technique.command_list) 287 | print(technique.commands) 288 | print(technique.queries) 289 | print(technique.parsed_datasets) 290 | print(technique.possible_detections) 291 | 292 | # accessing malware 293 | for malware in attack.mobile.malwares: 294 | print(malware.id) 295 | print(malware.name) 296 | 297 | # accessing actor or groups using this malware 298 | for actor in malware.actors: 299 | print(actor.id) 300 | print(actor.name) 301 | 302 | # accessing techniques that this malware is used in 303 | for technique in malware.techniques: 304 | print(technique.id) 305 | print(technique.name) 306 | 307 | # accessing mitigation 308 | for mitigation in attack.mobile.mitigations: 309 | print(mitigation.id) 310 | print(mitigation.name) 311 | 312 | # accessing techniques related to mitigation recommendations 313 | for technique in mitigation.techniques: 314 | print(technique.id) 315 | print(technique.name) 316 | # you can also access generated data sets on aa technique 317 | print(technique.command_list) 318 | print(technique.commands) 319 | print(technique.queries) 320 | print(technique.parsed_datasets) 321 | print(technique.possible_detections) 322 | 323 | # accessing tactics 324 | for tactic in attack.mobile.tactics: 325 | print(tactic.id) 326 | print(tactic.name) 327 | 328 | # accessing techniques related to this tactic 329 | for technique in tactic.techniques: 330 | print(technique.id) 331 | print(technique.name) 332 | # you can also access generated data sets on aa technique 333 | print(technique.command_list) 334 | print(technique.commands) 335 | print(technique.queries) 336 | print(technique.parsed_datasets) 337 | print(technique.possible_detections) 338 | 339 | # accessing techniques 340 | for technique in attack.mobile.techniques: 341 | print(technique.id) 342 | print(technique.name) 343 | # you can also access generated data sets on aa technique 344 | print(technique.command_list) 345 | print(technique.commands) 346 | print(technique.queries) 347 | print(technique.parsed_datasets) 348 | print(technique.possible_detections) 349 | 350 | # accessing tactics that this technique belongs to 351 | for tactic in technique.tactics: 352 | print(tactic.id) 353 | print(tactic.name) 354 | 355 | # accessing mitigation recommendations for this technique 356 | for mitigation in technique.mitigations: 357 | print(mitigation.id) 358 | print(mitigation.name) 359 | 360 | # accessing actors using this technique 361 | for actor in technique.actors: 362 | print(actor.id) 363 | print(actor.name) 364 | 365 | # accessing tools 366 | for tool in attack.mobile.tools: 367 | print(tool.id) 368 | print(tool.name) 369 | 370 | # accessing techniques this tool is used in 371 | for technique in tool.techniques: 372 | print(technique.id) 373 | print(technique.name) 374 | # you can also access generated data sets on aa technique 375 | print(technique.command_list) 376 | print(technique.commands) 377 | print(technique.queries) 378 | print(technique.parsed_datasets) 379 | print(technique.possible_detections) 380 | 381 | # accessing actor or groups using this tool 382 | for actor in tool.actors: 383 | print(actor.id) 384 | print(actor.name) 385 | 386 | 387 | # Accessing ICS MITRE ATT&CK Framework 388 | 389 | # accessing data_sources 390 | for data_source in attack.ics.data_sources: 391 | print(data_source.name) 392 | print(data_source.id) 393 | for technique in data_source.techniques: 394 | print(technique.id) 395 | print(technique.name) 396 | for component in data_source.data_components: 397 | print(component.name) 398 | print(component.id) 399 | 400 | # accessing malware 401 | for malware in attack.ics.malwares: 402 | print(malware.id) 403 | print(malware.name) 404 | 405 | # accessing techniques that this malware is used in 406 | for technique in malware.techniques: 407 | print(technique.id) 408 | print(technique.name) 409 | 410 | # to get a count of controls for a technique do the following 411 | print(len(technique.controls)) 412 | 413 | # below will print each controls properties & values 414 | for control in technique.controls: 415 | print(control.__dict__) 416 | 417 | # below will print the id, name and description of a control 418 | for control in technique.controls: 419 | print(control.id) 420 | print(control.name) 421 | print(control.description) 422 | 423 | # accessing mitigation 424 | for mitigation in attack.ics.mitigations: 425 | print(mitigation.id) 426 | print(mitigation.name) 427 | 428 | # accessing techniques related to mitigation recommendations 429 | for technique in mitigation.techniques: 430 | print(technique.id) 431 | print(technique.name) 432 | # you can also access generated data sets on aa technique 433 | print(technique.command_list) 434 | print(technique.commands) 435 | print(technique.queries) 436 | print(technique.parsed_datasets) 437 | print(technique.possible_detections) 438 | 439 | # to get a count of controls for a technique do the following 440 | print(len(technique.controls)) 441 | 442 | # below will print each controls properties & values 443 | for control in technique.controls: 444 | print(control.__dict__) 445 | 446 | # below will print the id, name and description of a control 447 | for control in technique.controls: 448 | print(control.id) 449 | print(control.name) 450 | print(control.description) 451 | 452 | # accessing tactics 453 | for tactic in attack.ics.tactics: 454 | print(tactic.id) 455 | print(tactic.name) 456 | 457 | # accessing techniques related to this tactic 458 | for technique in tactic.techniques: 459 | print(technique.id) 460 | print(technique.name) 461 | # you can also access generated data sets on aa technique 462 | print(technique.command_list) 463 | print(technique.commands) 464 | print(technique.queries) 465 | print(technique.parsed_datasets) 466 | print(technique.possible_detections) 467 | 468 | # to get a count of controls for a technique do the following 469 | print(len(technique.controls)) 470 | 471 | # below will print each controls properties & values 472 | for control in technique.controls: 473 | print(control.__dict__) 474 | 475 | # below will print the id, name and description of a control 476 | for control in technique.controls: 477 | print(control.id) 478 | print(control.name) 479 | print(control.description) 480 | 481 | for technique in attack.ics.techniques: 482 | print(technique.id) 483 | print(technique.stix) 484 | print(technique.name) 485 | 486 | # to get a count of controls for a technique do the following 487 | print(len(technique.controls)) 488 | 489 | # below will print each controls properties & values 490 | for control in technique.controls: 491 | print(control.__dict__) 492 | 493 | # below will print the id, name and description of a control 494 | for control in technique.controls: 495 | print(control.id) 496 | print(control.name) 497 | print(control.description) 498 | 499 | # you can also access generated data sets on aa technique 500 | print(technique.command_list) 501 | print(technique.commands) 502 | print(technique.queries) 503 | print(technique.parsed_datasets) 504 | print(technique.possible_detections) 505 | 506 | # accessing data_sources that this technique has 507 | for data_source in technique.data_sources: 508 | print(data_source) 509 | print(data_source.id) 510 | print(data_source.name) 511 | 512 | # accessing tactics that this technique belongs to 513 | for tactic in technique.tactics: 514 | print(tactic.id) 515 | print(tactic.name) 516 | 517 | # accessing mitigation recommendations for this technique 518 | for mitigation in technique.mitigations: 519 | print(mitigation.id) 520 | print(mitigation.name) 521 | --------------------------------------------------------------------------------