├── 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 |
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 |
--------------------------------------------------------------------------------
/images/ubuntu_support.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/images/windows_support.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/docs/images/macos_support.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/docs/images/ubuntu_support.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/docs/images/windows_support.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/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 | 
2 | 
3 | 
4 | 
5 | 
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 | 
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 | 
2 | 
3 | 
4 | 
5 | 
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 | 
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 |
--------------------------------------------------------------------------------