写在前面
因为自己的项目需要使用到ATT&CK知识库的诸多知识,因此将其使用文档随意翻译成中文方便自己查阅,在此分享给需要的朋友,如果有什么问题,欢迎大家留言讨论。
介绍
本文档描述了如何从这个仓库或 ATT&CK TAXII 服务器查询和操作 ATT&CK 数据,以及数据本身的格式。
本文档中演示的 ATT&CK 的编程使用了 stix2 python 库。有关如何以编程方式使用 STIX 的更多信息,请参阅 STIX2 Python API 文档。 另请参阅要求和引入部分.
本文档描述了 ATT&CK 如何实现和扩展 STIX 格式。要了解有关 STIX 的更多信息,请参阅STIX 2.0 网站.
同时建议阅读 ATT&CK Design and Philosophy,其中描述了 ATT&CK 的高级整体方法、意图和用法。
如果正在寻找 STIX 2.1 中表示的 ATT&CK 数据,请查看attack-stix-data GitHub库. 随附的 USAGE 文档 包含有关该仓库的改进数据模型的更多信息。
目录
ATT&CK数据模型
此仓库中的数据是 STIX 2.0格式的,并分为不同文件夹,每个文件夹对应 ATT&CK的不同域。这些域通常遵循相同的格式,但有一些不同之处。域的差异将在本文档的相关部分中注明。
ATT&CK 混合使用预定义和自定义 STIX 对象来实现 ATT&CK 概念。下表是 ATT&CK 概念到 STIX 2.0 对象的映射:
ATT&CK 概念 | STIX 对象类型 | 自定义类型? |
---|---|---|
Matrix(矩阵) | x-mitre-matrix | yes |
Tactic(战术) | x-mitre-tactic | yes |
Technique(技术) | attack-pattern | no |
Sub-technique(子技术) | attack-pattern 其中 x_mitre_is_subtechnique = true | no |
Procedure(程序) | relationship 其中 relationship_type = "uses" 并且 target_ref 继承自 attack-pattern | no |
Mitigation(缓解措施) | course-of-action | no |
Group(组织) | intrusion-set | no |
Software(软件) | malware 或 tool | no |
Data Source(数据源) | x-mitre-data-source | yes |
Campaign(行动) | campaign | no |
在 ATT&CK 目录中可以找到另外两种对象类型:
STIX 对象类型 | 关于 |
---|---|
identity | 使用 created_by_ref 的所有对象以声明 MITRE 公司创建了该对象 |
marking-definition | 使用 object_marking_refs 的所有对象以表达 MITRE 公司版权 |
STIX规范扩展
ATT&CK 扩展 STIX 2.0 格式的一般方式有以下三种:
-
自定义对象类型。以
x-mitre-
为前缀的对象类型, 例如x-mitre-matrix
, 是扩展 STIX 2.0 规范的自定义 STIX 类型。它们遵循通用的STIX 域对象模式 但描述了 STIX 2.0 中定义的类型未涵盖的概念。 -
现有对象类型的扩展。扩展 STIX 2.0 规范的字段带有前缀
x_mitre_
,例如在attack-patterns
中的x_mitre_platforms
。除了关系之外的所有对象都可以应用以下扩展属性:
字段 类型 描述 x_mitre_version
string 格式中的对象版本t major.minor
其中major
和minor
是整型。 更新对象内容时,ATT&CK 会增加此版本号。x_mitre_contributors
string[] 为对象做出贡献的人和组织。 x_mitre_deprecated
boolean 请参阅 使用已弃用和撤销的对象. -
新的关系类型。与自定义对象类型和扩展字段不同,自定义关系类型没有前缀
x_mitre_
. 可以在关系 部分找到完整的关系类型列表,其中还提到了该类型是否为默认 STIX 类型。
另请参阅有关 自定义STIX的文档。
ATT&CK中的ID
ATT&CK 中的对象可能有几种不同的 ID。
ATT&CK ID
最常用的 ID 格式称为 ATT&CK ID 或简称 ID。每种不同类型的 ATT&CK 对象在 ATT&CK ID 格式上都有自己的变化:
ATT&CK concept | ID format |
---|---|
Matrix(矩阵) | MAxxxx |
Tactic(战术) | TAxxxx |
Technique(技术) | Txxxx |
Sub-Technique(子技术) | Txxxx.yyy |
Mitigation(缓解措施) | Mxxxx |
Group(组织) | Gxxxx |
Software(软件) | Sxxxx |
Data Source(数据源) | DSxxxx |
Campaign(行动) | Cxxxx |
ATT&CK ID 通常但并非总是唯一的。有关缓解和技术之间的 ID 冲突的边缘案例,请参阅 与技术ATT&CK ID的冲突 。
ATT&CK ID 可以在所有对象的第一个外部引用中找到,除了关系(没有 ATT&CK ID)。第一个外部参考还包括一个url
字段,该字段链接到ATT&CK 网站上描述该对象的页面。
STIX ID
除了 ATT&CK ID,ATT&CK 中的所有对象(包括关系)在id
对象的字段中都有 STIX ID。与 ATT&CK ID 不同,STIX ID 保证是唯一的。因此,STIX ID 是以编程方式检索和引用对象的最佳方式。
其他 ID
在对象的外部引用中可以找到其他几个 ID:
NIST 移动威胁目录 ID 可以在外部参考所在的移动域中找到一些source_name技术"NIST Mobile Threat Catalogue"
- NIST 移动威胁目录 ID 可以移动领域中的一些技术中找到,其中外部引用的
source_name
是"NIST Mobile Threat Catalogue"
。 - CAPEC ID可以在企业领域中的一些技术中找到,其中外部引用的
source_name
是"capec"
。
ATT&CK类型
矩阵
ATT&CK 矩阵的整体布局存储在x-mitre-matrix
对象中。作为自定义 STIX 类型,它们仅遵循通用STIX 域对象模式。
矩阵使用以下字段扩展通用 SDO 格式:
字段 | 类型 | 描述 |
---|---|---|
tactic_refs | string[] | 矩阵数组包含与矩阵策略相对应的 STIX ID的tactic_refs 有序列表。x-mitre-tactic 的顺序tactic_refs 决定了战术应该出现在矩阵中的顺序。 |
映射矩阵,战术和技术
技术通过使用它们的kill_chain_phases
属性映射到战术。其中kill_chain_name
是mitre-attack
、mitre-mobile-attack
或mitre-ics-attack
(分别用于企业、移动和 ics 域),phase_name
对应于对象x-mitre-tactic
的x_mitre_shortname
属性。tactic_refs
矩阵使用嵌入的关系按顺序定义它们的策略。

战术
ATT&CK 中的战术由x-mitre-tactic
对象定义。作为自定义 STIX 类型,它们仅遵循通用STIX 域对象模式。
战术使用以下字段扩展了通用 SDO 格式:
字段 | 类型 | 描述 |
---|---|---|
x_mitre_shortname | string | 战术的x_mitre_shortname 用于将技术映射到战术中。它对应kill_chain_phases.phase_name 于战术中的技术。有关更多信息,请参阅映射矩阵,战术和技术。 |
技术
ATT&CK 中的技术被定义为攻击模式对象。
技术与以下字段的攻击模式格式不同。领域和战术特定字段在“适用于”列中标记:
字段 | 类型 | 适用于 | 描述 |
---|---|---|---|
x_mitre_detection | string | 所有技术 | 识别技术是否被对手使用。 |
x_mitre_platforms | string[] | 所有技术 | 适用于该技术的平台列表。 |
x_mitre_data_sources | string[] | 企业*和 ICS 域 | 可用于识别正在执行的操作或结果的信息来源。 |
x_mitre_is_subtechnique | boolean | 企业域 | 如果是true ,表示attack-pattern 是一种子技术。见子技术。 |
x_mitre_system_requirements | string[] | 企业域 | 有关攻击者需要满足的要求或有关该技术工作可能需要的系统状态(软件、补丁级别等)的附加信息。 |
x_mitre_tactic_type | string[] | 移动域 | “Post-Adversary Device Access”, “Pre-Adversary Device Access”,或 “Without Adversary Device Access”. |
x_mitre_permissions_required | string[] | 企业域中的权限提升战术 | 攻击者在系统上执行该技术所需的最低权限级别。 |
x_mitre_effective_permissions | string[] | 企业域中的权限提升战术 | 对手通过执行该技术将获得的权限级别。 |
x_mitre_defense_bypassed | string[] | 企业域中的防御绕过战术 | 该技术可以绕过的防御工具、方法或流程的列表。 |
x_mitre_remote_support | boolean | 企业域中的执行战术 | 如果为真,则该技术可用于在远程系统上执行某些操作。 |
x_mitre_impact_type | string[] | 企业域中的影响战术 | 表示该技术是否可用于完整性或可用性攻击。 |
*在企业域中,数据源通过x-mitre-data-source和x-mitre-data-component对象表示,它们与技术的关系通过类型的关系表示detects
。出于向后兼容的目的,该x_mitre_data_sources
字段仍将保留在企业技术上,但建议不要使用它,因为它不包括数据模型的完整上下文。
有关技术如何映射到战术和矩阵的更多信息,请参阅映射矩阵,战术和技术。
子技术
ATT&CK 中的子技术表示为attack-pattern
并遵循与技术相同的格式。它们的不同之处在于它们有一个布尔字段 (x_mitre_is_subtechnique
) 将它们标记为子技术,以及类型的关系subtechnique-of
,其中source_ref
是子技术,而target_ref
是父技术。一个子技术只能有一个父技术,但技术可以有多个子技术。
此外:
- 子技术 ATT&CK ID 是其父 ID 的后缀。对于给定的子技术 ID
Txxxx.yyy
,Txxxx
是父技术 ID,yyy
是子技术 ID。子技术具有唯一的 STIX ID。 - 子技术与它们的父技术具有相同的战术。
- 子技术具有其父技术平台的子集。
子技术只存在于企业领域。
程序
ATT&CK 不代表他们自己的 STIX 类型下的程序。相反,程序被表示为类型关系uses
,其中target_ref
是一种技术。这意味着程序可以源于组织(intrusion-sets
)和软件(malware
或tools
)的使用。程序的内容在关系描述中阐述。
缓解措施
ATT&CK 中的缓解被定义为行动过程 对象。ATT&CK 缓解措施不偏离 STIXcourse-of-action
规范。
与技术ATT&CK ID的冲突
在 v5(2019 年 7 月发布)之前的 ATT&CK 版本中,缓解措施与技术具有 1:1 的关系并共享其技术的 ID。这些旧的 1:1 缓解措施在后续的 ATT&CK 版本中已弃用,并且可以在查询中过滤掉 - 请参阅删除已撤销和已弃用的对象。
组织
ATT&CK 中的 Group 被定义为一个入侵集 对象。ATT&CK 组不偏离 STIXintrusion-set
格式。
软件
ATT&CK 中的软件是两种不同 STIX 类型的结合:malware 恶意软件 和tool 工具。
malware
和tool
类型都与STIX 格式不同,具有以下字段:
字段 | 类型 | 描述 |
---|---|---|
x_mitre_platforms | string[] | 适用于该软件的平台列表。 |
x_mitre_aliases | string[] | 给定软件的别名列表。 |
数据源和数据组件
数据源和数据组件表示可用于检测技术的数据。数据组件嵌套在数据源中,但有自己的 STIX 对象。
- 一个数据组件只能有一个父数据源。
- 一个数据源可以有任意数量的数据组件。
- 数据组件可以映射到任意数量的技术。
数据源和数据组件的一般结构如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ggqZialX-1670932584225)(:/27d493ae651546888ac9cfda77adfe2b)]
在 ATT&CK v10 之前,数据源存储在x_mitre_data_sources
技术领域。此表示仍可用于向后兼容的目的,并且确实正确反映了当前的数据源集。但是,由于该表示中的信息丢失,我们建议不要在旧应用程序中使用它。ICS 域的 ATT&CK 仍然只使用该x_mitre_data_sources
字段。
数据源
ATT&CK 中的数据源由x-mitre-data-source
对象定义。作为自定义 STIX 类型,它们仅遵循通用STIX 域对象模式。
数据源使用以下字段扩展通用 SDO 格式:
字段 | 类型 | 描述 |
---|---|---|
x_mitre_platforms | string[] | 适用于数据源的平台列表。 |
x_mitre_collection_layers | string[] | 可以从中收集数据的地点列表。 |
数据组件
ATT&CK 中的数据组件表示为一个x-mitre-data-component
对象。作为自定义 STIX 类型,它们仅遵循通用STIX 域对象模式。
数据组件使用以下字段扩展通用 SDO 格式:
字段 | 类型 | 描述 |
---|---|---|
x_mitre_data_source_ref | embedded relationship (string) | 此组件所属的数据源的 STIX ID。 |
行动
ATT&CK 中的行动被定义为campaign 对象。
行动使用以下字段扩展了通用 SDO 格式:
字段 | 类型 | 描述 |
---|---|---|
x_mitre_first_seen_citation | string | 首次以"(Citation:<citation>)”形式报告行动时的一对多引文,其中<citation>可以作为外部参考之一的来源名称之一被找到。 |
x_mitre_last_seen_citation | string | 上次以"(Citation:<citation>)”形式报告行动时的一对多引文,其中<citation>可以作为外部参考之一的来源名称之一被找到。 |
关系
ATT&CK 中的对象通过 STIX关系对象相互关联。这些关系传达了诸如使用技术的组织(在技术页面上也称为“程序(procedure)示例”)、技术和子技术的层次结构等概念。

与数据集中的其他对象不同,关系不能被撤销或弃用。如果关系所附加的对象之一被撤销或弃用,则关系被视为已弃用/撤销。有关已撤销对象的更多信息,请参阅使用已弃用和撤销的对象。
关系通常具有将对象之间的关系上下文化的描述。
来源类型 | 关系类型 | 目标类型 | 自定义类型? | 关于 |
---|---|---|---|---|
intrusion-set | uses | malware or tool | No | 组织使用软件。 |
intrusion-set | uses | attack-pattern | No | 组织使用技术,这也被认为是一个示例程序。 |
malware or tool | uses | attack-pattern | No | 软件使用技术,这也被认为是一个示例程序。 |
campaign | uses | malware or tool | No | 行动使用软件。 |
campaign | uses | attack-pattern | No | 行动使用技术,这也被认为是一个示例程序。 |
campaign | attributed-to | intrusion-set | No | 归因于某个组织的行动。 |
course-of-action | mitigates | attack-pattern | No | 缓解措施使用技术。 |
attack-pattern | subtechnique-of | attack-pattern | Yes | 一种技术的子技术,其中source_ref 是子技术,target_ref 是父技术。 |
x-mitre-data-component | detects | attack-pattern | Yes | 检测技术的数据组件。 |
any type | revoked-by | any type | Yes | 目标对象是源对象的替代品。仅在对象具有相同类型且源对象将具有属性revoked=true 的情况下发生。有关已撤销对象的更多信息,请参阅使用已弃用和撤销的对象。 |
请注意,由于组织使用软件和软件使用技术,因此可以将组织视为其软件使用的技术的间接用户。请参阅]获取组织软件使用的技术。
在python中访问ATT&CK数据
在 Python 中有几种获取 ATT&CK 数据的方法。它们都将提供一个实现 DataStore API 的对象,并且可以与Python使用方法部分中提供的方法互换使用。
本节使用stix2 python 库。有关如何以编程方式使用 STIX 的更多信息,请参阅STIX2 Python API 文档。
要求和引入
在安装要求之前,我们建议设置一个虚拟环境:
- 创建虚拟环境:
- macOS and Linux:
python3 -m venv env
- Windows:
py -m venv env
- macOS and Linux:
- Activate the virtual environment:
- macOS and Linux:
source env/bin/activate
- Windows:
env/Scripts/activate.bat
- macOS and Linux:
stix2
stix2 可以按照其存储库中的说明进行安装。可以从基础包中导入此存储库中的方法,例如:
from stix2 import Filter
但是,如果您的目标是使用新对象扩展 ATT&CK 数据集或实施复杂的工作流程,则可能需要v20
对某些导入使用说明符。这可确保对象使用 STIX 2.0 API 而不是 STIX 2.1 API。例如:
from stix2.v20 import AttackPattern
可以在此处查看具有版本化导入的类的完整列表。
taxii2客户端
Taxii2-client 可以按照其存储库上的说明进行安装。ATT&CK TAXII服务器实现的是2.0版本的TAXII规范,但是默认导入taxii2client
(2.0.0及以上版本)使用的是2.1版本的TAXII规范,如果没有注意版本,连接TAXII服务器会导致406响应。
如果 TAXII 客户端收到 406 响应,请确保运行的是最新版本(pip install --upgrade stix2
或pip install --upgrade taxii2-client
)。此外,请确保正在运行 2.0 版本的客户端(使用v20
导入),如下所示,以便与 ATT&CK TAXII 2.0 服务器通信。
from taxii2client.v20 import Collection
访问本地内容
许多用户可能会选择通过此 仓库 上的 STIX 数据的本地副本访问 ATT&CK 内容。出于以下几个原因,这可能是有利的:
- 初始下载后不需要互联网访问
- 用户可以根据需要修改ATT&CK内容
- 下载的副本是静态的,因此对ATT&CK目录的更新不会导致自动化工作流程中的错误。用户仍然可以通过克隆版本的数据来手动更新
通过FileSystemSource访问
此仓库中的每个域都根据STIX2 FileSystem 规范进行格式化。因此,您可以使用 FileSystemSource
来加载域,例如加载企业攻击域:
from stix2 import FileSystemSource
src = FileSystemSource('./cti/enterprise-attack')
通过bundle访问
如果您更喜欢只下载域包,例如enterprise-attack.json,您仍然可以使用 MemoryStore 加载它:
from stix2 import MemoryStore
src = MemoryStore()
src.load_from_file("enterprise-attack.json")
访问实时内容
一些用户可能更喜欢通过互联网访问“实时”ATT&CK 内容。这有几个原因是有利的:
- 始终与不断发展的 ATT&CK 目录保持同步
- 不需要初始下载 ATT&CK 内容,通常需要较少的设置
从ATT&CK TAXII服务器访问
用户可以从 ATT&CK TAXII 官方服务器访问 ATT&CK 数据。在 TAXII 中,ATT&CK 域表示为具有静态 ID 的集合:
域 | 集合 ID |
---|---|
enterprise-attack | 95ecc380-afe9-11e4-9b6c-751b66dd541e |
mobile-attack | 2f669986-b40b-4423-b720-4396ca6a462b |
ics-attack | 02c3ef24-9cd4-48f3-a99f-b74ce24f1d34 |
还可以直接从服务器获取可用集合的列表:
from taxii2client.v20 import Server # only specify v20 if your installed version is >= 2.0.0
server = Server("https://cti-taxii.mitre.org/taxii/")
api_root = server.api_roots[0]
# Print name and ID of all ATT&CK domains available as collections
for collection in api_root.collections:
print(collection.title.ljust(20) + collection.id)
以下方法演示了如何从 TAXII 服务器访问企业攻击数据。
from stix2 import TAXIICollectionSource
from taxii2client.v20 import Collection # only specify v20 if your installed version is >= 2.0.0
collections = {
"enterprise_attack": "95ecc380-afe9-11e4-9b6c-751b66dd541e",
"mobile_attack": "2f669986-b40b-4423-b720-4396ca6a462b",
"ics-attack": "02c3ef24-9cd4-48f3-a99f-b74ce24f1d34"
}
collection = Collection(f"https://cti-taxii.mitre.org/stix/collections/{collections['enterprise_attack']}/")
src = TAXIICollectionSource(collection)
关于 TAXII 的更多信息,请参阅 oasis-open 的TAXII 简介。
通过请求从 Github 访问
Users can alternatively access the data from MITRE/CTI using HTTP requests, and load the resulting content into a MemoryStore.
While typically the TAXII method is more desirable for “live” access, this method can be useful if you want to
access data on a branch of the MITRE/CTI repo (the TAXII server only holds the master branch) or in the case of a TAXII server outage.
import requests
from stix2 import MemoryStore
def get_data_from_branch(domain, branch="master"):
"""get the ATT&CK STIX data from MITRE/CTI. Domain should be 'enterprise-attack', 'mobile-attack' or 'ics-attack'. Branch should typically be master."""
stix_json = requests.get(f"https://raw.githubusercontent.com/mitre/cti/{branch}/{domain}/{domain}.json").json()
return MemoryStore(stix_data=stix_json["objects"])
src = get_data_from_branch("enterprise-attack")
访问特定版本的ATT&CK
ATT&CK 版本在 MITRE/CTI 存储库中使用tags进行跟踪。前缀为 ATT&CK 的标签ATT&CK-v
对应 ATT&CK 版本,前缀CAPEC-v
为 CAPEC 版本的标签。您可以在 ATT&CK 网站的 ATT&CK版本页面上找到有关 ATT&CK 版本的更多信息。
除了查看给定版本的标签下的 repo 或使用浏览器从 github 下载 STIX 之外,您还可以使用requests 方法的变体来访问特定版本的 ATT&CK:
import requests
from stix2 import MemoryStore
def get_data_from_version(domain, version):
"""get the ATT&CK STIX data for the given version from MITRE/CTI. Domain should be 'enterprise-attack', 'mobile-attack' or 'ics-attack'."""
stix_json = requests.get(f"https://raw.githubusercontent.com/mitre/cti/ATT%26CK-v{version}/{domain}/{domain}.json").json()
return MemoryStore(stix_data=stix_json["objects"])
src = get_data_from_version("enterprise-attack", "5.2")
您可以使用 github API 以编程方式获取 ATT&CK 版本列表:
import requests
import re
refToTag = re.compile(r"ATT&CK-v(.*)")
tags = requests.get("https://api.github.com/repos/mitre/cti/git/refs/tags").json()
versions = list(map(lambda tag: refToTag.search(tag["ref"]).groups()[0] , filter(lambda tag: "ATT&CK-v" in tag["ref"], tags)))
# versions = ["1.0", "2.0", ...]
同时访问多个域
由于 ATT&CK 存储在多个域中(在撰写本文时,企业攻击、移动攻击和 ics-attack),上述方法仅允许您一次使用单个域。虽然通常域的硬分离是有利的,但有时将域组合到单个数据存储中很有用。使用上述任何一种方法来获取各个数据存储,然后使用以下方法将它们组合成一个复杂数据源:
from stix2 import CompositeDataSource
src = CompositeDataSource()
src.add_data_sources([enterprise_attack_src, mobile_attack_src, ics_attack_src])
然后,您可以像使用单个域的数据存储一样使用此复杂数据源。
Python使用方法
以下是可用于处理 ATT&CK 数据的示例 python 方法。他们假设存在一个实现数据存储API 的对象。在 python 中访问 ATT&CK 数据部分中概述的任何方法都应提供实现此 API 的对象。
本节使用stix2 python 库。有关如何以编程方式使用 STIX 的更多信息,请参阅STIX2 Python API 文档。另请参阅要求和引入部分。
获取对象
本节中的方法介绍了如何查询数据集以获取单个对象。
By STIX ID
以下方法可用于根据其 STIX ID 检索对象。这通常是使用 ATT&CK 数据时检索对象的首选方式,因为 STIX ID 保证是唯一的。
g0075 = src.get("intrusion-set--f40eb8ce-2a74-4e56-89a1-227021410142")
By ATT&CK ID
以下方法可用于根据其 ATT&CK ID 检索对象:
from stix2 import Filter
g0075 = src.query([ Filter("external_references.external_id", "=", "G0075") ])[0]
注意:在 ATT&CK 的早期版本中,缓解措施与技术具有 1:1 的关系并共享其技术的 ID。因此,上述方法不适用于技术,因为技术 ATTT&CK ID 并不是真正唯一的。通过指定您正在寻找的 STIX 类型attack-pattern
,可以避免此问题。
from stix2 import Filter
t1134 = src.query([
Filter("external_references.external_id", "=", "T1134"),
Filter("type", "=", "attack-pattern")
])[0]
导致此问题的旧 1:1 缓解措施已弃用,因此您也可以通过这种方式将它们过滤掉 - 请参阅删除已撤销和已弃用的对象。
By name
以下方法根据其名称检索对象:
from stix2 import Filter
def get_technique_by_name(thesrc, name):
filt = [
Filter('type', '=', 'attack-pattern'),
Filter('name', '=', name)
]
return thesrc.query(filt)
# get the technique titled "System Information Discovery"
get_technique_by_name(src, 'System Information Discovery')
By alias
以下方法可用于查找与给定别名对应的组织:
from stix2 import Filter
def get_group_by_alias(thesrc, alias):
return thesrc.query([
Filter('type', '=', 'intrusion-set'),
Filter('aliases', '=', alias)
])[0]
get_group_by_alias(src, 'Cozy Bear')
获取多个对象
本节中的方法介绍了如何查询数据集的多个对象。
⚠ 在处理基于一组特征的查询以返回对象时,您最终可能会得到一些不再由 ATT&CK 维护的对象。这些是标记为已弃用或已撤销的对象。我们保留这些过时的对象,以便依赖于它们的工作流不会中断,但我们建议您尽可能避免使用它们。有关详细信息,请参阅使用已弃用和撤销的对象部分。
按类型划分的对象
有关 ATT&CK 类型到 STIX 类型的映射,请参阅ATT&CK 数据模型。
from stix2 import Filter
# use the appropriate STIX type in the query according to the desired ATT&CK type
groups = src.query([ Filter("type", "=", "intrusion-set") ])
获取技术或子技术
ATT&CK 技术和子技术都表示为attack-pattern
对象。因此,需要进一步解析以获得具体的技术或子技术。
from stix2 import Filter
def get_techniques_or_subtechniques(thesrc, include="both"):
"""Filter Techniques or Sub-Techniques from ATT&CK Enterprise Domain.
include argument has three options: "techniques", "subtechniques", or "both"
depending on the intended behavior."""
if include == "techniques":
query_results = thesrc.query([
Filter('type', '=', 'attack-pattern'),
Filter('x_mitre_is_subtechnique', '=', False)
])
elif include == "subtechniques":
query_results = thesrc.query([
Filter('type', '=', 'attack-pattern'),
Filter('x_mitre_is_subtechnique', '=', True)
])
elif include == "both":
query_results = thesrc.query([
Filter('type', '=', 'attack-pattern')
])
else:
raise RuntimeError("Unknown option %s!" % include)
return query_results
subtechniques = get_techniques_or_subtechniques(src, "subtechniques")
subtechniques = remove_revoked_deprecated(subtechniques) # see https://github.com/mitre/cti/blob/master/USAGE.md#removing-revoked-and-deprecated-objects
获取软件
因为软件是两种 STIX 类型 (tool
和malware
) 的结合,所以访问软件的过程稍微复杂一些。
from itertools import chain
from stix2 import Filter
def get_software(thesrc):
return list(chain.from_iterable(
thesrc.query(f) for f in [
Filter("type", "=", "tool"),
Filter("type", "=", "malware")
]
))
get_software(src)
按内容分类的对象
有时通过对象描述的内容来查询对象可能很有用:
from stix2 import Filter
def get_techniques_by_content(src, content):
techniques = src.query([ Filter('type', '=', 'attack-pattern') ])
return list(filter(lambda t: content.lower() in t.description.lower(), techniques))
# Get all techniques where the string LSASS appears in the description
get_techniques_by_content(src, 'LSASS')
按平台分类的技术
技术与一个或多个平台相关联。您可以使用以下代码查询特定平台下的技术:
from stix2 import Filter
def get_techniques_by_platform(thesrc, platform):
return thesrc.query([
Filter('type', '=', 'attack-pattern'),
Filter('x_mitre_platforms', '=', platform)
])
# get techniques in the windows platform
get_techniques_by_platform(src, 'Windows')
按战术分类的技术
技术通过其 kill_chain_phases
属性与策略相关。每个phase_name
杀伤链阶段对应x_mitre_shortname
一个战术。
from stix2 import Filter
def get_tactic_techniques(thesrc, tactic):
# double checking the kill chain is MITRE ATT&CK
# note: kill_chain_name is different for other domains:
# - enterprise: "mitre-attack"
# - mobile: "mitre-mobile-attack"
# - ics: "mitre-ics-attack"
return thesrc.query([
Filter('type', '=', 'attack-pattern'),
Filter('kill_chain_phases.phase_name', '=', tactic),
Filter('kill_chain_phases.kill_chain_name', '=', 'mitre-attack'),
])
# use the x_mitre_shortname as argument
get_tactic_techniques(src, 'defense-evasion')
按矩阵分类的战术
战术是单个的对象 ( x-mitre-tactic
),它们在矩阵 ( x-mitre-matrix
) 中的顺序可在矩阵的tactic_refs
属性中找到。该列表中的战术顺序与该矩阵中的战术顺序相匹配。以下配方返回输入数据存储的每个矩阵中的结构化战术列表。
from stix2 import Filter
def getTacticsByMatrix(thesrc):
tactics = {}
matrix = thesrc.query([
Filter('type', '=', 'x-mitre-matrix'),
])
for i in range(len(matrix)):
tactics[matrix[i]['name']] = []
for tactic_id in matrix[i]['tactic_refs']:
tactics[matrix[i]['name']].append(thesrc.get(tactic_id))
return tactics
# get tactic layout
getTacticsByMatrix(src)
自给定日期以来创建或修改的对象
有时您可能想要获取在特定时间后创建或修改的对象列表。
from stix2 import Filter
def get_created_after(thesrc, timestamp):
filt = [
Filter('created', '>', timestamp)
]
return thesrc.query(filt)
get_created_after(src, "2018-10-01T00:14:20.652Z")
def get_modified_after(thesrc, timestamp):
filt = [
Filter('modified', '>', timestamp)
]
return thesrc.query(filt)
get_modified_after(src, "2018-10-01T00:14:20.652Z")
我们不建议您使用此方法来检测知识库内容的更改。为了检测整个知识库的更新,我们建议使用请求来检查 ATT&CK 的已发布版本列表。
获取相关对象
使用 ATT&CK 的大部分工作围绕解析对象之间的关系展开。不仅跟踪相关对象而且跟踪关系本身很有用,因为通常会出现描述以将关系的性质置于上下文中。以下方法演示了关系的一些常见用途。
构建联系的微库
注意:以下代码旨在与包含活动对象的 ATT&CK v12 版本一起使用。这些示例向后兼容 ATT&CK 中省略了这些对象的先前版本。
该微库可用于构建 stixID 到相关对象和关系的查找表。每个访问器函数的参数是一个 STIX2 MemoryStore,用于构建关系映射。
from pprint import pprint
from stix2 import MemoryStore, Filter
# See section below on "Removing revoked and deprecated objects"
def remove_revoked_deprecated(stix_objects):
"""Remove any revoked or deprecated objects from queries made to the data source"""
# Note we use .get() because the property may not be present in the JSON data. The default is False
# if the property is not set.
return list(
filter(
lambda x: x.get("x_mitre_deprecated", False) is False and x.get("revoked", False) is False,
stix_objects
)
)
def get_related(thesrc, src_type, rel_type, target_type, reverse=False):
"""build relationship mappings
params:
thesrc: MemoryStore to build relationship lookups for
src_type: source type for the relationships, e.g "attack-pattern"
rel_type: relationship type for the relationships, e.g "uses"
target_type: target type for the relationship, e.g "intrusion-set"
reverse: build reverse mapping of target to source
"""
relationships = thesrc.query([
Filter('type', '=', 'relationship'),
Filter('relationship_type', '=', rel_type),
Filter('revoked', '=', False),
])
# See section below on "Removing revoked and deprecated objects"
relationships = remove_revoked_deprecated(relationships)
# stix_id => [ { relationship, related_object_id } for each related object ]
id_to_related = {}
# build the dict
for relationship in relationships:
if src_type in relationship.source_ref and target_type in relationship.target_ref:
if (relationship.source_ref in id_to_related and not reverse) or (relationship.target_ref in id_to_related and reverse):
# append to existing entry
if not reverse:
id_to_related[relationship.source_ref].append({
"relationship": relationship,
"id": relationship.target_ref
})
else:
id_to_related[relationship.target_ref].append({
"relationship": relationship,
"id": relationship.source_ref
})
else:
# create a new entry
if not reverse:
id_to_related[relationship.source_ref] = [{
"relationship": relationship,
"id": relationship.target_ref
}]
else:
id_to_related[relationship.target_ref] = [{
"relationship": relationship,
"id": relationship.source_ref
}]
# all objects of relevant type
if not reverse:
targets = thesrc.query([
Filter('type', '=', target_type),
Filter('revoked', '=', False)
])
else:
targets = thesrc.query([
Filter('type', '=', src_type),
Filter('revoked', '=', False)
])
# build lookup of stixID to stix object
id_to_target = {}
for target in targets:
id_to_target[target.id] = target
# build final output mappings
output = {}
for stix_id in id_to_related:
value = []
for related in id_to_related[stix_id]:
if not related["id"] in id_to_target:
continue # targeting a revoked object
value.append({
"object": id_to_target[related["id"]],
"relationship": related["relationship"]
})
output[stix_id] = value
return output
# software:group
def software_used_by_groups(thesrc):
"""returns group_id => {software, relationship} for each software used by the group and each software used by campaigns attributed to the group."""
# get all software used by groups
tools_used_by_group = get_related(thesrc, "intrusion-set", "uses", "tool")
malware_used_by_group = get_related(thesrc, "intrusion-set", "uses", "malware")
software_used_by_group = {**tools_used_by_group, **malware_used_by_group} # group_id -> [{software, relationship}]
# get groups attributing to campaigns and all software used by campaigns
software_used_by_campaign = get_related(thesrc, "campaign", "uses", "tool")
malware_used_by_campaign = get_related(thesrc, "campaign", "uses", "malware")
for id in malware_used_by_campaign:
if id in software_used_by_campaign:
software_used_by_campaign[id].extend(malware_used_by_campaign[id])
else:
software_used_by_campaign[id] = malware_used_by_campaign[id]
campaigns_attributed_to_group = {
"campaigns": get_related(thesrc, "campaign", "attributed-to", "intrusion-set", reverse=True), # group_id => {campaign, relationship}
"software": software_used_by_campaign # campaign_id => {software, relationship}
}
for group_id in campaigns_attributed_to_group["campaigns"]:
software_used_by_campaigns = []
# check if attributed campaign is using software
for campaign in campaigns_attributed_to_group["campaigns"][group_id]:
campaign_id = campaign["object"]["id"]
if campaign_id in campaigns_attributed_to_group["software"]:
software_used_by_campaigns.extend(campaigns_attributed_to_group["software"][campaign_id])
# update software used by group to include software used by a groups attributed campaign
if group_id in software_used_by_group:
software_used_by_group[group_id].extend(software_used_by_campaigns)
else:
software_used_by_group[group_id] = software_used_by_campaigns
return software_used_by_group
def groups_using_software(thesrc):
"""returns software_id => {group, relationship} for each group using the software and each software used by attributed campaigns."""
# get all groups using software
groups_using_tool = get_related(thesrc, "intrusion-set", "uses", "tool", reverse=True)
groups_using_malware = get_related(thesrc, "intrusion-set", "uses", "malware", reverse=True)
groups_using_software = {**groups_using_tool, **groups_using_malware} # software_id => {group, relationship}
# get campaigns attributed to groups and all campaigns using software
campaigns_using_software = get_related(thesrc, "campaign", "uses", "tool", reverse=True)
campaigns_using_malware = get_related(thesrc, "campaign", "uses", "malware", reverse=True)
for id in campaigns_using_malware:
if id in campaigns_using_software:
campaigns_using_software[id].extend(campaigns_using_malware[id])
else:
campaigns_using_software[id] = campaigns_using_malware[id]
groups_attributing_to_campaigns = {
"campaigns": campaigns_using_software,# software_id => {campaign, relationship}
"groups": get_related(thesrc, "campaign", "attributed-to", "intrusion-set") # campaign_id => {group, relationship}
}
for software_id in groups_attributing_to_campaigns["campaigns"]:
groups_attributed_to_campaigns = []
# check if campaign is attributed to group
for campaign in groups_attributing_to_campaigns["campaigns"][software_id]:
campaign_id = campaign["object"]["id"]
if campaign_id in groups_attributing_to_campaigns["groups"]:
groups_attributed_to_campaigns.extend(groups_attributing_to_campaigns["groups"][campaign_id])
# update groups using software to include software used by a groups attributed campaign
if software_id in groups_using_software:
groups_using_software[software_id].extend(groups_attributed_to_campaigns)
else:
groups_using_software[software_id] = groups_attributed_to_campaigns
return groups_using_software
# software:campaign
def software_used_by_campaigns(thesrc):
"""returns campaign_id => {software, relationship} for each software used by the campaign."""
tools_used_by_campaign = get_related(thesrc, "campaign", "uses", "tool")
malware_used_by_campaign = get_related(thesrc, "campaign", "uses", "malware")
return {**tools_used_by_campaign, **malware_used_by_campaign}
def campaigns_using_software(thesrc):
"""returns software_id => {campaign, relationship} for each campaign using the software."""
campaigns_using_tool = get_related(thesrc, "campaign", "uses", "tool", reverse=True)
campaigns_using_malware = get_related(thesrc, "campaign", "uses", "malware", reverse=True)
return {**campaigns_using_tool, **campaigns_using_malware}
# campaign:group
def groups_attributing_to_campaign(thesrc):
"""returns campaign_id => {group, relationship} for each group attributing to the campaign."""
return get_related(thesrc, "campaign", "attributed-to", "intrusion-set")
def campaigns_attributed_to_group(thesrc):
"""returns group_id => {campaign, relationship} for each campaign attributed to the group."""
return get_related(thesrc, "campaign", "attributed-to", "intrusion-set", reverse=True)
# technique:group
def techniques_used_by_groups(thesrc):
"""returns group_id => {technique, relationship} for each technique used by the group and each
technique used by campaigns attributed to the group."""
# get all techniques used by groups
techniques_used_by_groups = get_related(thesrc, "intrusion-set", "uses", "attack-pattern") # group_id => {technique, relationship}
# get groups attributing to campaigns and all techniques used by campaigns
campaigns_attributed_to_group = {
"campaigns": get_related(thesrc, "campaign", "attributed-to", "intrusion-set", reverse=True), # group_id => {campaign, relationship}
"techniques": get_related(thesrc, "campaign", "uses", "attack-pattern") # campaign_id => {technique, relationship}
}
for group_id in campaigns_attributed_to_group["campaigns"]:
techniques_used_by_campaigns = []
# check if attributed campaign is using technique
for campaign in campaigns_attributed_to_group["campaigns"][group_id]:
campaign_id = campaign["object"]["id"]
if campaign_id in campaigns_attributed_to_group["techniques"]:
techniques_used_by_campaigns.extend(campaigns_attributed_to_group["techniques"][campaign_id])
# update techniques used by groups to include techniques used by a groups attributed campaign
if group_id in techniques_used_by_groups:
techniques_used_by_groups[group_id].extend(techniques_used_by_campaigns)
else:
techniques_used_by_groups[group_id] = techniques_used_by_campaigns
return techniques_used_by_groups
def groups_using_technique(thesrc):
"""returns technique_id => {group, relationship} for each group using the technique and each campaign attributed to groups using the technique."""
# get all groups using techniques
groups_using_techniques = get_related(thesrc, "intrusion-set", "uses", "attack-pattern", reverse=True) # technique_id => {group, relationship}
# get campaigns attributed to groups and all campaigns using techniques
groups_attributing_to_campaigns = {
"campaigns": get_related(thesrc, "campaign", "uses", "attack-pattern", reverse=True), # technique_id => {campaign, relationship}
"groups": get_related(thesrc, "campaign", "attributed-to", "intrusion-set") # campaign_id => {group, relationship}
}
for technique_id in groups_attributing_to_campaigns["campaigns"]:
campaigns_attributed_to_group = []
# check if campaign is attributed to group
for campaign in groups_attributing_to_campaigns["campaigns"][technique_id]:
campaign_id = campaign["object"]["id"]
if campaign_id in groups_attributing_to_campaigns["groups"]:
campaigns_attributed_to_group.extend(groups_attributing_to_campaigns["groups"][campaign_id])
# update groups using techniques to include techniques used by a groups attributed campaign
if technique_id in groups_using_techniques:
groups_using_techniques[technique_id].extend(campaigns_attributed_to_group)
else:
groups_using_techniques[technique_id] = campaigns_attributed_to_group
return groups_using_techniques
# technique:campaign
def techniques_used_by_campaigns(thesrc):
"""returns campaign_id => {technique, relationship} for each technique used by the campaign."""
return get_related(thesrc, "campaign", "uses", "attack-pattern")
def campaigns_using_technique(thesrc):
"""returns technique_id => {campaign, relationship} for each campaign using the technique."""
return get_related(thesrc, "campaign", "uses", "attack-pattern", reverse=True)
# technique:software
def techniques_used_by_software(thesrc):
"""return software_id => {technique, relationship} for each technique used by the software."""
techniques_by_tool = get_related(thesrc, "tool", "uses", "attack-pattern")
techniques_by_malware = get_related(thesrc, "malware", "uses", "attack-pattern")
return {**techniques_by_tool, **techniques_by_malware}
def software_using_technique(thesrc):
"""return technique_id => {software, relationship} for each software using the technique."""
tools_by_technique_id = get_related(thesrc, "tool", "uses", "attack-pattern", reverse=True)
malware_by_technique_id = get_related(thesrc, "malware", "uses", "attack-pattern", reverse=True)
return {**tools_by_technique_id, **malware_by_technique_id}
# technique:mitigation
def mitigation_mitigates_techniques(thesrc):
"""return mitigation_id => {technique, relationship} for each technique mitigated by the mitigation."""
return get_related(thesrc, "course-of-action", "mitigates", "attack-pattern", reverse=False)
def technique_mitigated_by_mitigations(thesrc):
"""return technique_id => {mitigation, relationship} for each mitigation of the technique."""
return get_related(thesrc, "course-of-action", "mitigates", "attack-pattern", reverse=True)
# technique:sub-technique
def subtechniques_of(thesrc):
"""return technique_id => {subtechnique, relationship} for each subtechnique of the technique."""
return get_related(thesrc, "attack-pattern", "subtechnique-of", "attack-pattern", reverse=True)
def parent_technique_of(thesrc):
"""return subtechnique_id => {technique, relationship} describing the parent technique of the subtechnique"""
return get_related(thesrc, "attack-pattern", "subtechnique-of", "attack-pattern")[0]
# technique:data-component
def datacomponent_detects_techniques(thesrc):
"""return datacomponent_id => {technique, relationship} describing the detections of each data component"""
return get_related(thesrc, "x-mitre-data-component", "detects", "attack-pattern")
def technique_detected_by_datacomponents(thesrc):
"""return technique_id => {datacomponent, relationship} describing the data components that can detect the technique"""
return get_related(thesrc, "x-mitre-data-component", "detects", "attack-pattern", reverse=True)
# Example usage:
src = MemoryStore()
src.load_from_file("path/to/enterprise-attack.json")
group_id_to_software = software_used_by_groups(src)
pprint(group_id_to_software["intrusion-set--2a158b0a-7ef8-43cb-9985-bf34d1e12050"]) # G0019
# [
# {
# "object": Malware, # S0061
# "relationship": Relationship # relationship between G0019 and S0061
# },
# {
# ...
# }
# ]
获取组织软件使用的技术
因为一个组织使用软件,而软件使用技术,所以可以将组织视为其软件使用的技术的间接用户。这些技术通常与一个组织直接使用的技术不同,尽管这两组技术偶尔会有交叉点。
以下方法可用于检索组软件使用的技术:
from stix2.utils import get_type_from_id
from stix2 import Filter
def get_techniques_by_group_software(thesrc, group_stix_id):
# get the malware, tools that the group uses
group_uses = [
r for r in thesrc.relationships(group_stix_id, 'uses', source_only=True)
if get_type_from_id(r.target_ref) in ['malware', 'tool']
]
# get the technique stix ids that the malware, tools use
software_uses = thesrc.query([
Filter('type', '=', 'relationship'),
Filter('relationship_type', '=', 'uses'),
Filter('source_ref', 'in', [r.source_ref for r in group_uses])
])
#get the techniques themselves
return thesrc.query([
Filter('type', '=', 'attack-pattern'),
Filter('id', 'in', [r.target_ref for r in software_uses])
])
get_techniques_by_group_software(src, "intrusion-set--f047ee18-7985-4946-8bfb-4ed754d3a0dd")
使用已弃用和撤销的对象
被认为不再有益于作为知识库的一部分进行跟踪的对象被标记为已弃用,并且被不同对象替换的对象被撤销。在这两种情况下,旧对象都标有一个字段(或x_mitre_deprecated
或revoked
)来说明它们的状态。在撤销对象的情况下,revoked-by
还会创建针对替换对象的类型关系。
与数据集中的其他对象不同,关系不能被撤销或弃用。如果关系所附加的对象之一被撤销或弃用,则关系被视为已弃用/撤销。
删除已撤销和已弃用的对象
已撤销和不推荐使用的对象保留在知识库中,因此依赖于这些对象的工作流不会被破坏。我们建议您尽可能从您的视图中过滤出已撤销和已弃用的对象,因为 ATT&CK 不再维护它们。
我们建议不要使用内置的 STIX 过滤器来删除已撤销的对象(例如Filter('revoked', '=', False)
)。这是因为此特定过滤器的行为因访问方法(使用本地数据或通过 TAXII 服务器访问)而异。我们建议使用以下代码示例来过滤已撤销的对象。
from stix2 import Filter
def remove_revoked_deprecated(stix_objects):
"""Remove any revoked or deprecated objects from queries made to the data source"""
# Note we use .get() because the property may not be present in the JSON data. The default is False
# if the property is not set.
return list(
filter(
lambda x: x.get("x_mitre_deprecated", False) is False and x.get("revoked", False) is False,
stix_objects
)
)
mitigations = src.query([ Filter("type", "=", "course-of-action") ])
mitigations = remove_revoked_deprecated(mitigations)
获取撤销对象
当一个对象被另一个对象替换时,它会用字段进行标记revoked
并创建类型关系,revoked-by
其中source_ref
是被撤销的对象,而target_ref
是代替被撤销的对象。可以遵循这种关系来找到替换对象:
from stix2 import Filter
def getRevokedBy(stix_id, thesrc):
relations = thesrc.relationships(stix_id, 'revoked-by', source_only=True)
revoked_by = thesrc.query([
Filter('id', 'in', [r.target_ref for r in relations]),
Filter('revoked', '=', False)
])
if revoked_by is not None:
revoked_by = revoked_by[0]
return revoked_by
getRevokedBy("attack-pattern--c16e5409-ee53-4d79-afdc-4099dc9292df", src)