boB Rudis
5 years ago
32 changed files with 912 additions and 126 deletions
@ -1,26 +1,37 @@ |
|||||
Package: attckr |
Package: attckr |
||||
Type: Package |
Type: Package |
||||
Title: attckr title goes here otherwise CRAN checks fail |
Title: Analyze Adversary Tactics and Techniques Using the MITRE ATT&CK CTI Corpus |
||||
Version: 0.1.0 |
Version: 0.1.0 |
||||
Date: 2019-05-28 |
Date: 2019-07-28 |
||||
Authors@R: c( |
Authors@R: c( |
||||
person("Bob", "Rudis", email = "bob@rud.is", role = c("aut", "cre"), |
person("Bob", "Rudis", email = "bob@rud.is", role = c("aut", "cre"), |
||||
comment = c(ORCID = "0000-0001-5670-2640")) |
comment = c(ORCID = "0000-0001-5670-2640")) |
||||
) |
) |
||||
Maintainer: Bob Rudis <bob@rud.is> |
Maintainer: Bob Rudis <bob@rud.is> |
||||
Description: A good description goes here otherwise CRAN checks fail. |
Description: MITRE ATT&CK is a globally-accessible knowledge base of |
||||
|
adversary tactics and techniques based on real-world observations. |
||||
|
The ATT&CK knowledge base is used as a foundation for the development |
||||
|
of specific threat models and methodologies in the private sector, |
||||
|
in government, and in the cybersecurity product and service community. |
||||
|
Tools are provided to analyze adversary tactics and techniques, |
||||
|
build incident metrics, and identify high level program gaps |
||||
|
using the MITRE ATT&CK CTI Corpus. |
||||
URL: https://gitlab.com/hrbrmstr/attckr |
URL: https://gitlab.com/hrbrmstr/attckr |
||||
BugReports: https://gitlab.com/hrbrmstr/attckr/issues |
BugReports: https://gitlab.com/hrbrmstr/attckr/issues |
||||
Encoding: UTF-8 |
Encoding: UTF-8 |
||||
License: AGPL |
License: Apache License 2.0 | file LICENSE |
||||
|
LazyData: true |
||||
Suggests: |
Suggests: |
||||
testthat, |
testthat, |
||||
covr |
covr |
||||
Depends: |
Depends: |
||||
R (>= 3.2.0) |
R (>= 3.2.0) |
||||
Imports: |
Imports: |
||||
httr, |
|
||||
jsonlite, |
jsonlite, |
||||
tibble |
tibble, |
||||
|
ggplot2, |
||||
|
shiny, |
||||
|
stringi, |
||||
|
rmarkdown |
||||
Roxygen: list(markdown = TRUE) |
Roxygen: list(markdown = TRUE) |
||||
RoxygenNote: 6.1.1 |
RoxygenNote: 6.1.1 |
||||
|
@ -0,0 +1,201 @@ |
|||||
|
Apache License |
||||
|
Version 2.0, January 2004 |
||||
|
http://www.apache.org/licenses/ |
||||
|
|
||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION |
||||
|
|
||||
|
1. Definitions. |
||||
|
|
||||
|
"License" shall mean the terms and conditions for use, reproduction, |
||||
|
and distribution as defined by Sections 1 through 9 of this document. |
||||
|
|
||||
|
"Licensor" shall mean the copyright owner or entity authorized by |
||||
|
the copyright owner that is granting the License. |
||||
|
|
||||
|
"Legal Entity" shall mean the union of the acting entity and all |
||||
|
other entities that control, are controlled by, or are under common |
||||
|
control with that entity. For the purposes of this definition, |
||||
|
"control" means (i) the power, direct or indirect, to cause the |
||||
|
direction or management of such entity, whether by contract or |
||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the |
||||
|
outstanding shares, or (iii) beneficial ownership of such entity. |
||||
|
|
||||
|
"You" (or "Your") shall mean an individual or Legal Entity |
||||
|
exercising permissions granted by this License. |
||||
|
|
||||
|
"Source" form shall mean the preferred form for making modifications, |
||||
|
including but not limited to software source code, documentation |
||||
|
source, and configuration files. |
||||
|
|
||||
|
"Object" form shall mean any form resulting from mechanical |
||||
|
transformation or translation of a Source form, including but |
||||
|
not limited to compiled object code, generated documentation, |
||||
|
and conversions to other media types. |
||||
|
|
||||
|
"Work" shall mean the work of authorship, whether in Source or |
||||
|
Object form, made available under the License, as indicated by a |
||||
|
copyright notice that is included in or attached to the work |
||||
|
(an example is provided in the Appendix below). |
||||
|
|
||||
|
"Derivative Works" shall mean any work, whether in Source or Object |
||||
|
form, that is based on (or derived from) the Work and for which the |
||||
|
editorial revisions, annotations, elaborations, or other modifications |
||||
|
represent, as a whole, an original work of authorship. For the purposes |
||||
|
of this License, Derivative Works shall not include works that remain |
||||
|
separable from, or merely link (or bind by name) to the interfaces of, |
||||
|
the Work and Derivative Works thereof. |
||||
|
|
||||
|
"Contribution" shall mean any work of authorship, including |
||||
|
the original version of the Work and any modifications or additions |
||||
|
to that Work or Derivative Works thereof, that is intentionally |
||||
|
submitted to Licensor for inclusion in the Work by the copyright owner |
||||
|
or by an individual or Legal Entity authorized to submit on behalf of |
||||
|
the copyright owner. For the purposes of this definition, "submitted" |
||||
|
means any form of electronic, verbal, or written communication sent |
||||
|
to the Licensor or its representatives, including but not limited to |
||||
|
communication on electronic mailing lists, source code control systems, |
||||
|
and issue tracking systems that are managed by, or on behalf of, the |
||||
|
Licensor for the purpose of discussing and improving the Work, but |
||||
|
excluding communication that is conspicuously marked or otherwise |
||||
|
designated in writing by the copyright owner as "Not a Contribution." |
||||
|
|
||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity |
||||
|
on behalf of whom a Contribution has been received by Licensor and |
||||
|
subsequently incorporated within the Work. |
||||
|
|
||||
|
2. Grant of Copyright License. Subject to the terms and conditions of |
||||
|
this License, each Contributor hereby grants to You a perpetual, |
||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable |
||||
|
copyright license to reproduce, prepare Derivative Works of, |
||||
|
publicly display, publicly perform, sublicense, and distribute the |
||||
|
Work and such Derivative Works in Source or Object form. |
||||
|
|
||||
|
3. Grant of Patent License. Subject to the terms and conditions of |
||||
|
this License, each Contributor hereby grants to You a perpetual, |
||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable |
||||
|
(except as stated in this section) patent license to make, have made, |
||||
|
use, offer to sell, sell, import, and otherwise transfer the Work, |
||||
|
where such license applies only to those patent claims licensable |
||||
|
by such Contributor that are necessarily infringed by their |
||||
|
Contribution(s) alone or by combination of their Contribution(s) |
||||
|
with the Work to which such Contribution(s) was submitted. If You |
||||
|
institute patent litigation against any entity (including a |
||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work |
||||
|
or a Contribution incorporated within the Work constitutes direct |
||||
|
or contributory patent infringement, then any patent licenses |
||||
|
granted to You under this License for that Work shall terminate |
||||
|
as of the date such litigation is filed. |
||||
|
|
||||
|
4. Redistribution. You may reproduce and distribute copies of the |
||||
|
Work or Derivative Works thereof in any medium, with or without |
||||
|
modifications, and in Source or Object form, provided that You |
||||
|
meet the following conditions: |
||||
|
|
||||
|
(a) You must give any other recipients of the Work or |
||||
|
Derivative Works a copy of this License; and |
||||
|
|
||||
|
(b) You must cause any modified files to carry prominent notices |
||||
|
stating that You changed the files; and |
||||
|
|
||||
|
(c) You must retain, in the Source form of any Derivative Works |
||||
|
that You distribute, all copyright, patent, trademark, and |
||||
|
attribution notices from the Source form of the Work, |
||||
|
excluding those notices that do not pertain to any part of |
||||
|
the Derivative Works; and |
||||
|
|
||||
|
(d) If the Work includes a "NOTICE" text file as part of its |
||||
|
distribution, then any Derivative Works that You distribute must |
||||
|
include a readable copy of the attribution notices contained |
||||
|
within such NOTICE file, excluding those notices that do not |
||||
|
pertain to any part of the Derivative Works, in at least one |
||||
|
of the following places: within a NOTICE text file distributed |
||||
|
as part of the Derivative Works; within the Source form or |
||||
|
documentation, if provided along with the Derivative Works; or, |
||||
|
within a display generated by the Derivative Works, if and |
||||
|
wherever such third-party notices normally appear. The contents |
||||
|
of the NOTICE file are for informational purposes only and |
||||
|
do not modify the License. You may add Your own attribution |
||||
|
notices within Derivative Works that You distribute, alongside |
||||
|
or as an addendum to the NOTICE text from the Work, provided |
||||
|
that such additional attribution notices cannot be construed |
||||
|
as modifying the License. |
||||
|
|
||||
|
You may add Your own copyright statement to Your modifications and |
||||
|
may provide additional or different license terms and conditions |
||||
|
for use, reproduction, or distribution of Your modifications, or |
||||
|
for any such Derivative Works as a whole, provided Your use, |
||||
|
reproduction, and distribution of the Work otherwise complies with |
||||
|
the conditions stated in this License. |
||||
|
|
||||
|
5. Submission of Contributions. Unless You explicitly state otherwise, |
||||
|
any Contribution intentionally submitted for inclusion in the Work |
||||
|
by You to the Licensor shall be under the terms and conditions of |
||||
|
this License, without any additional terms or conditions. |
||||
|
Notwithstanding the above, nothing herein shall supersede or modify |
||||
|
the terms of any separate license agreement you may have executed |
||||
|
with Licensor regarding such Contributions. |
||||
|
|
||||
|
6. Trademarks. This License does not grant permission to use the trade |
||||
|
names, trademarks, service marks, or product names of the Licensor, |
||||
|
except as required for reasonable and customary use in describing the |
||||
|
origin of the Work and reproducing the content of the NOTICE file. |
||||
|
|
||||
|
7. Disclaimer of Warranty. Unless required by applicable law or |
||||
|
agreed to in writing, Licensor provides the Work (and each |
||||
|
Contributor provides its Contributions) on an "AS IS" BASIS, |
||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or |
||||
|
implied, including, without limitation, any warranties or conditions |
||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A |
||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the |
||||
|
appropriateness of using or redistributing the Work and assume any |
||||
|
risks associated with Your exercise of permissions under this License. |
||||
|
|
||||
|
8. Limitation of Liability. In no event and under no legal theory, |
||||
|
whether in tort (including negligence), contract, or otherwise, |
||||
|
unless required by applicable law (such as deliberate and grossly |
||||
|
negligent acts) or agreed to in writing, shall any Contributor be |
||||
|
liable to You for damages, including any direct, indirect, special, |
||||
|
incidental, or consequential damages of any character arising as a |
||||
|
result of this License or out of the use or inability to use the |
||||
|
Work (including but not limited to damages for loss of goodwill, |
||||
|
work stoppage, computer failure or malfunction, or any and all |
||||
|
other commercial damages or losses), even if such Contributor |
||||
|
has been advised of the possibility of such damages. |
||||
|
|
||||
|
9. Accepting Warranty or Additional Liability. While redistributing |
||||
|
the Work or Derivative Works thereof, You may choose to offer, |
||||
|
and charge a fee for, acceptance of support, warranty, indemnity, |
||||
|
or other liability obligations and/or rights consistent with this |
||||
|
License. However, in accepting such obligations, You may act only |
||||
|
on Your own behalf and on Your sole responsibility, not on behalf |
||||
|
of any other Contributor, and only if You agree to indemnify, |
||||
|
defend, and hold each Contributor harmless for any liability |
||||
|
incurred by, or claims asserted against, such Contributor by reason |
||||
|
of your accepting any such warranty or additional liability. |
||||
|
|
||||
|
END OF TERMS AND CONDITIONS |
||||
|
|
||||
|
APPENDIX: How to apply the Apache License to your work. |
||||
|
|
||||
|
To apply the Apache License to your work, attach the following |
||||
|
boilerplate notice, with the fields enclosed by brackets "[]" |
||||
|
replaced with your own identifying information. (Don't include |
||||
|
the brackets!) The text should be enclosed in the appropriate |
||||
|
comment syntax for the file format. We also recommend that a |
||||
|
file or class name and description of purpose be included on the |
||||
|
same "printed page" as the copyright notice for easier |
||||
|
identification within third-party archives. |
||||
|
|
||||
|
Copyright [yyyy] [name of copyright owner] |
||||
|
|
||||
|
Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
you may not use this file except in compliance with the License. |
||||
|
You may obtain a copy of the License at |
||||
|
|
||||
|
http://www.apache.org/licenses/LICENSE-2.0 |
||||
|
|
||||
|
Unless required by applicable law or agreed to in writing, software |
||||
|
distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
See the License for the specific language governing permissions and |
||||
|
limitations under the License. |
@ -1,5 +1,12 @@ |
|||||
# Generated by roxygen2: do not edit by hand |
# Generated by roxygen2: do not edit by hand |
||||
|
|
||||
import(httr) |
export(fct_tactic) |
||||
|
export(validate_tactics) |
||||
|
export(validate_technique_ids) |
||||
|
export(validate_techniques) |
||||
|
import(ggplot2) |
||||
|
import(rmarkdown) |
||||
|
import(shiny) |
||||
|
import(stringi) |
||||
import(tibble) |
import(tibble) |
||||
importFrom(jsonlite,fromJSON) |
importFrom(jsonlite,fromJSON) |
||||
|
@ -1,13 +1,18 @@ |
|||||
#' ... |
#' Analyze Adversary Tactics and Techniques Using the MITRE ATT&CK CTI Corpus |
||||
#' |
#' |
||||
#' - URL: <https://gitlab.com/hrbrmstr/attckr> |
#' MITRE ATT&CK™ is a globally-accessible knowledge base of |
||||
#' - BugReports: <https://gitlab.com/hrbrmstr/attckr/issues> |
#' adversary tactics and techniques based on real-world observations. |
||||
|
#' The ATT&CK knowledge base is used as a foundation for the development |
||||
|
#' of specific threat models and methodologies in the private sector, |
||||
|
#' in government, and in the cybersecurity product and service community. |
||||
|
#' Tools are provided to analyze adversary tactics and techniques, |
||||
|
#' build incident metrics, and identify high level program gaps |
||||
|
#' using the MITRE ATT&CK CTI Corpus. |
||||
#' |
#' |
||||
#' @md |
#' @md |
||||
#' @name attckr |
#' @name attckr |
||||
#' @docType package |
#' @docType package |
||||
#' @author Bob Rudis (bob@@rud.is) |
#' @author Bob Rudis (bob@@rud.is) |
||||
#' @import httr |
#' @import tibble ggplot2 shiny rmarkdown stringi |
||||
#' @import tibble |
|
||||
#' @importFrom jsonlite fromJSON |
#' @importFrom jsonlite fromJSON |
||||
NULL |
"_PACKAGE" |
@ -0,0 +1,19 @@ |
|||||
|
#' @title Enterprise Attack Taxonomy |
||||
|
#' @name enterprise_attack |
||||
|
#' @docType data |
||||
|
NULL |
||||
|
|
||||
|
#' @title Mobile Attack Taxonomy |
||||
|
#' @name mobile_attack |
||||
|
#' @docType data |
||||
|
NULL |
||||
|
|
||||
|
#' @title Pre-Attack Taxonomy |
||||
|
#' @name pre_attack |
||||
|
#' @docType data |
||||
|
NULL |
||||
|
|
||||
|
#' @title Combined ATT&CK Matricies Tactics, Techniques and Technique detail |
||||
|
#' @name tidy_attack |
||||
|
#' @docType data |
||||
|
NULL |
@ -0,0 +1,52 @@ |
|||||
|
.tactics_f <- list() |
||||
|
|
||||
|
.tactics_f[["mitre-attack"]] <- list( |
||||
|
id = c( |
||||
|
"initial-access", "execution", "persistence", "privilege-escalation", |
||||
|
"defense-evasion", "credential-access", "discovery", "lateral-movement", |
||||
|
"collection", "command-and-control", "exfiltration", "impact" |
||||
|
), |
||||
|
pretty = c( |
||||
|
"Initial Access", "Execution", "Persistence", "Privilege Escalation", |
||||
|
"Defense Evasion", "Credential Access", "Discovery", "Lateral Movement", |
||||
|
"Collection", "Command And Control", "Exfiltration", "Impact" |
||||
|
), |
||||
|
nl = c( |
||||
|
"Initial\nAccess", "Execution", "Persistence", "Privilege\nEscalation", |
||||
|
"Defense\nEvasion", "Credential\nAccess", "Discovery", "Lateral\nMovement", |
||||
|
"Collection", "Command\nAnd\nControl", "Exfiltration", "Impact" |
||||
|
) |
||||
|
) |
||||
|
|
||||
|
#' Make an ordered Tactics factor with optional better labelling |
||||
|
#' |
||||
|
#' |
||||
|
#' @param tactics a character vector |
||||
|
#' @param input what is in `tactics`? |
||||
|
#' @param output what do you want the factor label to be? |
||||
|
#' @param matrix which matrix? |
||||
|
#' @export |
||||
|
fct_tactic <- function(tactics, |
||||
|
input = c("id", "pretty", "nl"), |
||||
|
output = c("pretty", "nl", "id"), |
||||
|
matrix = c("enterprise", "mobile", "pre")) { |
||||
|
|
||||
|
input <- match.arg(input[1], c("id", "pretty", "nl")) |
||||
|
output <- match.arg(output[1], c("id", "pretty", "nl")) |
||||
|
matrix <- match.arg(matrix[1], c("enterprise", "mobile", "pre")) |
||||
|
|
||||
|
switch( |
||||
|
matrix, |
||||
|
enterprise = "mitre-attack", |
||||
|
mobile = "mitre-mobile-attack", |
||||
|
pre = "mitre-pre-attack" |
||||
|
) -> tax |
||||
|
|
||||
|
input <- .tactics_f[[tax]][[input]] |
||||
|
output <- .tactics_f[[tax]][[output]] |
||||
|
|
||||
|
factor(x = tactics, levels = input, labels = output, ordered = TRUE) |
||||
|
|
||||
|
} |
||||
|
|
||||
|
|
@ -0,0 +1,6 @@ |
|||||
|
#' Read in ATT&CK events from a file |
||||
|
read_events <- function(path) { |
||||
|
|
||||
|
} |
||||
|
|
||||
|
|
@ -0,0 +1 @@ |
|||||
|
normalize_identifier <- function(x) { gsub("[[:space:]]+", "-", trimws(tolower(x))) } |
@ -0,0 +1,56 @@ |
|||||
|
#' Validate Tactics strings against MITRE authoritative source |
||||
|
#' |
||||
|
#' @param tactics a character vector of tactic strings to validate. This will be |
||||
|
#' converted to lower-case, left/right spaces will be trimmed and |
||||
|
#' internal spaces will be converted to a single `-` |
||||
|
#' @param matrix which matrix to use when validating? |
||||
|
#' @param na_rm remove NA's before comparing? |
||||
|
#' @return `TRUE` if all tactics validate, otherwise `FALSE` with messages |
||||
|
#' identifying the invalid tactics. |
||||
|
#' @export |
||||
|
#' @examples |
||||
|
#' validate_tactics("persistence") |
||||
|
#' validate_tactics(c("persistence", "Persistence", "Persistance")) |
||||
|
validate_tactics <- function(tactics, matrix = c("enterprise", "mobile", "pre"), |
||||
|
na_rm = TRUE) { |
||||
|
|
||||
|
matrix <- match.arg(matrix[1], c("enterprise", "mobile", "pre")) |
||||
|
|
||||
|
switch( |
||||
|
matrix, |
||||
|
enterprise = "mitre-attack", |
||||
|
mobile = "mitre-mobile-attack", |
||||
|
pre = "mitre-pre-attack" |
||||
|
) -> tax |
||||
|
|
||||
|
tax <- unique(tidy_attack[tidy_attack$matrix == tax, "tactic", drop=TRUE]) |
||||
|
|
||||
|
if (na_rm) { |
||||
|
no_na <- na.exclude(tactics) |
||||
|
where_nas <- attr(no_na, "na.action", exact = TRUE) |
||||
|
if (length(where_nas)) message("Removed ", length(where_nas), " NA values.\n") |
||||
|
tac <- as.character(no_na) |
||||
|
} |
||||
|
|
||||
|
o_tac <- tactics |
||||
|
|
||||
|
tac <- normalize_identifier(tactics) |
||||
|
|
||||
|
bad <- o_tac[which(!(tac %in% tax))] |
||||
|
|
||||
|
if (length(bad)) { |
||||
|
warning( |
||||
|
"Tactics not in the ", matrix, " MITRE ATT&CK matrix found\n", |
||||
|
paste0(sprintf('- "%s"', sort(unique(bad))), collapse = "\n"), |
||||
|
call. = FALSE |
||||
|
) |
||||
|
invisible(sort(unique(bad))) |
||||
|
} else { |
||||
|
message( |
||||
|
"All tactics were found in the ", matrix, " MITRE ATT&CK matrix" |
||||
|
) |
||||
|
invisible(TRUE) |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
|
@ -0,0 +1,47 @@ |
|||||
|
#' Validate Technique IDs |
||||
|
#' |
||||
|
#' @param technique_ids a character vector of technique ids to validate |
||||
|
#' @param matrix which matrix to validate against (not all IDs are associated |
||||
|
#' with techniques in every matrix) |
||||
|
#' @param na_rm remove NA's before comparing? |
||||
|
#' @export |
||||
|
validate_technique_ids <- function(technique_ids, |
||||
|
matrix = c("enterprise", "mobile", "pre"), |
||||
|
na_rm = TRUE) { |
||||
|
|
||||
|
matrix <- match.arg(matrix[1], c("enterprise", "mobile", "pre")) |
||||
|
|
||||
|
switch( |
||||
|
matrix, |
||||
|
enterprise = "mitre-attack", |
||||
|
mobile = "mitre-mobile-attack", |
||||
|
pre = "mitre-pre-attack" |
||||
|
) -> tax |
||||
|
|
||||
|
if (na_rm) { |
||||
|
no_na <- na.exclude(technique_ids) |
||||
|
where_nas <- attr(no_na, "na.action", exact = TRUE) |
||||
|
if (length(where_nas)) message("Removed ", length(where_nas), " NA values.\n") |
||||
|
tec <- as.character(no_na) |
||||
|
} |
||||
|
|
||||
|
mtec <- unique(tidy_attack[tidy_attack$matrix == tax, "id", drop=TRUE]) |
||||
|
|
||||
|
bad <- unique(tec[which(!(tec %in% mtec))]) |
||||
|
|
||||
|
if (length(bad)) { |
||||
|
warning( |
||||
|
"Technique ids not in the ", matrix, " MITRE ATT&CK matrix found\n", |
||||
|
paste0(sprintf('- "%s"', sort(unique(bad))), collapse = "\n"), |
||||
|
call. = FALSE |
||||
|
) |
||||
|
invisible(sort(unique(bad))) |
||||
|
} else { |
||||
|
message( |
||||
|
"All techniques were found in the ", matrix, " MITRE ATT&CK matrix" |
||||
|
) |
||||
|
invisible(TRUE) |
||||
|
} |
||||
|
|
||||
|
|
||||
|
} |
@ -0,0 +1,83 @@ |
|||||
|
#' Validate Techniques strings against MITRE authoritative source |
||||
|
#' |
||||
|
#' @param tactics a character vector of tactic strings to validate. |
||||
|
#' @param matrix which matrix to use when validating? |
||||
|
#' @param ignore_case if `TRUE` case will not be taken into account when |
||||
|
#' comparing strings. Default is `FALSE`. |
||||
|
#' @param na_rm remove NA's before comparing? |
||||
|
#' @return `TRUE` if all tactics validate, otherwise `FALSE` with messages |
||||
|
#' identifying the invalid tactics. |
||||
|
#' @export |
||||
|
#' @examples |
||||
|
#' validate_techniques("persistence") |
||||
|
#' validate_techniques(c("persistence", "Persistence", "Persistance")) |
||||
|
validate_techniques <- function(techniques, matrix = c("enterprise", "mobile", "pre"), |
||||
|
ignore_case = FALSE, na_rm = TRUE) { |
||||
|
|
||||
|
matrix <- match.arg(matrix[1], c("enterprise", "mobile", "pre")) |
||||
|
|
||||
|
switch( |
||||
|
matrix, |
||||
|
enterprise = "mitre-attack", |
||||
|
mobile = "mitre-mobile-attack", |
||||
|
pre = "mitre-pre-attack" |
||||
|
) -> tax |
||||
|
|
||||
|
if (na_rm) { |
||||
|
no_na <- na.exclude(techniques) |
||||
|
where_nas <- attr(no_na, "na.action", exact = TRUE) |
||||
|
if (length(where_nas)) message("Removed ", length(where_nas), " NA values.\n") |
||||
|
tec <- as.character(no_na) |
||||
|
} |
||||
|
|
||||
|
mtec <- unique(tidy_attack[tidy_attack$matrix == tax, "technique", drop=TRUE]) |
||||
|
o_tec <- tec |
||||
|
|
||||
|
if (ignore_case) { |
||||
|
o_tec <- tolower(tec) |
||||
|
mtec <- tolower(mtec) |
||||
|
} |
||||
|
|
||||
|
bad <- unique(o_tec[which(!(tec %in% mtec))]) |
||||
|
|
||||
|
if (length(bad)) { |
||||
|
|
||||
|
bad <- sort(bad) |
||||
|
suggest <- sapply(bad, agrep, x = mtec, ignore.case = ignore_case) |
||||
|
suggest <- ifelse(lengths(suggest) == 0, "No suggestions found", suggest) |
||||
|
|
||||
|
do.call( |
||||
|
rbind.data.frame, |
||||
|
lapply(names(suggest), function(x) { |
||||
|
data.frame( |
||||
|
input = x, |
||||
|
alts = as.character(suggest[[x]]), |
||||
|
stringsAsFactors = FALSE |
||||
|
) %>% as_tibble() |
||||
|
}) |
||||
|
) -> suggest |
||||
|
|
||||
|
can_suggest <- which(suggest[["alts"]] != "No suggestions found") |
||||
|
|
||||
|
suggest[can_suggest, "alts"] <- sprintf('Perhaps: "%s"', |
||||
|
mtec[as.integer(suggest[can_suggest, "alts", drop=TRUE])]) |
||||
|
|
||||
|
warning( |
||||
|
"Techniques not in the ", matrix, " MITRE ATT&CK matrix found:\n", |
||||
|
paste0(sprintf('- "%s (%s)"', suggest$input, suggest$alts), collapse = "\n"), |
||||
|
call. = FALSE |
||||
|
) |
||||
|
|
||||
|
invisible( |
||||
|
tibble( |
||||
|
input = suggest$input, |
||||
|
alts = suggest$alts |
||||
|
) |
||||
|
) |
||||
|
|
||||
|
} else { |
||||
|
message("All techniques were found in the ", matrix, " MITRE ATT&CK Framework.") |
||||
|
invisible(TRUE) |
||||
|
} |
||||
|
|
||||
|
} |
@ -1,2 +1,85 @@ |
|||||
|
|
||||
|
[![Project Status: Active – The project has reached a stable, usable |
||||
|
state and is being actively |
||||
|
developed.](https://www.repostatus.org/badges/latest/active.svg)](https://www.repostatus.org/#active) |
||||
|
[![Signed |
||||
|
by](https://img.shields.io/badge/Keybase-Verified-brightgreen.svg)](https://keybase.io/hrbrmstr) |
||||
|
![Signed commit |
||||
|
%](https://img.shields.io/badge/Signed_Commits-66.7%25-lightgrey.svg) |
||||
|
[![Linux build |
||||
|
Status](https://travis-ci.org/hrbrmstr/attckr.svg?branch=master)](https://travis-ci.org/hrbrmstr/attckr) |
||||
|
![Minimal R |
||||
|
Version](https://img.shields.io/badge/R%3E%3D-3.2.0-blue.svg) |
||||
|
![License](https://img.shields.io/badge/License-Apache-blue.svg) |
||||
|
|
||||
# attckr |
# attckr |
||||
|
|
||||
|
Analyze Adversary Tactics and Techniques Using the MITRE ATT\&CK CTI |
||||
|
Corpus |
||||
|
|
||||
|
## Description |
||||
|
|
||||
|
MITRE ATT\&CK is a globally-accessible knowledge base of adversary |
||||
|
tactics and techniques based on real-world observations. The ATT\&CK |
||||
|
knowledge base is used as a foundation for the development of specific |
||||
|
threat models and methodologies in the private sector, in government, |
||||
|
and in the cybersecurity product and service community. Tools are |
||||
|
provided to analyze adversary tactics and techniques, build incident |
||||
|
metrics, and identify high level program gaps using the MITRE ATT\&CK |
||||
|
CTI Corpus. |
||||
|
|
||||
|
## What’s Inside The Tin |
||||
|
|
||||
|
The following functions are implemented: |
||||
|
|
||||
|
- `enterprise_attack`: Enterprise Attack Taxonomy |
||||
|
- `fct_tactic`: Make an ordered Tactics factor with optional better |
||||
|
labelling |
||||
|
- `mobile_attack`: Mobile Attack Taxonomy |
||||
|
- `pre_attack`: Pre-Attack Taxonomy |
||||
|
- `read_events`: Read in ATT\&CK events from a file |
||||
|
- `tidy_attack`: Combined ATT\&CK Matricies Tactics, Techniques and |
||||
|
Technique detail |
||||
|
- `validate_tactics`: Validate Tactics strings against MITRE |
||||
|
authoritative source |
||||
|
- `validate_technique_ids`: Validate Technique IDs |
||||
|
- `validate_techniques`: Validate Techniques strings against MITRE |
||||
|
authoritative source |
||||
|
|
||||
|
## Installation |
||||
|
|
||||
|
``` r |
||||
|
remotes::install_git("https://git.rud.is/hrbrmstr/attckr.git") |
||||
|
# or |
||||
|
remotes::install_git("https://git.sr.ht/~hrbrmstr/attckr") |
||||
|
# or |
||||
|
remotes::install_gitlab("hrbrmstr/attckr") |
||||
|
# or |
||||
|
remotes::install_bitbucket("hrbrmstr/attckr") |
||||
|
``` |
||||
|
|
||||
|
NOTE: To use the ‘remotes’ install options you will need to have the |
||||
|
[{remotes} package](https://github.com/r-lib/remotes) installed. |
||||
|
|
||||
|
## Usage |
||||
|
|
||||
|
``` r |
||||
|
library(attckr) |
||||
|
|
||||
|
# current version |
||||
|
packageVersion("attckr") |
||||
|
## [1] '0.1.0' |
||||
|
``` |
||||
|
|
||||
|
## attckr Metrics |
||||
|
|
||||
|
| Lang | \# Files | (%) | LoC | (%) | Blank lines | (%) | \# Lines | (%) | |
||||
|
| :--- | -------: | ---: | --: | ---: | ----------: | ---: | -------: | ---: | |
||||
|
| R | 10 | 0.91 | 168 | 0.95 | 50 | 0.77 | 72 | 0.74 | |
||||
|
| Rmd | 1 | 0.09 | 8 | 0.05 | 15 | 0.23 | 25 | 0.26 | |
||||
|
|
||||
|
## Code of Conduct |
||||
|
|
||||
|
Please note that this project is released with a Contributor Code of |
||||
|
Conduct. By participating in this project you agree to abide by its |
||||
|
terms. |
||||
|
Binary file not shown.
@ -1,94 +0,0 @@ |
|||||
## code to prepare `enterprise_attack` dataset goes here |
|
||||
library(tidyverse) |
|
||||
|
|
||||
unlink( |
|
||||
sprintf("%s.xz", here::here( |
|
||||
"data-raw", |
|
||||
c( |
|
||||
"enterprise-attack.json", |
|
||||
"mobile-attack.json", |
|
||||
"pre-attack.json" |
|
||||
) |
|
||||
)) |
|
||||
) |
|
||||
|
|
||||
download.file( |
|
||||
url = c( |
|
||||
"https://github.com/mitre/cti/raw/master/enterprise-attack/enterprise-attack.json", |
|
||||
"https://github.com/mitre/cti/raw/master/mobile-attack/mobile-attack.json", |
|
||||
"https://github.com/mitre/cti/raw/master/pre-attack/pre-attack.json" |
|
||||
), |
|
||||
destfile = c( |
|
||||
here::here( |
|
||||
"data-raw", |
|
||||
c( |
|
||||
"enterprise-attack.json", |
|
||||
"mobile-attack.json", |
|
||||
"pre-attack.json" |
|
||||
) |
|
||||
) |
|
||||
), |
|
||||
method = "libcurl" |
|
||||
) |
|
||||
|
|
||||
walk( |
|
||||
here::here( |
|
||||
"data-raw", |
|
||||
c( |
|
||||
"enterprise-attack.json", |
|
||||
"mobile-attack.json", |
|
||||
"pre-attack.json" |
|
||||
) |
|
||||
), |
|
||||
~system2("xz", args = .x) |
|
||||
) |
|
||||
|
|
||||
jsonlite::fromJSON( |
|
||||
here::here("data-raw/enterprise-attack.json.xz") |
|
||||
) -> enterprise_attack |
|
||||
|
|
||||
enterprise_attack[["objects"]] <- tibble::as_tibble(enterprise_attack[["objects"]]) |
|
||||
|
|
||||
usethis::use_data(enterprise_attack, overwrite = TRUE, compress = "xz") |
|
||||
|
|
||||
jsonlite::fromJSON( |
|
||||
here::here("data-raw/mobile-attack.json.xz") |
|
||||
) -> mobile_attack |
|
||||
|
|
||||
mobile_attack[["objects"]] <- tibble::as_tibble(mobile_attack[["objects"]]) |
|
||||
|
|
||||
usethis::use_data(mobile_attack, overwrite = TRUE, compress = "xz") |
|
||||
|
|
||||
jsonlite::fromJSON( |
|
||||
here::here("data-raw/pre-attack.json.xz") |
|
||||
) -> pre_attack |
|
||||
|
|
||||
pre_attack[["objects"]] <- tibble::as_tibble(pre_attack[["objects"]]) |
|
||||
|
|
||||
usethis::use_data(pre_attack, overwrite = TRUE, compress = "xz") |
|
||||
|
|
||||
bind_rows( |
|
||||
tibble( |
|
||||
technique = enterprise_attack$objects$name, |
|
||||
description = enterprise_attack$objects$x_mitre_detection, |
|
||||
tactic = enterprise_attack$objects$kill_chain_phases |
|
||||
) %>% |
|
||||
filter(lengths(tactic) > 0) %>% |
|
||||
unnest(), |
|
||||
tibble( |
|
||||
technique = mobile_attack$objects$name, |
|
||||
description = mobile_attack$objects$x_mitre_detection, |
|
||||
tactic = mobile_attack$objects$kill_chain_phases |
|
||||
) %>% |
|
||||
filter(lengths(tactic) > 0) %>% |
|
||||
unnest(), |
|
||||
tibble( |
|
||||
technique = pre_attack$objects$name, |
|
||||
description = pre_attack$objects$description, |
|
||||
tactic = pre_attack$objects$kill_chain_phases |
|
||||
) %>% |
|
||||
filter(lengths(tactic) > 0) %>% |
|
||||
unnest() |
|
||||
) -> tidy_attack |
|
||||
|
|
||||
usethis::use_data(tidy_attack, overwrite = TRUE, compress = "xz") |
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1,9 @@ |
|||||
|
% Generated by roxygen2: do not edit by hand |
||||
|
% Please edit documentation in R/data-docs.R |
||||
|
\docType{data} |
||||
|
\name{enterprise_attack} |
||||
|
\alias{enterprise_attack} |
||||
|
\title{Enterprise Attack Taxonomy} |
||||
|
\description{ |
||||
|
Enterprise Attack Taxonomy |
||||
|
} |
@ -0,0 +1,22 @@ |
|||||
|
% Generated by roxygen2: do not edit by hand |
||||
|
% Please edit documentation in R/fct-tactic.R |
||||
|
\name{fct_tactic} |
||||
|
\alias{fct_tactic} |
||||
|
\title{Make an ordered Tactics factor with optional better labelling} |
||||
|
\usage{ |
||||
|
fct_tactic(tactics, input = c("id", "pretty", "nl"), |
||||
|
output = c("pretty", "nl", "id"), matrix = c("enterprise", "mobile", |
||||
|
"pre")) |
||||
|
} |
||||
|
\arguments{ |
||||
|
\item{tactics}{a character vector} |
||||
|
|
||||
|
\item{input}{what is in \code{tactics}?} |
||||
|
|
||||
|
\item{output}{what do you want the factor label to be?} |
||||
|
|
||||
|
\item{matrix}{which matrix?} |
||||
|
} |
||||
|
\description{ |
||||
|
Make an ordered Tactics factor with optional better labelling |
||||
|
} |
@ -0,0 +1,9 @@ |
|||||
|
% Generated by roxygen2: do not edit by hand |
||||
|
% Please edit documentation in R/data-docs.R |
||||
|
\docType{data} |
||||
|
\name{mobile_attack} |
||||
|
\alias{mobile_attack} |
||||
|
\title{Mobile Attack Taxonomy} |
||||
|
\description{ |
||||
|
Mobile Attack Taxonomy |
||||
|
} |
@ -0,0 +1,9 @@ |
|||||
|
% Generated by roxygen2: do not edit by hand |
||||
|
% Please edit documentation in R/data-docs.R |
||||
|
\docType{data} |
||||
|
\name{pre_attack} |
||||
|
\alias{pre_attack} |
||||
|
\title{Pre-Attack Taxonomy} |
||||
|
\description{ |
||||
|
Pre-Attack Taxonomy |
||||
|
} |
@ -0,0 +1,11 @@ |
|||||
|
% Generated by roxygen2: do not edit by hand |
||||
|
% Please edit documentation in R/read-events.R |
||||
|
\name{read_events} |
||||
|
\alias{read_events} |
||||
|
\title{Read in ATT&CK events from a file} |
||||
|
\usage{ |
||||
|
read_events(path) |
||||
|
} |
||||
|
\description{ |
||||
|
Read in ATT&CK events from a file |
||||
|
} |
@ -0,0 +1,9 @@ |
|||||
|
% Generated by roxygen2: do not edit by hand |
||||
|
% Please edit documentation in R/data-docs.R |
||||
|
\docType{data} |
||||
|
\name{tidy_attack} |
||||
|
\alias{tidy_attack} |
||||
|
\title{Combined ATT&CK Matricies Tactics, Techniques and Technique detail} |
||||
|
\description{ |
||||
|
Combined ATT&CK Matricies Tactics, Techniques and Technique detail |
||||
|
} |
@ -0,0 +1,29 @@ |
|||||
|
% Generated by roxygen2: do not edit by hand |
||||
|
% Please edit documentation in R/validate-tactics.R |
||||
|
\name{validate_tactics} |
||||
|
\alias{validate_tactics} |
||||
|
\title{Validate Tactics strings against MITRE authoritative source} |
||||
|
\usage{ |
||||
|
validate_tactics(tactics, matrix = c("enterprise", "mobile", "pre"), |
||||
|
na_rm = TRUE) |
||||
|
} |
||||
|
\arguments{ |
||||
|
\item{tactics}{a character vector of tactic strings to validate. This will be |
||||
|
converted to lower-case, left/right spaces will be trimmed and |
||||
|
internal spaces will be converted to a single \code{-}} |
||||
|
|
||||
|
\item{matrix}{which matrix to use when validating?} |
||||
|
|
||||
|
\item{na_rm}{remove NA's before comparing?} |
||||
|
} |
||||
|
\value{ |
||||
|
\code{TRUE} if all tactics validate, otherwise \code{FALSE} with messages |
||||
|
identifying the invalid tactics. |
||||
|
} |
||||
|
\description{ |
||||
|
Validate Tactics strings against MITRE authoritative source |
||||
|
} |
||||
|
\examples{ |
||||
|
validate_tactics("persistence") |
||||
|
validate_tactics(c("persistence", "Persistence", "Persistance")) |
||||
|
} |
@ -0,0 +1,20 @@ |
|||||
|
% Generated by roxygen2: do not edit by hand |
||||
|
% Please edit documentation in R/validate-tech-ids.R |
||||
|
\name{validate_technique_ids} |
||||
|
\alias{validate_technique_ids} |
||||
|
\title{Validate Technique IDs} |
||||
|
\usage{ |
||||
|
validate_technique_ids(technique_ids, matrix = c("enterprise", "mobile", |
||||
|
"pre"), na_rm = TRUE) |
||||
|
} |
||||
|
\arguments{ |
||||
|
\item{technique_ids}{a character vector of technique ids to validate} |
||||
|
|
||||
|
\item{matrix}{which matrix to validate against (not all IDs are associated |
||||
|
with techniques in every matrix)} |
||||
|
|
||||
|
\item{na_rm}{remove NA's before comparing?} |
||||
|
} |
||||
|
\description{ |
||||
|
Validate Technique IDs |
||||
|
} |
@ -0,0 +1,30 @@ |
|||||
|
% Generated by roxygen2: do not edit by hand |
||||
|
% Please edit documentation in R/validate-techniques.R |
||||
|
\name{validate_techniques} |
||||
|
\alias{validate_techniques} |
||||
|
\title{Validate Techniques strings against MITRE authoritative source} |
||||
|
\usage{ |
||||
|
validate_techniques(techniques, matrix = c("enterprise", "mobile", |
||||
|
"pre"), ignore_case = FALSE, na_rm = TRUE) |
||||
|
} |
||||
|
\arguments{ |
||||
|
\item{matrix}{which matrix to use when validating?} |
||||
|
|
||||
|
\item{ignore_case}{if \code{TRUE} case will not be taken into account when |
||||
|
comparing strings. Default is \code{FALSE}.} |
||||
|
|
||||
|
\item{na_rm}{remove NA's before comparing?} |
||||
|
|
||||
|
\item{tactics}{a character vector of tactic strings to validate.} |
||||
|
} |
||||
|
\value{ |
||||
|
\code{TRUE} if all tactics validate, otherwise \code{FALSE} with messages |
||||
|
identifying the invalid tactics. |
||||
|
} |
||||
|
\description{ |
||||
|
Validate Techniques strings against MITRE authoritative source |
||||
|
} |
||||
|
\examples{ |
||||
|
validate_techniques("persistence") |
||||
|
validate_techniques(c("persistence", "Persistence", "Persistance")) |
||||
|
} |
@ -0,0 +1,21 @@ |
|||||
|
filter(tidy_attack, matrix == "mitre-attack") %>% |
||||
|
distinct(tactic, technique) %>% |
||||
|
mutate(tactic = fct_tactic("id", "nl")) %>% |
||||
|
group_by(tactic) %>% |
||||
|
mutate(ypos = 1:n()) %>% |
||||
|
ggplot(aes(tactic, ypos)) + |
||||
|
geom_tile(color = "#b2b2b2", fill = "white") + |
||||
|
geom_text(aes(label = technique), size = 2) + |
||||
|
scale_x_discrete(position = "top") + |
||||
|
scale_y_reverse(expand = c(0,0)) + |
||||
|
labs( |
||||
|
x = NULL, y = NULL |
||||
|
) + |
||||
|
theme_minimal() + |
||||
|
theme(panel.grid = element_blank()) + |
||||
|
theme(axis.text.y = element_blank()) |
||||
|
|
||||
|
|
||||
|
|
||||
|
filter(tidy_attack, matrix == "mitre-attack") %>% |
||||
|
distinct(tactic, technique) |
@ -0,0 +1,129 @@ |
|||||
|
## code to prepare `enterprise_attack` dataset goes here |
||||
|
|
||||
|
library(tidyverse) |
||||
|
|
||||
|
unlink( |
||||
|
sprintf("%s.xz", here::here( |
||||
|
"data-raw", |
||||
|
c( |
||||
|
"enterprise-attack.json", |
||||
|
"mobile-attack.json", |
||||
|
"pre-attack.json" |
||||
|
) |
||||
|
)) |
||||
|
) |
||||
|
|
||||
|
download.file( |
||||
|
url = c( |
||||
|
"https://github.com/mitre/cti/raw/master/enterprise-attack/enterprise-attack.json", |
||||
|
"https://github.com/mitre/cti/raw/master/mobile-attack/mobile-attack.json", |
||||
|
"https://github.com/mitre/cti/raw/master/pre-attack/pre-attack.json" |
||||
|
), |
||||
|
destfile = c( |
||||
|
here::here( |
||||
|
"data-raw", |
||||
|
c( |
||||
|
"enterprise-attack.json", |
||||
|
"mobile-attack.json", |
||||
|
"pre-attack.json" |
||||
|
) |
||||
|
) |
||||
|
), |
||||
|
method = "libcurl" |
||||
|
) |
||||
|
|
||||
|
walk( |
||||
|
here::here( |
||||
|
"data-raw", |
||||
|
c( |
||||
|
"enterprise-attack.json", |
||||
|
"mobile-attack.json", |
||||
|
"pre-attack.json" |
||||
|
) |
||||
|
), |
||||
|
~system2("xz", args = .x) |
||||
|
) |
||||
|
|
||||
|
jsonlite::fromJSON( |
||||
|
here::here("data-raw/enterprise-attack.json.xz") |
||||
|
) -> enterprise_attack |
||||
|
|
||||
|
enterprise_attack[["objects"]] <- tibble::as_tibble(enterprise_attack[["objects"]]) |
||||
|
|
||||
|
jsonlite::fromJSON( |
||||
|
here::here("data-raw/mobile-attack.json.xz") |
||||
|
) -> mobile_attack |
||||
|
|
||||
|
mobile_attack[["objects"]] <- tibble::as_tibble(mobile_attack[["objects"]]) |
||||
|
|
||||
|
jsonlite::fromJSON( |
||||
|
here::here("data-raw/pre-attack.json.xz") |
||||
|
) -> pre_attack |
||||
|
|
||||
|
pre_attack[["objects"]] <- tibble::as_tibble(pre_attack[["objects"]]) |
||||
|
|
||||
|
bind_rows( |
||||
|
map_df(1:nrow(enterprise_attack$objects), ~{ |
||||
|
if (is.na(enterprise_attack$objects$name[[.x]])) return(NULL) |
||||
|
if (is.null(enterprise_attack$objects$kill_chain_phases[[.x]])) return(NULL) |
||||
|
tibble( |
||||
|
technique = enterprise_attack$objects$name[[.x]], |
||||
|
description = enterprise_attack$objects$description[.x], |
||||
|
id = discard(enterprise_attack$objects$external_references[[.x]]$external_id, is.na) %||% NA_character_, |
||||
|
phs = enterprise_attack$objects$kill_chain_phases[.x] |
||||
|
) %>% |
||||
|
unnest() |
||||
|
}), |
||||
|
map_df(1:nrow(mobile_attack$objects), ~{ |
||||
|
if (is.na(mobile_attack$objects$name[[.x]])) return(NULL) |
||||
|
if (is.null(mobile_attack$objects$kill_chain_phases[[.x]])) return(NULL) |
||||
|
tibble( |
||||
|
technique = mobile_attack$objects$name[[.x]], |
||||
|
description = mobile_attack$objects$description[.x], |
||||
|
id = discard(mobile_attack$objects$external_references[[.x]]$external_id, is.na) %||% NA_character_, |
||||
|
phs = mobile_attack$objects$kill_chain_phases[.x] |
||||
|
) %>% |
||||
|
unnest() |
||||
|
}), |
||||
|
map_df(1:nrow(pre_attack$objects), ~{ |
||||
|
if (is.na(pre_attack$objects$name[[.x]])) return(NULL) |
||||
|
if (is.null(pre_attack$objects$kill_chain_phases[[.x]])) return(NULL) |
||||
|
tibble( |
||||
|
technique = pre_attack$objects$name[[.x]], |
||||
|
description = pre_attack$objects$description[.x], |
||||
|
id = discard(pre_attack$objects$external_references[[.x]]$external_id, is.na) %||% NA_character_, |
||||
|
phs = pre_attack$objects$kill_chain_phases[.x] |
||||
|
) %>% |
||||
|
unnest() |
||||
|
}) |
||||
|
) %>% |
||||
|
rename(tactic = phase_name, matrix = kill_chain_name) %>% |
||||
|
distinct() -> tidy_attack |
||||
|
|
||||
|
usethis::use_data( |
||||
|
enterprise_attack, mobile_attack, pre_attack, tidy_attack, |
||||
|
internal = FALSE, |
||||
|
overwrite = TRUE, |
||||
|
compress = "xz" |
||||
|
) |
||||
|
|
||||
|
bind_rows(enterprise_attack$objects$kill_chain_phases) %>% |
||||
|
distinct(phase_name) |
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
Loading…
Reference in new issue