├── LICENSE ├── README.md ├── cicat ├── .gitkeep ├── cicat2 │ ├── .gitkeep │ └── settings.py ├── data │ ├── .gitkeep │ ├── 800-53 │ │ └── controls.2.xlsx │ ├── ATK │ │ ├── .gitkeep │ │ └── attack.json │ ├── ATK4ICS.xlsx │ ├── ATKPROFILES.xlsx │ ├── CVE │ │ └── CVE-year-2019.xml │ ├── TB_infrastructure.xlsx │ └── TB_scenarios.xlsx ├── example │ ├── CCATrun.bat │ ├── CICAT brief.ap1000.pptx │ ├── INFRASTRUCTURE.ap1000.xlsx │ ├── RESULTS.ap1000.xlsx │ └── SCENARIOS.ap1000.xlsx ├── generator │ ├── .gitkeep │ ├── SSoutput.py │ ├── TACSequence.py │ ├── TTPFilter.py │ ├── actorGEN.py │ ├── actorlists.py │ ├── afactory.py │ ├── amodel.py │ ├── atk2xl.py │ ├── atk4ics.py │ ├── cicat.py │ ├── columnloader.py │ ├── dbLoad.py │ ├── fastcache.py │ ├── ffactory.py │ ├── ifactory.py │ ├── imodel.py │ ├── loaddata.py │ ├── mitGEN.py │ ├── scenGEN.py │ ├── spreadout.py │ ├── stats.py │ ├── tfactory.py │ ├── tmodel.py │ ├── topology.py │ ├── vfactory.py │ └── zoneCrawlr.py ├── requirements └── utests │ ├── .gitkeep │ └── utests.bat ├── docker └── Dockerfile ├── docker_info.txt └── shared └── placeholder.txt /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2020 The MITRE Corporation. All rights reserved. 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Critical Infrastructure Cyberspace Analysis Tool (CICAT) 2 | CICAT is a modeling and simulation tool for evaluating how an adversary might conduct a cyber attack. CICAT uses a threat model that leverages open source cyber threat data provided by MITRE Enterprise ATT&CK™ and ATT&CK for ICS. MITRE developed CICAT to automate production of cyber attack scenarios in support of International Atomic Energy Agency (IAEA) Coordinated Research Program (CRP) J02008: "Enhancing Computer Security Incident Analysis at Nuclear Facilities", which seeks to improve capabilities to prevent, detect, and respond to cyber security incidents at nuclear facilities. 3 | # Version 4 | CICAT v1.1 5 | # Copyright 2020 The MITRE Corporation. All Rights Reserved. 6 | Approved for public release; Distribution unlimited. PRS Case 20-2400. 7 | # Licensing 8 | Apache 2.0 9 | # Documentation 10 | A whitepaper discussing CICAT is available at https://www.mitre.org/publications/technical-papers/critical-infrastructure-cyberspace-analysis-tool-cicat-capability. 11 | # Platform requirements 12 | Python 3.7 or better; pip 13 | # Installation 14 | 1. Unzip the distribution into a local directory. 15 | 2. cd to the cicat folder. 16 | 3. pip install -r requirements. 17 | 4. cd to the example folder. 18 | 5. run CCATrun.bat. (If CICAT is installed correctly, this will generate scenarios for the AP1000 model and create a Results spreadsheet.) 19 | # Scenario Generation 20 | This CICAT distribution includes the program scenGEN.py, which is used to generate cyber attack scenarios. This program imports an infrastructure model and scenario specification as Excel spreadsheets, and exports generated scnerarios data as an Excel spreadsdheet. 21 | 22 | To run scenGEN.py: 23 | python scenGEN.py -i \ -s \ -o \ 24 | 25 | Examples of infrastucture and scenario specification spreadsheets can be found in the data subdirectory and are called TB_infrastructure.xlsx and TB_scnearios.xlsx, respectively. These spreadsheets represent an infrastructure testbed developed for testing purposes. These testbed files are imported by default if scenGEN.py is invoked without -i or -s input parameters. 26 | 27 | The -o parameter is used to specify a filename for the output spreadsheet. If no -o parameter is supplied, the default filename 'Results' is used and saved in the data subdirectory. The output filename is always appended with a timestamp to prevent CICAT from overwriting previous scenario results. 28 | # Worked Example 29 | The example subfolder contains an example assessment of AP1000 pressurized water reactor (PWR) safety systems using CICAT scenario generation. This worked example is approved for public release, PRS case 20-1395. 30 | 31 | The infrastructure spreadsheet includes model details for roughly 12 AP1000 safety systems and 80 components. Note that this model contains fictitious component and network details, and was developed for demonstration purposes. 32 | 33 | The scneario specification includes 4 scenarios, each targeting a different safety system from internal plant locations and through an external entry point. Each scenario uses the same threat actor (IS01) applying a different tactic pattern. A tactic pattern specifies the sequence of ATT&CK tactics the threat actor uses over the course of an cyber attack. The spreadsheet provides details on threat actor capabilities and tactic patterns supported in CICAT. 34 | 35 | Note that IS01 is a fictional threat actor and the tactic patterns do not reflect known adversary behaviors. The IS01 threat actor profile includes both Enterprise ATT&CK(tm) and ATT&CK for ICS techniques. It was developed to demonstrate generation of cyberattack scenarios in a blended IT and OT environment. 36 | 37 | The CCATrun.bat script (Windows) invokes the scenGEN.py program to generate scnearios that are output to a RESULTS.ap1000.xlsx spreadsheet. 38 | 39 | The CICAT brief.ap1000.pptx powerpoint outlines the assessment scope, objectives, results, and lessons learned. 40 | -------------------------------------------------------------------------------- /cicat/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitre/CICAT/501e87047e884f61133e1dd165593b4f1c803815/cicat/.gitkeep -------------------------------------------------------------------------------- /cicat/cicat2/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitre/CICAT/501e87047e884f61133e1dd165593b4f1c803815/cicat/cicat2/.gitkeep -------------------------------------------------------------------------------- /cicat/cicat2/settings.py: -------------------------------------------------------------------------------- 1 | """ 2 | Django settings for cicat2 project. 3 | 4 | Generated by 'django-admin startproject' using Django 2.2.2. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/2.2/topics/settings/ 8 | 9 | For the full list of settings and their values, see 10 | https://docs.djangoproject.com/en/2.2/ref/settings/ 11 | """ 12 | 13 | import os 14 | 15 | # Build paths inside the project like this: os.path.join(BASE_DIR, ...) 16 | BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 17 | 18 | 19 | # Quick-start development settings - unsuitable for production 20 | # See https://docs.djangoproject.com/en/2.2/howto/deployment/checklist/ 21 | 22 | # SECURITY WARNING: keep the secret key used in production secret! 23 | SECRET_KEY = '' 24 | 25 | # SECURITY WARNING: don't run with debug turned on in production! 26 | DEBUG = False 27 | 28 | ALLOWED_HOSTS = ['*'] 29 | 30 | 31 | # Application definition 32 | 33 | INSTALLED_APPS = [ 34 | 'cicat2.apps.Cicat2Config', 35 | 'django.contrib.admin', 36 | 'django.contrib.auth', 37 | 'django.contrib.contenttypes', 38 | 'django.contrib.sessions', 39 | 'django.contrib.messages', 40 | 'django.contrib.staticfiles', 41 | ] 42 | 43 | MIDDLEWARE = [ 44 | 'django.middleware.security.SecurityMiddleware', 45 | 'django.contrib.sessions.middleware.SessionMiddleware', 46 | 'django.middleware.common.CommonMiddleware', 47 | 'django.middleware.csrf.CsrfViewMiddleware', 48 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 49 | 'django.contrib.messages.middleware.MessageMiddleware', 50 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', 51 | ] 52 | 53 | ROOT_URLCONF = 'cicat2.urls' 54 | 55 | TEMPLATES = [ 56 | { 57 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', 58 | 'DIRS': [], 59 | 'APP_DIRS': True, 60 | 'OPTIONS': { 61 | 'context_processors': [ 62 | 'django.template.context_processors.debug', 63 | 'django.template.context_processors.request', 64 | 'django.contrib.auth.context_processors.auth', 65 | 'django.contrib.messages.context_processors.messages', 66 | ], 67 | }, 68 | }, 69 | ] 70 | 71 | WSGI_APPLICATION = 'cicat2.wsgi.application' 72 | 73 | 74 | # Database 75 | # https://docs.djangoproject.com/en/2.2/ref/settings/#databases 76 | 77 | DATABASES = { 78 | #'default': { 79 | # 'ENGINE': 'django.db.backends.sqlite3', 80 | # 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), 81 | #} 82 | 'default': { 83 | 'ENGINE': 'django.db.backends.mysql', 84 | 'NAME': 'db_name', 85 | 'USER': 'username', 86 | 'PASSWORD': 'password', 87 | 'HOST': 'localhost', 88 | 'PORT': '', 89 | } 90 | } 91 | 92 | 93 | # Password validation 94 | # https://docs.djangoproject.com/en/2.2/ref/settings/#auth-password-validators 95 | 96 | AUTH_PASSWORD_VALIDATORS = [ 97 | { 98 | 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', 99 | }, 100 | { 101 | 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', 102 | }, 103 | { 104 | 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', 105 | }, 106 | { 107 | 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', 108 | }, 109 | ] 110 | 111 | 112 | # Internationalization 113 | # https://docs.djangoproject.com/en/2.2/topics/i18n/ 114 | 115 | LANGUAGE_CODE = 'en-us' 116 | 117 | TIME_ZONE = 'UTC' 118 | 119 | USE_I18N = True 120 | 121 | USE_L10N = True 122 | 123 | USE_TZ = True 124 | 125 | 126 | # Static files (CSS, JavaScript, Images) 127 | # https://docs.djangoproject.com/en/2.2/howto/static-files/ 128 | 129 | STATIC_URL = '/static/' 130 | 131 | try: 132 | from local_settings import * 133 | except ImportError: 134 | print("can't import local_settings") 135 | pass -------------------------------------------------------------------------------- /cicat/data/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitre/CICAT/501e87047e884f61133e1dd165593b4f1c803815/cicat/data/.gitkeep -------------------------------------------------------------------------------- /cicat/data/800-53/controls.2.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitre/CICAT/501e87047e884f61133e1dd165593b4f1c803815/cicat/data/800-53/controls.2.xlsx -------------------------------------------------------------------------------- /cicat/data/ATK/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitre/CICAT/501e87047e884f61133e1dd165593b4f1c803815/cicat/data/ATK/.gitkeep -------------------------------------------------------------------------------- /cicat/data/ATK4ICS.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitre/CICAT/501e87047e884f61133e1dd165593b4f1c803815/cicat/data/ATK4ICS.xlsx -------------------------------------------------------------------------------- /cicat/data/ATKPROFILES.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitre/CICAT/501e87047e884f61133e1dd165593b4f1c803815/cicat/data/ATKPROFILES.xlsx -------------------------------------------------------------------------------- /cicat/data/TB_infrastructure.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitre/CICAT/501e87047e884f61133e1dd165593b4f1c803815/cicat/data/TB_infrastructure.xlsx -------------------------------------------------------------------------------- /cicat/data/TB_scenarios.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitre/CICAT/501e87047e884f61133e1dd165593b4f1c803815/cicat/data/TB_scenarios.xlsx -------------------------------------------------------------------------------- /cicat/example/CCATrun.bat: -------------------------------------------------------------------------------- 1 | python ..\generator\scenGEN.py -r -i INFRASTRUCTURE.ap1000.xlsx -s SCENARIOS.ap1000.xlsx -o RESULTS.ap1000.xlsx 2 | -------------------------------------------------------------------------------- /cicat/example/CICAT brief.ap1000.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitre/CICAT/501e87047e884f61133e1dd165593b4f1c803815/cicat/example/CICAT brief.ap1000.pptx -------------------------------------------------------------------------------- /cicat/example/INFRASTRUCTURE.ap1000.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitre/CICAT/501e87047e884f61133e1dd165593b4f1c803815/cicat/example/INFRASTRUCTURE.ap1000.xlsx -------------------------------------------------------------------------------- /cicat/example/RESULTS.ap1000.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitre/CICAT/501e87047e884f61133e1dd165593b4f1c803815/cicat/example/RESULTS.ap1000.xlsx -------------------------------------------------------------------------------- /cicat/example/SCENARIOS.ap1000.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitre/CICAT/501e87047e884f61133e1dd165593b4f1c803815/cicat/example/SCENARIOS.ap1000.xlsx -------------------------------------------------------------------------------- /cicat/generator/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitre/CICAT/501e87047e884f61133e1dd165593b4f1c803815/cicat/generator/.gitkeep -------------------------------------------------------------------------------- /cicat/generator/SSoutput.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | :::::::::::::::::::::::: Critical Infrastructure Cyberspace Analysis Tool (CICAT) ::::::::::::::::::::::::::::::::::::::: 4 | 5 | NOTICE 6 | 7 | The contents of this material reflect the views of the author and/or the Director of the Center for Advanced Aviation 8 | System Development (CAASD), and do not necessarily reflect the views of the Federal Aviation Administration (FAA) 9 | or the Department of Transportation (DOT). Neither the FAA nor the DOT makes any warranty or guarantee, or promise, 10 | expressed or implied, concerning the content or accuracy of the views expressed herein. 11 | 12 | This is the copyright work of The MITRE Corporation and was produced for the U.S. Government under Contract Number 13 | DTFAWA-10-C-00080 and is subject to Federal Aviation Administration Acquisition Management System Clause 3.5-13, 14 | Rights in Data-General, Alt. III and Alt. IV (Oct. 1996). No other use other than that granted to the U.S. Government, 15 | or to those acting on behalf of the U.S. Government, under that Clause is authorized without the express written permission 16 | of The MITRE Corporation. For further information, please contact The MITRE Corporation, Contract Office, 7515 Colshire Drive, 17 | McLean, VA 22102 (703) 983-6000. ©2020 The MITRE Corporation. 18 | 19 | The Government retains a nonexclusive, royalty-free right to publish or reproduce this document, or to allow others to do so, for 20 | “Government Purposes Only.” 21 | 22 | (c) 2020 The MITRE Corporation. All Rights Reserved. 23 | 24 | :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: 25 | SSoutput.py - Routines to generate scenario spreadsheet output 26 | :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: 27 | """ 28 | 29 | from openpyxl import Workbook 30 | 31 | m_taginfo = {'Scenario' : [1, 1], 32 | 'Scenario Desc' : [2, 1], 33 | 'Entry Point' : [3,1 ], 34 | 'Threat Actor' : [5, 1], 35 | 'Sophistication' : [6, 1], 36 | 'Tactics Pattern' : [7, 1], 37 | 'Target' : [9, 1], 38 | 'Impact score [0...160]' : [10, 1], 39 | '1st order effects' : [11, 1], 40 | '2nd order effects' : [12, 1], 41 | '3rd order effects' : [13, 1], 42 | 'Attack Path Component' : [15, 1], 43 | 'Description' : [16, 1], 44 | 'Platform' : [17, 1], 45 | 'System' : [18, 1], 46 | 'Techniques' : [20, 1], 47 | 'Mitigations' : [30, 1], 48 | 'Forensics' : [40, 1], 49 | 'Vendor Product Vulnerabilities' : [46, 1] } 50 | 51 | def list2string(plist, separator): 52 | ret = 'undefined' 53 | if plist: 54 | ret = str(plist[0]) 55 | for j in range (1, len(plist)): 56 | ret = ret + separator + str(plist[j]) 57 | return ret 58 | 59 | def getTTPlist (tracedata): 60 | ttplist = [] 61 | for k in tracedata.keys(): 62 | curdict = tracedata[k] 63 | curtrace = curdict[0] 64 | tlist = curtrace['ttps'] 65 | for x in tlist: 66 | if len(x) > 0: 67 | for xx in x: 68 | ttplist.append(xx) 69 | 70 | tset = set (ttplist) # dump list into set to elimnate duplicates 71 | ret = list (tset) # dump back into list 72 | 73 | return ret 74 | 75 | def getMITsforTTP (dataset, ttpID ): 76 | for t in dataset['ATT&CK']: 77 | ret = [] 78 | if t.getTECHID() == ttpID: 79 | clist = t.getCOA() 80 | for c in clist: 81 | ret.append(c[0].getTECHID() ) 82 | return ret 83 | 84 | 85 | def getMITsforTTPList (dataset, ttplist): 86 | clist = [] 87 | for x in ttplist: 88 | clist = clist + getMITsforTTP(dataset, x) 89 | 90 | uset = set (clist) 91 | ret = list (uset) 92 | 93 | return ret 94 | 95 | def getFromToList(path): 96 | ret = [] 97 | idx = 0 98 | while idx < len(path)-1 : 99 | ret.append([path[idx], path[idx+1]]) 100 | idx = idx + 1 101 | 102 | return ret 103 | 104 | def writeField (sheet, tag, value=None, rowval=-1): 105 | row, col = m_taginfo[tag] 106 | if rowval > 0: 107 | row = rowval 108 | sheet.cell (row, col, tag+':') 109 | if value: 110 | sheet.cell (row, col+1, value) 111 | 112 | def writeBlockData (sheet, row, col, listdata ): 113 | idx = 0 114 | for k in listdata: 115 | sheet.cell (row+idx, col, k) 116 | idx = idx + 1 117 | return idx 118 | 119 | def writeRowData (sheet, tag, pathinfo): 120 | row, col = m_taginfo[tag] 121 | sheet.cell (row, col, tag+':') 122 | for c in pathinfo: 123 | col = col + 1 124 | sheet.cell (row, col, c) 125 | 126 | def getComponentInfo(dataset, cname): 127 | ret = [] 128 | for c in dataset['COMPONENT']: 129 | if c.getName() == cname: 130 | cdet = c.getVendor() + ' ' + str(c.getDesc()) 131 | ret.append (cdet) 132 | ret.append (c.getPlatform() ) 133 | ret.append (c.getSysName() ) 134 | return ret 135 | 136 | 137 | def updateEffects (dataset, sheet, cname): 138 | for c in dataset['COMPONENT']: 139 | if c.getName() == cname: 140 | writeField (sheet, '1st order effects', c.getSystemAffected() ) 141 | writeField (sheet, '2nd order effects', list2string (c.getFunctionsAffected(), ', ') ) 142 | writeField (sheet, '3rd order effects', list2string (c.getCapabilitiesAffected(), ', ') ) 143 | 144 | def getActorSophistication (dataset, actorName): 145 | for a in dataset['ATKGROUPS']: 146 | if a.getName() == actorName: 147 | return a.getSophisticationLevel() 148 | 149 | def writeComponentDetails (dataset, sheet, atkpath): 150 | cdesc = [] 151 | cplat = [] 152 | sname = [] 153 | 154 | for x in atkpath: 155 | cinfo = getComponentInfo (dataset, x) 156 | cdesc.append (cinfo[0]) 157 | cplat.append (cinfo[1]) 158 | sname.append (cinfo[2]) 159 | 160 | writeRowData (sheet, 'Description', cdesc) 161 | writeRowData (sheet, 'Platform', cplat ) 162 | writeRowData (sheet, 'System', sname) 163 | 164 | 165 | def getTechniqueInfo (dataset, techID): 166 | ret = [] 167 | for t in dataset['ATT&CK']: 168 | if t.getTECHID() == techID: 169 | ret.append(t.getTECHID() + ' : ' + t.getName() ) 170 | ret.append (list2string (t.getTactic(), ', ')) 171 | ret.append (t.getURL() ) 172 | return ret 173 | 174 | for i in dataset['ATK4ICS TTPs']: 175 | if i.getTECHID() == techID: 176 | ret.append (i.getTECHID() + ' : ' + i.getName() ) 177 | ret.append (list2string(i.getTactic(), ', ')) 178 | ret.append (i.getURL() ) 179 | return ret 180 | 181 | 182 | def getMitigationInfo (dataset, techID): 183 | ret = [] 184 | for t in dataset['ATT&CK']: 185 | if t.getTECHID() == techID: 186 | mlist = t.getCOA() 187 | for m in mlist: 188 | mit = m[0] 189 | if (mit.getTECHID() == techID): # ATT&CK portal gives 404 errors for old mitigations... Omit from list 190 | continue 191 | ret.append (mit.getTECHID() + ' : ' + mit.getName() ) 192 | ret.append (mit.getURL() ) 193 | ret.append (" ") 194 | return ret 195 | 196 | for t in dataset['ATK4ICS TTPs']: 197 | if t.getTECHID() == techID: 198 | mlist = t.getCOA() 199 | for m in mlist: 200 | mit = m[0] 201 | ret.append (mit.getTECHID() + ' : ' + mit.getName() ) 202 | ret.append (mit.getURL() ) 203 | ret.append (" ") 204 | return ret 205 | 206 | def getForensicInfo (dataset, techID): 207 | 208 | ret = [] 209 | for t in dataset['ATT&CK']: 210 | if t.getTECHID() == techID: 211 | ret.append(t.getDET() ) 212 | return ret 213 | 214 | for t in dataset['ATK4ICS TTPs']: 215 | if t.getTECHID() == techID: 216 | ret.append (t.getDET() ) 217 | return ret 218 | 219 | 220 | def writeTTPDetails (dataset, sheet, path, ttplist): 221 | 222 | writeField (sheet, 'Techniques') 223 | idx = 0 224 | maxrow = 0 225 | for p in path: 226 | tlist = ttplist[idx] 227 | tdetail = [] 228 | for t in tlist: 229 | tinfo = getTechniqueInfo (dataset, t) 230 | if tinfo: 231 | tdetail.append(tinfo) 232 | 233 | row, col = m_taginfo['Techniques'] 234 | for d in tdetail: 235 | row = row + writeBlockData (sheet, row, idx+2, d ) + 1 236 | if row > maxrow: 237 | maxrow = row 238 | idx = idx + 1 239 | 240 | return maxrow 241 | 242 | 243 | def writeMITDetails (dataset, sheet, startrow, path, ttplist): 244 | 245 | writeField (sheet, 'Mitigations', rowval=startrow) 246 | 247 | idx = 0 248 | maxrow = 0 249 | for p in path: 250 | tlist = ttplist[idx] 251 | tdetail = [] 252 | for t in tlist: 253 | tinfo = getMitigationInfo (dataset, t) 254 | if tinfo: 255 | tdetail.append(tinfo) 256 | 257 | row, col = m_taginfo['Mitigations'] 258 | row = startrow 259 | for d in tdetail: 260 | row = row + writeBlockData (sheet, row, idx+2, d ) + 1 261 | if row > maxrow: 262 | maxrow = row 263 | idx = idx + 1 264 | 265 | return maxrow 266 | 267 | def writeForensicInfo (dataset, sheet, startrow, path, ttplist): 268 | 269 | writeField (sheet, 'Forensics', rowval=startrow) 270 | 271 | idx = 0 272 | maxrow = 0 273 | for p in path: 274 | tlist = ttplist[idx] 275 | tdetail = [] 276 | for t in tlist: 277 | tinfo = getForensicInfo (dataset, t) 278 | if tinfo: 279 | tdetail.append(tinfo) 280 | 281 | row, col = m_taginfo['Mitigations'] 282 | row = startrow 283 | for d in tdetail: 284 | row = row + writeBlockData (sheet, row, idx+2, d ) + 1 285 | if row > maxrow: 286 | maxrow = row 287 | idx = idx + 1 288 | 289 | return maxrow 290 | 291 | def getCVEInfo (vlist, maxcount): 292 | count = 0 293 | ret = [] 294 | for v in vlist: 295 | ret.append('https://nvd.nist.gov/vuln/detail/'+ v.getCVE()) 296 | count = count + 1 297 | if count > maxcount: 298 | break 299 | 300 | return ret 301 | 302 | def writeCVEInfo (dataset, sheet, startrow, maxcount, path ): 303 | 304 | writeField (sheet, 'Vendor Product Vulnerabilities', rowval=startrow) 305 | 306 | idx = 1 307 | for x in path: 308 | idx = idx + 1 309 | vlist = [] 310 | for c in dataset['COMPONENT']: 311 | if c.getName() == x: 312 | vlist = c.getVulnerabilityList() 313 | if vlist: 314 | olist = getCVEInfo (vlist, maxcount) 315 | row, col = m_taginfo['Vendor Product Vulnerabilities'] 316 | row = startrow 317 | writeBlockData (sheet, row, idx, olist) 318 | break 319 | 320 | 321 | def DumpScenarioDetail (wb, key, tracedata, dataset): 322 | sheet = wb.create_sheet (title=key) 323 | scendata = tracedata[key] 324 | scendatax = scendata[0] 325 | 326 | name = scendatax['name'] 327 | sname = name.split('EP') 328 | writeField (sheet, 'Scenario', sname[0]) 329 | 330 | for s in dataset['SCENARIO']: 331 | if s.getID() == sname[0]: 332 | writeField (sheet, 'Scenario Desc', s.getDesc() ) 333 | 334 | writeField (sheet, 'Entry Point', sname[1]) 335 | 336 | path = scendatax['path'] 337 | writeRowData (sheet, 'Attack Path Component', path) 338 | 339 | writeComponentDetails (dataset, sheet, path) 340 | 341 | actor = scendatax['actor'] 342 | writeField (sheet, 'Threat Actor', actor) 343 | writeField (sheet, 'Sophistication', getActorSophistication (dataset, actor) ) 344 | 345 | pattern = scendatax['effect'] 346 | writeField (sheet, 'Tactics Pattern', pattern) 347 | 348 | target = scendatax['target'] 349 | writeField (sheet, 'Target', target) 350 | 351 | score = scendatax['score'] 352 | writeField (sheet, 'Impact score [0...160]', score) 353 | 354 | updateEffects (dataset, sheet, target) 355 | 356 | ttplist = scendatax['ttps'] 357 | maxrow = writeTTPDetails (dataset, sheet, path, ttplist) 358 | maxrow = writeMITDetails (dataset, sheet, maxrow, path, ttplist) 359 | maxrow = writeForensicInfo (dataset, sheet, maxrow, path, ttplist) 360 | writeCVEInfo (dataset, sheet, maxrow, 10, path ) 361 | 362 | 363 | def DumpScenario (fname, tracedata, dataset ): 364 | wb = Workbook() 365 | for s in tracedata.keys(): 366 | DumpScenarioDetail (wb, s, tracedata, dataset) 367 | wb.save(filename = fname ) 368 | 369 | 370 | 371 | 372 | 373 | -------------------------------------------------------------------------------- /cicat/generator/TACSequence.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | :::::::::::::::::::::::: Critical Infrastructure Cyberspace Analysis Tool (CICAT) ::::::::::::::::::::::::::::::::::::::: 4 | 5 | NOTICE 6 | 7 | The contents of this material reflect the views of the author and/or the Director of the Center for Advanced Aviation 8 | System Development (CAASD), and do not necessarily reflect the views of the Federal Aviation Administration (FAA) 9 | or the Department of Transportation (DOT). Neither the FAA nor the DOT makes any warranty or guarantee, or promise, 10 | expressed or implied, concerning the content or accuracy of the views expressed herein. 11 | 12 | This is the copyright work of The MITRE Corporation and was produced for the U.S. Government under Contract Number 13 | DTFAWA-10-C-00080 and is subject to Federal Aviation Administration Acquisition Management System Clause 3.5-13, 14 | Rights in Data-General, Alt. III and Alt. IV (Oct. 1996). No other use other than that granted to the U.S. Government, 15 | or to those acting on behalf of the U.S. Government, under that Clause is authorized without the express written permission 16 | of The MITRE Corporation. For further information, please contact The MITRE Corporation, Contract Office, 7515 Colshire Drive, 17 | McLean, VA 22102 (703) 983-6000. ©2020 The MITRE Corporation. 18 | 19 | The Government retains a nonexclusive, royalty-free right to publish or reproduce this document, or to allow others to do so, for 20 | “Government Purposes Only.” 21 | 22 | (c) 2020 The MITRE Corporation. All Rights Reserved. 23 | 24 | :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: 25 | TACSequence.py - Routines to implement tactic patterns and generate TTP sequences 26 | :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: 27 | """ 28 | 29 | import sys 30 | from random import sample 31 | from collections import defaultdict 32 | 33 | from ffactory import FILTER_FACTORY, INIT_FILTERS 34 | from loaddata import LOAD_DATA 35 | from loaddata import m_file_TESTBED_MODEL, m_file_TESTBED_SCNRO 36 | from loaddata import m_IT_test_list, m_ICS_test_list 37 | from topology import INIT_TOPOLOGY 38 | from TTPFilter import TTP_FILTER, m_TACTIC_LIST 39 | 40 | 41 | # Note: ICS patterns do support privilege escalation[3], credential-access[5], and exfiltration[10] tactics 42 | # Note: Non-ICS patterns do not support inhibit-response-function[13] and impair-process-control[14] tactics 43 | # Note: Current ATT&CK dataset provides no attribution for impact tactic or ICS-SCADA (scenearios must use threat actor profiles to use) 44 | 45 | 46 | # objseqs reference ATT&CK tactics using m_tactList indexes 47 | m_objseqs = [['foothold', 'first', [0, 7] ], 48 | ['justgo', 'any', [7] ], 49 | ['recceandgo', 'any', [6, 7] ], 50 | ['collectandgo', 'any', [6, 8, 7] ], 51 | ['coverandgo', 'any', [4, 7] ], 52 | ['credsandgo', 'any', [3, 5, 7 ]], 53 | ['phonehomeandgo', 'any', [9, 7] ], 54 | ['backdoor', 'last', [6, 3, 2] ], 55 | ['hammer_1', 'last', [6, 3, 11] ], #11 - impact 56 | ['hammer_2', 'last', [6, 11] ], 57 | ['hammer_3', 'last', [6, 12] ], #12 - inhibit-response-function 58 | ['hammer_4', 'last', [6, 13] ], #13 - impair-process-contro 59 | ['exfil', 'last', [8, 10] ] ] 60 | 61 | # dictionary of tactic sequences 62 | m_objdict = defaultdict (list) 63 | m_tactDict = defaultdict (list) 64 | m_patternMenu = defaultdict (list) 65 | 66 | # initialize data structure and pattern menu 67 | 68 | def initDS(): 69 | for p in m_objseqs: 70 | m_objdict[p[0]].append([p[1], p[2]] ) 71 | 72 | 73 | def initPatternMenu (): 74 | if m_patternMenu.keys(): 75 | return m_patternMenu 76 | 77 | initDS() 78 | 79 | m_patternMenu ['Saguaro'] = ['foothold', 'justgo', 'hammer_1'] 80 | m_patternMenu ['Barrel'] = ['foothold', 'recceandgo', 'hammer_1' ] 81 | m_patternMenu ['Star'] = ['foothold', 'credsandgo', 'exfil'] 82 | m_patternMenu ['Feather'] = ['foothold', 'collectandgo', 'exfil'] 83 | m_patternMenu ['Old Lady'] = ['recceandgo', 'recceandgo', 'coverandgo', 'backdoor'] 84 | m_patternMenu ['Bunny Ear'] = ['justgo', 'recceandgo', 'coverandgo', 'hammer_1'] 85 | m_patternMenu ['Blue Columnar'] = ['justgo', 'justgo', 'hammer_1'] 86 | m_patternMenu ['Moon'] = ['collectandgo', 'collectandgo', 'exfil'] 87 | m_patternMenu ['Easter'] = ['justgo', 'justgo', 'backdoor'] 88 | m_patternMenu ['Ladyfinger'] = ['justgo', 'justgo', 'hammer_1'] 89 | m_patternMenu ['Parodia'] = ['justgo', 'justgo', 'hammer_2'] 90 | m_patternMenu ['Bishops Cap'] = ['justgo', 'justgo', 'hammer_3'] 91 | m_patternMenu ['Fairy Castle'] = ['foothold', 'justgo', 'hammer_4'] 92 | 93 | return m_patternMenu 94 | 95 | 96 | # assundry utility functions 97 | 98 | def gettactics (entry): 99 | ret = [] 100 | for t in entry: 101 | ret.append(m_TACTIC_LIST[t]) 102 | return ret 103 | 104 | def gettactlist (obj): 105 | entry = m_objdict[obj] 106 | if not (entry): 107 | print ('Warning! gettactlist has no entry for', obj) 108 | return 109 | return gettactics ((entry [0])[1] ) 110 | 111 | def isFirst (entry, listx): 112 | if listx[0] == entry: 113 | return True 114 | return False 115 | 116 | def isLast (entry, listx): 117 | if listx[len(listx)-1] == entry: 118 | return True 119 | return False 120 | 121 | def inMiddle (entry, listx): 122 | if not(isFirst (entry, listx)) and not (isLast(entry, listx)): 123 | return True 124 | return False 125 | 126 | def randomMiddle(listx): 127 | ret = sample (listx[1:len(listx)-1], 1) 128 | return ret[0] 129 | 130 | # returns sequence of attack patterns assigned each host from objective sequence 131 | def genObjectiveSequence ( pathlist, attackpat ): 132 | 133 | ret = [] 134 | for host in pathlist: 135 | if isFirst(host, pathlist): 136 | ret.append (attackpat[0] ) 137 | elif isLast(host, pathlist): 138 | ret.append (attackpat[len(attackpat) - 1]) 139 | else: 140 | ret.append (randomMiddle(attackpat)) 141 | 142 | return ret 143 | 144 | 145 | # returns tactic sequence for hosts in attack path applying specified pattern 146 | def GenTacticPattern (path, pattern, trace ): 147 | 148 | tactSeq = [] 149 | 150 | objseq = genObjectiveSequence (path, pattern) 151 | for o in objseq: 152 | tactSeq.append (gettactlist(o)) 153 | 154 | mixlist = [] 155 | 156 | indx = 0 157 | for t in tactSeq: 158 | mixlist.append ([path[indx], t]) 159 | indx = indx + 1 160 | 161 | if trace: 162 | print (mixlist) 163 | 164 | return mixlist 165 | 166 | def getComponentbyName(dataset, cname): 167 | for c in dataset['COMPONENT']: 168 | if c.getName() == cname: 169 | return c 170 | return None 171 | 172 | def getActorbyName(dataset, aname): 173 | for c in dataset['ATKGROUPS']: 174 | if c.getGroupID() == aname: 175 | return c 176 | return None 177 | 178 | def GenTTPSequence (dataset, factory, patSeq, aName, actFlag, trace): 179 | 180 | ret = [] 181 | 182 | actor = getActorbyName (dataset, aName) 183 | for p in patSeq: 184 | 185 | foo = [] 186 | 187 | cmpName = p[0] 188 | tacpat = p[1] 189 | 190 | component = getComponentbyName (dataset, cmpName) 191 | if not (component): 192 | if trace: 193 | print ('WARNING! GenTTPSequence cannot find host', cmpName ) 194 | return 195 | 196 | foo.append(cmpName) 197 | 198 | platflag = True 199 | if component.getSurfaceList(): 200 | platflag = False 201 | 202 | for t in tacpat: 203 | foo.append (TTP_FILTER (dataset, factory, cmpName, t, platflag, actor, actFlag, trace) ) 204 | 205 | ret.append (foo) 206 | 207 | return ret 208 | 209 | # returns the objective sequence for the name pattern 210 | def GetPatternbyName(name): 211 | patternDict = initPatternMenu() 212 | return patternDict[name] 213 | 214 | 215 | # Helper function for reading options from command line 216 | def optionReader(params, flag): 217 | idx = params.index(flag) 218 | if len(params) > idx + 1 and '-' not in params[idx + 1]: 219 | return params[idx + 1] 220 | else: 221 | print(flag + ' flag must include an option!') 222 | exit() 223 | 224 | # main entry point 225 | if ( __name__ == "__main__"): 226 | 227 | Ispread = m_file_TESTBED_MODEL 228 | Tspread = m_file_TESTBED_SCNRO 229 | 230 | params = sys.argv 231 | if len(params) > 1: 232 | if 'help' in params[1].lower(): 233 | print ('\nUSAGE: python', params[0], '[-i ] [-s ]') 234 | exit() 235 | 236 | if '-i' in params: 237 | Ispread = optionReader(params, '-i') 238 | 239 | if '-s' in params: 240 | Tspread = optionReader(params, '-s') 241 | 242 | 243 | myDATASET = LOAD_DATA (Ispread, Tspread, False, False ) 244 | zonemap = INIT_TOPOLOGY(myDATASET, True ) 245 | 246 | ffactory = FILTER_FACTORY(False ) 247 | INIT_FILTERS (ffactory, myDATASET) 248 | 249 | initPatternMenu() 250 | 251 | print ('\n') 252 | print ('>> Objective to Tactic mappings test <<') 253 | 254 | # Evaluate each tactic pattern. If the pattern includes hammer_3 or hammer_4 then use the ICS threat actor and component sequence, 255 | # otherwise use the IT threat actor and component list 256 | 257 | for k in m_patternMenu.keys(): 258 | 259 | if 'hammer_3' in m_patternMenu[k] or 'hammer_4' in m_patternMenu[k]: 260 | testpath = m_ICS_test_list 261 | actor = 'IS01' 262 | else: 263 | testpath = m_IT_test_list 264 | actor = 'APT28' 265 | 266 | print ('\n') 267 | print ('Testing pattern:', k, 'using actor:', actor, 'and component sequence:', testpath ) 268 | 269 | 270 | patseq = GenTacticPattern(testpath, GetPatternbyName (k), False) 271 | print (patseq) 272 | 273 | print ('TTP Sequence(', actor,'):', GenTTPSequence (myDATASET, ffactory, patseq, actor, True, True) ) 274 | 275 | 276 | print ('End of run.') 277 | 278 | -------------------------------------------------------------------------------- /cicat/generator/TTPFilter.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | :::::::::::::::::::::::: Critical Infrastructure Cyberspace Analysis Tool (CICAT) ::::::::::::::::::::::::::::::::::::::: 4 | 5 | NOTICE 6 | 7 | The contents of this material reflect the views of the author and/or the Director of the Center for Advanced Aviation 8 | System Development (CAASD), and do not necessarily reflect the views of the Federal Aviation Administration (FAA) 9 | or the Department of Transportation (DOT). Neither the FAA nor the DOT makes any warranty or guarantee, or promise, 10 | expressed or implied, concerning the content or accuracy of the views expressed herein. 11 | 12 | This is the copyright work of The MITRE Corporation and was produced for the U.S. Government under Contract Number 13 | DTFAWA-10-C-00080 and is subject to Federal Aviation Administration Acquisition Management System Clause 3.5-13, 14 | Rights in Data-General, Alt. III and Alt. IV (Oct. 1996). No other use other than that granted to the U.S. Government, 15 | or to those acting on behalf of the U.S. Government, under that Clause is authorized without the express written permission 16 | of The MITRE Corporation. For further information, please contact The MITRE Corporation, Contract Office, 7515 Colshire Drive, 17 | McLean, VA 22102 (703) 983-6000. ©2020 The MITRE Corporation. 18 | 19 | The Government retains a nonexclusive, royalty-free right to publish or reproduce this document, or to allow others to do so, for 20 | “Government Purposes Only.” 21 | 22 | (c) 2020 The MITRE Corporation. All Rights Reserved. 23 | 24 | :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: 25 | TTPfilter.py - Main TTP filtering routine 26 | :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: 27 | """ 28 | 29 | import sys 30 | from loaddata import LOAD_DATA, m_file_TESTBED_MODEL, m_file_TESTBED_SCNRO 31 | from loaddata import m_filter_test_list, m_test_actor_list 32 | from ffactory import FILTER_FACTORY, INIT_FILTERS, getTTPs, getTTPsforCTYPE, m_TACTIC_LIST 33 | 34 | # ATT&CK tactics list 35 | #m_TACTIC_LIST = ['initial-access', 'execution', 'persistence', 'privilege-escalation', 'defense-evasion', 'credential-access', 36 | # 'discovery', 'lateral-movement', 'collection', 'command-and-control', 'exfiltration', 37 | # 'impact', 'inhibit-response-function', 'impair-process-control' ] 38 | 39 | 40 | def TTP_FILTER (dataset, ffactory, IPorHostame, pattern, platFlag, actor, actFlag, trace): 41 | 42 | for component in dataset['COMPONENT']: 43 | if component.getIPAddress() == IPorHostame: 44 | break 45 | 46 | if not (component): 47 | if trace: 48 | print ('WARNING TTP_FILTER: Component', IPorHostame, 'not found.') 49 | return 50 | 51 | ctype = component.getCtype() 52 | if not (ctype): 53 | if trace: 54 | print ('WARNING TTP_FILTER: CTYPE for', IPorHostame, 'not found.') 55 | return 56 | 57 | # if platFlag use platform Filter (Windows, LInux) instead of surface Filter 58 | 59 | platform = None 60 | surflist = ctype.getSurfaceList() 61 | if not (surflist): 62 | platform = ctype.getPlatform() 63 | 64 | actr = '' 65 | if actFlag: 66 | actr = actor.getGroupID() 67 | 68 | stepTTPs = getTTPs(ffactory, dataset, pattern, platform, actr) 69 | 70 | surfTTPs = [] 71 | if surflist: 72 | surfTTPs = getTTPsforCTYPE(ffactory, dataset, ctype.getID() ) 73 | 74 | ret = [] 75 | for t in stepTTPs: 76 | ret.append (t.getTECHID()) 77 | 78 | surfret = [] 79 | if surfTTPs: 80 | for k in surfTTPs: 81 | surfret.append (k.getTECHID()) 82 | 83 | retset = set(ret) 84 | if (surfret): 85 | retset = retset & set(surfret) 86 | 87 | ret = list(retset) 88 | 89 | if trace: 90 | if platform: 91 | print ('TTP_FILTER: Asset:', IPorHostame, 'Platform:', platform, 'Tactic:', pattern, 'TTPs:', ret ) 92 | elif surflist: 93 | slist = [] 94 | for s in surflist: 95 | slist.append (s.getSurface() ) 96 | 97 | print ('TTP_FILTER: Asset:', IPorHostame, 'Surfaces:', slist, 'Pattern:', pattern, 'TTPs:', ret) 98 | 99 | else: 100 | print ('Warning! TTP_FILTER:', IPorHostame, 'has no platform or surface list.') 101 | 102 | return ret 103 | 104 | 105 | # Helper function for reading options from command line 106 | def optionReader(params, flag): 107 | idx = params.index(flag) 108 | if len(params) > idx + 1 and '-' not in params[idx + 1]: 109 | return params[idx + 1] 110 | else: 111 | print(flag + ' flag must include an option!') 112 | exit() 113 | 114 | 115 | # main entry point 116 | if ( __name__ == "__main__"): 117 | 118 | Ispread = m_file_TESTBED_MODEL 119 | Tspread = m_file_TESTBED_SCNRO #testbed data used for unit tests 120 | 121 | params = sys.argv 122 | if len(params) > 1: 123 | if 'help' in params[1].lower(): 124 | print ('\nUSAGE: python', params[0], '[-i ] [-s ]') 125 | exit() 126 | 127 | if '-i' in params: 128 | Ispread = optionReader(params, '-i') 129 | 130 | if '-s' in params: 131 | Tspread = optionReader(params, '-s') 132 | 133 | myDATASET = LOAD_DATA (Ispread, Tspread, False, False ) 134 | # LOAD_TTP_SUPPLEMENT(myDATASET) 135 | 136 | ffactory = FILTER_FACTORY(False ) 137 | INIT_FILTERS (ffactory, myDATASET) 138 | 139 | testhosts = m_filter_test_list 140 | 141 | print ('\n') 142 | print (' >>> Platform and Attack Surface Filter Test <<<' ) 143 | 144 | for c in testhosts: 145 | for component in myDATASET['COMPONENT']: 146 | if component.getIPAddress() == c: 147 | component.PP(True) 148 | break 149 | 150 | if not component.getSurfaceList(): 151 | print ('\n') 152 | print ('Testing platform filter:', component.getPlatform() ) 153 | for p in m_TACTIC_LIST: 154 | ttpseq1 = TTP_FILTER (myDATASET, ffactory, c, p, True, None, False, False) 155 | print ('TTPs for', c, 'tactic:', p, '('+ str(len(ttpseq1)) + '):', ttpseq1) 156 | else: 157 | print ('\n') 158 | print ('Testing attack surface filter:', component.getCTYPEID() ) 159 | for p in m_TACTIC_LIST: 160 | ttpseq1 = TTP_FILTER (myDATASET, ffactory, c, p, False, None, False, False) 161 | print ('TTPs for', c, 'tactic:', p, '('+ str(len(ttpseq1)) + '):', ttpseq1) 162 | 163 | 164 | print ('\n') 165 | print (' >>> Threat Actor Filter Testing <<<' ) 166 | 167 | actorlist = m_test_actor_list 168 | 169 | for c in testhosts: 170 | for component in myDATASET['COMPONENT']: 171 | if component.getIPAddress() == c: 172 | component.PP(True) 173 | break 174 | 175 | for a in actorlist: 176 | for actor in myDATASET['ATKGROUPS']: 177 | if actor.getGroupID() == a: 178 | break 179 | 180 | if not component.getSurfaceList(): 181 | print ('\n') 182 | print ('Testing platform filter:', component.getPlatform(), 'with actor', actor.getName() ) 183 | for p in m_TACTIC_LIST: 184 | ttpseq2 = TTP_FILTER (myDATASET, ffactory, c, p, True, actor, True, False ) 185 | print ('TTPs for', c, 'tactic:', p, '('+ str(len(ttpseq2)) + '):', ttpseq2 ) 186 | else: 187 | print ('\n') 188 | print ('Testing attack surface filter:', component.getCTYPEID(), 'with actor', actor.getName() ) 189 | for p in m_TACTIC_LIST: 190 | ttpseq2 = TTP_FILTER (myDATASET, ffactory, c, p, False, actor, True, False ) 191 | print ('TTPs for', c, 'tactic:', p, '('+ str(len(ttpseq2)) + '):', ttpseq2 ) 192 | 193 | print ('End of run') 194 | 195 | -------------------------------------------------------------------------------- /cicat/generator/actorGEN.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """ 4 | :::::::::::::::::::::::: Critical Infrastructure Cyberspace Analysis Tool (CICAT) ::::::::::::::::::::::::::::::::::::::: 5 | 6 | NOTICE 7 | 8 | The contents of this material reflect the views of the author and/or the Director of the Center for Advanced Aviation 9 | System Development (CAASD), and do not necessarily reflect the views of the Federal Aviation Administration (FAA) 10 | or the Department of Transportation (DOT). Neither the FAA nor the DOT makes any warranty or guarantee, or promise, 11 | expressed or implied, concerning the content or accuracy of the views expressed herein. 12 | 13 | This is the copyright work of The MITRE Corporation and was produced for the U.S. Government under Contract Number 14 | DTFAWA-10-C-00080 and is subject to Federal Aviation Administration Acquisition Management System Clause 3.5-13, 15 | Rights in Data-General, Alt. III and Alt. IV (Oct. 1996). No other use other than that granted to the U.S. Government, 16 | or to those acting on behalf of the U.S. Government, under that Clause is authorized without the express written permission 17 | of The MITRE Corporation. For further information, please contact The MITRE Corporation, Contract Office, 7515 Colshire Drive, 18 | McLean, VA 22102 (703) 983-6000. ©2020 The MITRE Corporation. 19 | 20 | The Government retains a nonexclusive, royalty-free right to publish or reproduce this document, or to allow others to do so, for 21 | “Government Purposes Only.” 22 | 23 | (c) 2020 The MITRE Corporation. All Rights Reserved. 24 | 25 | :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: 26 | actorGEN.py - verifies threat actor capabilities over range of tactics and platforms 27 | :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: 28 | """ 29 | 30 | import sys 31 | from ffactory import FILTER_FACTORY, INIT_FILTERS, getTTPs, showTTPs 32 | from loaddata import LOAD_DATA 33 | from topology import INIT_TOPOLOGY 34 | from loaddata import m_file_TESTBED_MODEL, m_file_TESTBED_SCNRO #testbed data used for unit tests 35 | from TACSequence import m_TACTIC_LIST, initPatternMenu, m_objdict 36 | from stats import TAGS_INDEX, x_TACTICS 37 | from mitGEN import ACTOR_REPORT 38 | 39 | 40 | 41 | # verifies the combination: threat actor, tactic, platform includes at least one TTP 42 | def filterTest (factory, dataset, tactic, platform, actor, trace): 43 | ret = getTTPs(ffactory, dataset, tactic, platform, actor) 44 | if trace: 45 | showTTPs ('FILTER TEST: ' + tactic + ', ' + platform + ', ' + actor +': ', ret ) 46 | return ret 47 | 48 | 49 | # verifies tactic and platform combinations for a specified objctive sequence 50 | def verifyObjSequence (factory, dataset, seqname, platform, actor, trace): 51 | 52 | ret = True 53 | entry = m_objdict[seqname][0] 54 | tIndexList = entry[1] 55 | 56 | if trace: 57 | print ('\nVerifying use of', seqname, 'by threat actor', actor, 'on', platform) 58 | 59 | for tIndex in tIndexList: 60 | tactic = m_TACTIC_LIST[tIndex] 61 | if not (filterTest (factory, dataset, tactic, platform, actor, trace) ): 62 | ret = False 63 | if trace: 64 | print ('WARNING! threat actor', actor, 'has no TTPs for', tactic, 'on', platform ) 65 | 66 | return ret 67 | 68 | # verifies TTP usage for range of threat actors 69 | def TestActorCapabilities(factory, dataset): 70 | 71 | actorlist = ['G0093', 'G0074', 'G0061', 'G0049', 'G0045', 'G0032', 'G0027', 'G0022', 'G0010', 'G0007' ] 72 | seqlist = m_objdict.keys() 73 | platlist = ['Windows', 'Linux', 'macOS'] 74 | 75 | for a in actorlist: 76 | print ('\n') 77 | print('Verifying TTPs for threat actor', a) 78 | for s in seqlist: 79 | for p in platlist: 80 | verifyObjSequence (factory, dataset, s, p, a, True) 81 | return 82 | 83 | # Helper function for reading options from command line 84 | def optionReader(params, flag): 85 | idx = params.index(flag) 86 | if len(params) > idx + 1 and '-' not in params[idx + 1]: 87 | return params[idx + 1] 88 | else: 89 | print(flag + ' flag must include an option!') 90 | exit() 91 | 92 | # main entry point 93 | if ( __name__ == "__main__"): 94 | 95 | Ispread = m_file_TESTBED_MODEL 96 | Tspread = m_file_TESTBED_SCNRO 97 | 98 | params = sys.argv 99 | if len(params) > 1: 100 | if 'help' in params[1].lower(): 101 | print ('\nUSAGE: python', params[0], '[-i ] [-s ]') 102 | exit() 103 | 104 | if '-i' in params: 105 | Ispread = optionReader(params, '-i') 106 | 107 | if '-s' in params: 108 | Tspread = optionReader(params, '-s') 109 | 110 | 111 | myDATASET = LOAD_DATA (Ispread, Tspread, False, False ) 112 | zonemap = INIT_TOPOLOGY(myDATASET, True ) 113 | 114 | ffactory = FILTER_FACTORY(False ) 115 | INIT_FILTERS (ffactory, myDATASET) 116 | 117 | initPatternMenu() 118 | 119 | TAGS_INDEX (myDATASET, True) 120 | ACTOR_REPORT (myDATASET, x_TACTICS.keys() ) 121 | 122 | TestActorCapabilities(ffactory, myDATASET) 123 | 124 | 125 | print ('End of run') 126 | 127 | -------------------------------------------------------------------------------- /cicat/generator/actorlists.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | :::::::::::::::::::::::: Critical Infrastructure Cyberspace Analysis Tool (CICAT) ::::::::::::::::::::::::::::::::::::::: 4 | 5 | NOTICE 6 | 7 | The contents of this material reflect the views of the author and/or the Director of the Center for Advanced Aviation 8 | System Development (CAASD), and do not necessarily reflect the views of the Federal Aviation Administration (FAA) 9 | or the Department of Transportation (DOT). Neither the FAA nor the DOT makes any warranty or guarantee, or promise, 10 | expressed or implied, concerning the content or accuracy of the views expressed herein. 11 | 12 | This is the copyright work of The MITRE Corporation and was produced for the U.S. Government under Contract Number 13 | DTFAWA-10-C-00080 and is subject to Federal Aviation Administration Acquisition Management System Clause 3.5-13, 14 | Rights in Data-General, Alt. III and Alt. IV (Oct. 1996). No other use other than that granted to the U.S. Government, 15 | or to those acting on behalf of the U.S. Government, under that Clause is authorized without the express written permission 16 | of The MITRE Corporation. For further information, please contact The MITRE Corporation, Contract Office, 7515 Colshire Drive, 17 | McLean, VA 22102 (703) 983-6000. ©2020 The MITRE Corporation. 18 | 19 | The Government retains a nonexclusive, royalty-free right to publish or reproduce this document, or to allow others to do so, for 20 | “Government Purposes Only.” 21 | 22 | (c) 2020 The MITRE Corporation. All Rights Reserved. 23 | 24 | :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: 25 | actorlists.py - Generates actor profiles from imported ATT&CK and ATT&CK for ICS data 26 | :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: 27 | """ 28 | 29 | #import sys, time 30 | from collections import defaultdict 31 | from loaddata import LOAD_DATA, LOAD_ATK4ICS 32 | from loaddata import m_file_INFRASTRUCTURE, m_file_SCENARIOS 33 | from columnloader import DumpColsToSpreadsheet 34 | 35 | def getMasterTTPlist (dataset): 36 | ret = [] 37 | for x in dataset['ATT&CK']: 38 | ret.append (x.getTECHID()) 39 | 40 | for i in dataset['ATK4ICS TTPs']: 41 | ret.append (i.getTECHID() ) 42 | 43 | return ret 44 | 45 | 46 | def getTTPidList (ttplist): 47 | ret = [] 48 | for t in ttplist: 49 | ret.append(t.getTECHID()) 50 | 51 | return ret 52 | 53 | # main entry point 54 | if ( __name__ == "__main__"): 55 | 56 | testfile = '..\\data\\ATKPROFILES.xlsx' 57 | myDATAWARE = LOAD_DATA(m_file_INFRASTRUCTURE, m_file_SCENARIOS, False, False ) 58 | LOAD_ATK4ICS (myDATAWARE, '..\\data\ATK4ICS.xlsx' ) 59 | 60 | atkdict = defaultdict(list) 61 | 62 | atkdict['MASTER'] = getMasterTTPlist(myDATAWARE) 63 | 64 | for j in myDATAWARE['ATKGROUPS']: 65 | atkdict[j.getGroupID()] = getTTPidList(j.getTTPList()) 66 | 67 | DumpColsToSpreadsheet (testfile, 'ACTOR PROFILES', atkdict) 68 | 69 | -------------------------------------------------------------------------------- /cicat/generator/atk2xl.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | :::::::::::::::::::::::: Critical Infrastructure Cyberspace Analysis Tool (CICAT) ::::::::::::::::::::::::::::::::::::::: 4 | 5 | NOTICE 6 | 7 | The contents of this material reflect the views of the author and/or the Director of the Center for Advanced Aviation 8 | System Development (CAASD), and do not necessarily reflect the views of the Federal Aviation Administration (FAA) 9 | or the Department of Transportation (DOT). Neither the FAA nor the DOT makes any warranty or guarantee, or promise, 10 | expressed or implied, concerning the content or accuracy of the views expressed herein. 11 | 12 | This is the copyright work of The MITRE Corporation and was produced for the U.S. Government under Contract Number 13 | DTFAWA-10-C-00080 and is subject to Federal Aviation Administration Acquisition Management System Clause 3.5-13, 14 | Rights in Data-General, Alt. III and Alt. IV (Oct. 1996). No other use other than that granted to the U.S. Government, 15 | or to those acting on behalf of the U.S. Government, under that Clause is authorized without the express written permission 16 | of The MITRE Corporation. For further information, please contact The MITRE Corporation, Contract Office, 7515 Colshire Drive, 17 | McLean, VA 22102 (703) 983-6000. ©2020 The MITRE Corporation. 18 | 19 | The Government retains a nonexclusive, royalty-free right to publish or reproduce this document, or to allow others to do so, for 20 | “Government Purposes Only.” 21 | 22 | (c) 2020 The MITRE Corporation. All Rights Reserved. 23 | 24 | :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: 25 | atk2xl.py - Utility to load ATTACK data into spreadsheet 26 | :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: 27 | """ 28 | 29 | import datetime 30 | import sys 31 | from openpyxl import Workbook 32 | from collections import defaultdict 33 | from loaddata import LOAD_DATA 34 | from loaddata import m_file_INFRASTRUCTURE, m_file_SCENARIOS 35 | 36 | m_DATASET = defaultdict(list ) 37 | 38 | m_ATTCKSHEETS = ['ATT&CK', 'ATKMITIGATION', 'ATKGROUPS', 'ATKMALWARE','ATKTOOL', 'ATKRELS'] 39 | 40 | def list2string(plist, separator): 41 | ret = 'undefined' 42 | if plist: 43 | ret = str(plist[0]) 44 | for j in range (1, len(plist)): 45 | ret = ret + separator + str(plist[j]) 46 | return ret 47 | 48 | def saveTechHeader(sheet): 49 | sheet.cell(1, 1, 'capec_id') 50 | sheet.cell(1, 2, 'capec_url') 51 | sheet.cell(1, 3, 'contributors') 52 | sheet.cell(1, 4, 'date_created') 53 | sheet.cell(1, 5, 'created_by_ref') 54 | sheet.cell(1, 6, 'data_sources') 55 | sheet.cell(1, 7, 'defense_bypassed') 56 | sheet.cell(1, 8, 'detectable_by_common_defenses') 57 | sheet.cell(1, 9, 'detectable_explanation') 58 | sheet.cell(1, 10, 'difficulty_explanation') 59 | sheet.cell(1, 11, 'difficulty_for_adversary)') 60 | sheet.cell(1, 12, 'effective_permissions') 61 | sheet.cell(1, 13, 'ID') 62 | sheet.cell(1, 14, 'matrix') 63 | sheet.cell(1, 15, 'date_modified') 64 | sheet.cell(1, 16, 'network_requirements') 65 | sheet.cell(1, 17, 'object_marking_refs') 66 | sheet.cell(1, 18, 'permissions_required') 67 | sheet.cell(1, 19, 'platform') 68 | sheet.cell(1, 20, 'remote_support') 69 | sheet.cell(1, 21, 'system_requirements') 70 | sheet.cell(1, 22, 'tactic') 71 | sheet.cell(1, 23, 'tactic_type') 72 | sheet.cell(1, 24, 'tech_name') 73 | sheet.cell(1, 25, 'tech_desc') 74 | sheet.cell(1, 26, 'tech_detect') 75 | sheet.cell(1, 27, 'tech_id') 76 | sheet.cell(1, 28, 'tech_references') 77 | sheet.cell(1, 29, 'type' ) 78 | 79 | def saveTechnique(sheet, row, TTP): 80 | sheet.cell(row, 1, list2string(TTP.capec_id)) 81 | sheet.cell(row, 2, list2string(TTP.capec_url)) 82 | sheet.cell(row, 3, list2string(TTP.contributors)) 83 | sheet.cell(row, 4, str(TTP.created)) 84 | sheet.cell(row, 5, str(TTP.created_by_ref)) 85 | sheet.cell(row, 6, list2string(TTP.data_sources)) 86 | sheet.cell(row, 7, list2string(TTP.defense_bypassed)) 87 | sheet.cell(row, 8, str(TTP.detectable_by_common_defenses )) 88 | sheet.cell(row, 9, str(TTP.detectable_explanation)) 89 | sheet.cell(row, 10, str(TTP.difficulty_explanation)) 90 | sheet.cell(row, 11, str(TTP.difficulty_for_adversary)) 91 | sheet.cell(row, 12, list2string(TTP.effective_permissions)) 92 | sheet.cell(row, 13, str(TTP.myID)) 93 | sheet.cell(row, 14, str(TTP.matrix)) 94 | sheet.cell(row, 15, str(TTP.modified)) 95 | sheet.cell(row, 16, str(TTP.network_requirements)) 96 | sheet.cell(row, 17, list2string(TTP.object_marking_refs)) 97 | sheet.cell(row, 18, list2string(TTP.permissions_required)) 98 | sheet.cell(row, 19, list2string(TTP.platform )) 99 | sheet.cell(row, 20, str(TTP.remote_support)) 100 | sheet.cell(row, 21, list2string(TTP.system_requirements)) 101 | sheet.cell(row, 22, list2string(TTP.tactic )) 102 | sheet.cell(row, 23, str(TTP.tactic_type)) 103 | sheet.cell(row, 24, str(TTP.technique)) 104 | sheet.cell(row, 25, str(TTP.desc)) 105 | sheet.cell(row, 26, str(TTP.technique_detection)) 106 | sheet.cell(row, 27, str(TTP.technique_id)) 107 | sheet.cell(row, 28, list2string(TTP.technique_references)) 108 | sheet.cell(row, 29, str(TTP.typex)) 109 | 110 | def saveICSTTP (sheet, row, ICSTTP): 111 | sheet.cell(row, 1, 'undefined' ) #list2string(TTP.capec_id)) 112 | sheet.cell(row, 2, 'undefined' ) #list2string(TTP.capec_url)) 113 | sheet.cell(row, 3, ICSTTP.contributor ) 114 | sheet.cell(row, 4, datetime.datetime.now() ) 115 | sheet.cell(row, 5, 'identity--c78cb6e5-0c4b-4611-8297-d1b8b55e40b5' ) #str(TTP.created_by_ref)) 116 | sheet.cell(row, 6, list2string(ICSTTP.datasources, ',')) 117 | sheet.cell(row, 7, 'undefined' ) #list2string(TTP.defense_bypassed)) 118 | sheet.cell(row, 8, None ) #str(TTP.detectable_by_common_defenses )) 119 | sheet.cell(row, 9, None ) #str(TTP.detectable_explanation)) 120 | sheet.cell(row, 10, None) #str(TTP.difficulty_explanation)) 121 | sheet.cell(row, 11, None) #str(TTP.difficulty_for_adversary)) 122 | sheet.cell(row, 12, 'undefined' ) #list2string(TTP.effective_permissions)) 123 | sheet.cell(row, 13, 'attack4ics-pattern--'+str(ICSTTP.aid)) 124 | sheet.cell(row, 14, 'mitre-attack4ics') 125 | sheet.cell(row, 15, None ) #str(TTP.modified)) 126 | sheet.cell(row, 16, None ) #str(TTP.network_requirements)) 127 | sheet.cell(row, 17, None ) #list2string(TTP.object_marking_refs)) 128 | sheet.cell(row, 18, None) #list2string(TTP.permissions_required)) 129 | sheet.cell(row, 19, list2string(ICSTTP.platform, ',' )) 130 | sheet.cell(row, 20, None ) #str(TTP.remote_support)) 131 | sheet.cell(row, 21, 'undefined' ) #list2string(TTP.system_requirements)) 132 | sheet.cell(row, 22, list2string(ICSTTP.tactic, ',' )) 133 | sheet.cell(row, 23, None ) #str(TTP.tactic_type)) 134 | sheet.cell(row, 24, str(ICSTTP.name)) 135 | sheet.cell(row, 25, str(ICSTTP.desc)) 136 | sheet.cell(row, 26, None ) #str(TTP.technique_detection)) 137 | sheet.cell(row, 27, str(ICSTTP.techID)) 138 | sheet.cell(row, 28, ICSTTP.url) 139 | sheet.cell(row, 29, 'attack-pattern') 140 | 141 | 142 | def saveMitHeader(sheet, row=1 ): 143 | sheet.cell(row, 1, 'date_created') 144 | sheet.cell(row, 2, 'created_by_ref') 145 | sheet.cell(row, 3, 'ID') 146 | sheet.cell(row, 4, 'matrix') 147 | sheet.cell(row, 5, 'mitigation') 148 | sheet.cell(row, 6, 'mit_desc') 149 | sheet.cell(row, 7, 'mit_references') 150 | sheet.cell(row, 8, 'date_modified') 151 | sheet.cell(row, 9, 'mit_technique_id') 152 | sheet.cell(row, 10, 'type') 153 | sheet.cell(row, 11, 'mit_url') 154 | 155 | def saveICSMitigation(sheet, row, ICSTTP): 156 | sheet.cell(row, 1, datetime.datetime.now() ) 157 | sheet.cell(row, 2, 'identity--c78cb6e5-0c4b-4611-8297-d1b8b55e40b5') #str(MIT.created_by_ref)) 158 | sheet.cell(row, 3, 'attack4ics-course-of-action--'+str(ICSTTP.aid) ) 159 | sheet.cell(row, 4, 'mitre-attack4ics' ) 160 | sheet.cell(row, 5, ICSTTP.name + ' Mitigation') 161 | sheet.cell(row, 6, ICSTTP.mitigation) 162 | sheet.cell(row, 7, ICSTTP.url) 163 | sheet.cell(row, 8, None ) #str(MIT.modified)) 164 | sheet.cell(row, 9, ICSTTP.techID ) 165 | sheet.cell(row, 10, 'course-of-action') 166 | sheet.cell(row, 11, ICSTTP.url ) 167 | 168 | def saveMitigation(sheet, row, MIT ): 169 | sheet.cell(row, 1, str(MIT.created)) 170 | sheet.cell(row, 2, str(MIT.created_by_ref)) 171 | sheet.cell(row, 3, str(MIT.myID)) 172 | sheet.cell(row, 4, str(MIT.matrix)) 173 | sheet.cell(row, 5, str(MIT.mitigation)) 174 | sheet.cell(row, 6, str(MIT.desc)) 175 | sheet.cell(row, 7, list2string(MIT.mitigation_references)) 176 | sheet.cell(row, 8, str(MIT.modified)) 177 | sheet.cell(row, 9, str(MIT.technique_id)) 178 | sheet.cell(row, 10, str(MIT.typex)) 179 | sheet.cell(row, 11, str(MIT.url)) 180 | 181 | def saveMalHeader(sheet, row=1): 182 | sheet.cell(row, 1, 'date_created') 183 | sheet.cell(row, 2, 'created_by_ref') 184 | sheet.cell(row, 3, 'ID') 185 | sheet.cell(row, 4, 'matrix') 186 | sheet.cell(row, 5, 'date_modified') 187 | sheet.cell(row, 6, 'software') 188 | sheet.cell(row, 7, 'software_aliases') 189 | sheet.cell(row, 8, 'desc') 190 | sheet.cell(row, 9, 'software_id') 191 | sheet.cell(row, 10, 'software_labels') 192 | sheet.cell(row, 11, 'software_platform' ) 193 | sheet.cell(row, 12, 'software_references') 194 | sheet.cell(row, 13, 'typex') 195 | sheet.cell(row, 14, 'mal_url') 196 | 197 | def saveMalware(sheet, row, MAL): 198 | sheet.cell(row, 1, str(MAL.created )) 199 | sheet.cell(row, 2, str(MAL.created_by_ref)) 200 | sheet.cell(row, 3, str(MAL.myID )) 201 | sheet.cell(row, 4, str(MAL.matrix )) 202 | sheet.cell(row, 5, str(MAL.modified )) 203 | sheet.cell(row, 6, str(MAL.software )) 204 | sheet.cell(row, 7, list2string(MAL.software_aliases )) 205 | sheet.cell(row, 8, str(MAL.desc )) 206 | sheet.cell(row, 9, str(MAL.software_id )) 207 | sheet.cell(row, 10, list2string(MAL.software_labels )) 208 | sheet.cell(row, 11, list2string(MAL.software_platform )) 209 | sheet.cell(row, 12, list2string(MAL.software_references )) 210 | sheet.cell(row, 13, str(MAL.typex )) 211 | sheet.cell(row, 14, str(MAL.url )) 212 | 213 | def saveToolHeader(sheet, row=1): 214 | sheet.cell(row, 1, 'date_created') 215 | sheet.cell(row, 2, 'created_by_ref') 216 | sheet.cell(row, 3, 'ID') 217 | sheet.cell(row, 4, 'matrix') 218 | sheet.cell(row, 5, 'date_modified') 219 | sheet.cell(row, 6, 'software') 220 | sheet.cell(row, 7, 'software_aliases') 221 | sheet.cell(row, 8, 'desc') 222 | sheet.cell(row, 9, 'software_id') 223 | sheet.cell(row, 10, 'software_labels') 224 | sheet.cell(row, 11, 'software_platform') 225 | sheet.cell(row, 12, 'software_references') 226 | sheet.cell(row, 13, 'type') 227 | sheet.cell(row, 14, 'tool_url') 228 | 229 | def saveCybertool(sheet, row, TOOL): 230 | sheet.cell(row, 1, str(TOOL.created )) 231 | sheet.cell(row, 2, str(TOOL.created_by_ref )) 232 | sheet.cell(row, 3, str(TOOL.myID )) 233 | sheet.cell(row, 4, str(TOOL.matrix )) 234 | sheet.cell(row, 5, str(TOOL.modified )) 235 | sheet.cell(row, 6, str(TOOL.software )) 236 | sheet.cell(row, 7, list2string(TOOL.software_aliases )) 237 | sheet.cell(row, 8, str(TOOL.desc )) 238 | sheet.cell(row, 9, str(TOOL.software_id )) 239 | sheet.cell(row, 10, list2string(TOOL.software_labels )) 240 | sheet.cell(row, 11, list2string(TOOL.software_platform )) 241 | sheet.cell(row, 12, list2string(TOOL.software_references )) 242 | sheet.cell(row, 13, str(TOOL.typex )) 243 | sheet.cell(row, 14, str(TOOL.url )) 244 | 245 | def saveGroupHeader(sheet, row=1): 246 | sheet.cell(row, 1, 'date_created') 247 | sheet.cell(row, 2, 'created_by_ref') 248 | sheet.cell(row, 3, 'group') 249 | sheet.cell(row, 4, 'group_aliases') 250 | sheet.cell(row, 5, 'desc') 251 | sheet.cell(row, 6, 'group_id') 252 | sheet.cell(row, 7, 'group_references') 253 | sheet.cell(row, 8, 'ID') 254 | sheet.cell(row, 9, 'matrix') 255 | sheet.cell(row, 10, 'date_modified') 256 | sheet.cell(row, 11, 'type') 257 | sheet.cell(row, 12, 'group_url') 258 | 259 | def saveGroup(sheet, row, ACT): 260 | sheet.cell(row, 1, str(ACT.created )) 261 | sheet.cell(row, 2, str(ACT.created_by_ref )) 262 | sheet.cell(row, 3, str(ACT.group )) 263 | sheet.cell(row, 4, list2string(ACT.group_aliases )) 264 | sheet.cell(row, 5, str(ACT.desc )) 265 | sheet.cell(row, 6, str(ACT.group_id )) 266 | sheet.cell(row, 7, list2string(ACT.group_references )) 267 | sheet.cell(row, 8, str(ACT.myID )) 268 | sheet.cell(row, 9, str(ACT.matrix )) 269 | sheet.cell(row, 10, str(ACT.modified )) 270 | sheet.cell(row, 11, str(ACT.typex )) 271 | sheet.cell(row, 12, str(ACT.url )) 272 | 273 | def saveRelationHeader(sheet, row=1): 274 | sheet.cell(row, 1, 'date_created') 275 | sheet.cell(row, 2, 'created_by_ref') 276 | sheet.cell(row, 3, 'ID') 277 | sheet.cell(row, 4, 'date_modified') 278 | sheet.cell(row, 5, 'rship_name') 279 | sheet.cell(row, 6, 'rship_desc') 280 | sheet.cell(row, 7, 'src_obj') 281 | sheet.cell(row, 8, 'tgt_obj') 282 | 283 | def saveRelation(sheet, row, REL): 284 | sheet.cell(row, 1, str(REL.created )) 285 | sheet.cell(row, 2, str(REL.created_by_ref )) 286 | sheet.cell(row, 3, str(REL.myID )) 287 | sheet.cell(row, 4, str(REL.modified )) 288 | sheet.cell(row, 5, str(REL.rship_name )) 289 | sheet.cell(row, 6, str(REL.rship_desc )) 290 | sheet.cell(row, 7, str(REL.src_obj )) 291 | sheet.cell(row, 8, str(REL.tgt_obj )) 292 | 293 | def saveDataset (wb, sheetname): 294 | 295 | sheet = wb.create_sheet (title=sheetname ) 296 | if sheetname == 'ATT&CK': 297 | saveTechHeader (sheet) 298 | i=2 299 | for row in m_DATASET[sheetname]: 300 | saveTechnique(sheet, i, row) 301 | i = i + 1 302 | 303 | elif sheetname == 'ATKMITIGATION': 304 | saveMitHeader(sheet) 305 | i = 2 306 | for row in m_DATASET[sheetname]: 307 | saveMitigation(sheet, i, row) 308 | i= i + 1 309 | 310 | elif sheetname == 'ATKGROUPS': 311 | saveGroupHeader(sheet) 312 | i = 2 313 | for row in m_DATASET[sheetname]: 314 | saveGroup(sheet, i, row) 315 | i= i + 1 316 | 317 | elif sheetname == 'ATKMALWARE': 318 | saveMalHeader(sheet) 319 | i = 2 320 | for row in m_DATASET[sheetname]: 321 | saveMalware(sheet, i, row) 322 | i= i + 1 323 | 324 | elif sheetname == 'ATKTOOL': 325 | saveToolHeader(sheet) 326 | i = 2 327 | for row in m_DATASET[sheetname]: 328 | saveCybertool(sheet, i, row) 329 | i= i + 1 330 | 331 | elif sheetname == 'ATKRELS': 332 | saveRelationHeader(sheet) 333 | i = 2 334 | for row in m_DATASET[sheetname]: 335 | saveRelation(sheet, i, row) 336 | i= i + 1 337 | else: 338 | print ('Dont know', sheetname) 339 | 340 | def saveICSDataset (wb, sheetname, dataset): 341 | 342 | sheet = wb.create_sheet(title=sheetname ) 343 | if sheetname == 'ATK4ICS TTPs': 344 | saveTechHeader (sheet) 345 | i = 2 346 | for row in dataset.keys(): 347 | saveICSTTP (sheet, i, dataset[row] ) 348 | i = i + 1 349 | 350 | elif sheetname == 'ATK4ICS MITs': 351 | saveMitHeader(sheet) 352 | i = 2 353 | for row in dataset.keys(): 354 | saveICSMitigation(sheet, i, dataset[row]) 355 | i = i + 1 356 | 357 | 358 | def DumpToSpreadsheet(fname ): 359 | wb = Workbook() 360 | for sheet in m_ATTCKSHEETS: 361 | saveDataset (wb, sheet) 362 | wb.save(filename = fname ) 363 | 364 | 365 | # main entry point 366 | if ( __name__ == "__main__"): 367 | 368 | # default settings 369 | fname = '../data/ATTACK.xlsx' 370 | loadopt = 'JSON' 371 | 372 | params = sys.argv 373 | if (len(params) > 1 ): 374 | fname = params[1] 375 | 376 | if (len(params) > 2 ): 377 | loadopt = params[2] 378 | 379 | if not(loadopt == 'JSON') and not(loadopt == 'STIX'): 380 | print ('Usage: python', params[0], ' [JSON|STIX]' ) 381 | exit() 382 | 383 | print ('Loading ATT&CK data from', loadopt, 'into spreadsheet', fname) 384 | 385 | m_DATASET = LOAD_DATA (m_file_INFRASTRUCTURE, m_file_SCENARIOS, False, False) 386 | DumpToSpreadsheet (fname ) 387 | 388 | print ('End of run') 389 | -------------------------------------------------------------------------------- /cicat/generator/atk4ics.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | :::::::::::::::::::::::: Critical Infrastructure Cyberspace Analysis Tool (CICAT) ::::::::::::::::::::::::::::::::::::::: 4 | 5 | NOTICE 6 | 7 | The contents of this material reflect the views of the author and/or the Director of the Center for Advanced Aviation 8 | System Development (CAASD), and do not necessarily reflect the views of the Federal Aviation Administration (FAA) 9 | or the Department of Transportation (DOT). Neither the FAA nor the DOT makes any warranty or guarantee, or promise, 10 | expressed or implied, concerning the content or accuracy of the views expressed herein. 11 | 12 | This is the copyright work of The MITRE Corporation and was produced for the U.S. Government under Contract Number 13 | DTFAWA-10-C-00080 and is subject to Federal Aviation Administration Acquisition Management System Clause 3.5-13, 14 | Rights in Data-General, Alt. III and Alt. IV (Oct. 1996). No other use other than that granted to the U.S. Government, 15 | or to those acting on behalf of the U.S. Government, under that Clause is authorized without the express written permission 16 | of The MITRE Corporation. For further information, please contact The MITRE Corporation, Contract Office, 7515 Colshire Drive, 17 | McLean, VA 22102 (703) 983-6000. ©2020 The MITRE Corporation. 18 | 19 | The Government retains a nonexclusive, royalty-free right to publish or reproduce this document, or to allow others to do so, for 20 | “Government Purposes Only.” 21 | 22 | (c) 2020 The MITRE Corporation. All Rights Reserved. 23 | 24 | :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: 25 | atk4ics.py - Utility to convert ATK4ICS XML file to spreadsheet that can be imported by CICAT 26 | :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: 27 | """ 28 | 29 | 30 | import xml.sax 31 | import collections 32 | from openpyxl import Workbook 33 | 34 | from atk2xl import saveICSDataset 35 | 36 | class ICSTTP(): 37 | def __init__ (self, aid, techID): 38 | self.aid = aid 39 | self.techID = techID 40 | self.name = '' 41 | self.tactic = '' 42 | self.desc = '' 43 | self.platform = '' 44 | self.contributor = '' 45 | self.datasources = '' 46 | self.levels = '' 47 | self.mitigation = '' 48 | self.assets = '' 49 | self.url = '' 50 | 51 | def setName(self, name): 52 | self.name = name 53 | 54 | def setTactic (self, category): 55 | self.tactic = category 56 | 57 | def setDesc (self, desc): 58 | self.desc = desc 59 | 60 | def setPlatform (self, assets): 61 | self.platform = assets 62 | 63 | def setContributor(self, contributor): 64 | self.contributor = contributor 65 | 66 | def setDatasources (self, datasources): 67 | self.datasources = datasources 68 | 69 | def setLevels (self, levels): 70 | self.levels = levels 71 | 72 | def setMitigation (self, mitigation): 73 | self.mitigation = mitigation 74 | 75 | def setAssets (self, assets): 76 | self.assets = assets 77 | 78 | def getTECHID(self): 79 | return self.techID 80 | 81 | def setURL (self, urlstr): 82 | self.url = urlstr 83 | 84 | def PP(self): 85 | print ('\n') 86 | print (self.techID, ':', self.name) 87 | print (self.desc.encode('utf-8')) 88 | print ('Tactics:', self.tactic ) 89 | print ('Levels:', self.levels ) 90 | print ('Assets:', self.assets ) 91 | print ('Platforms:', self.platform ) 92 | print ('Sources:', self.datasources ) 93 | print ('Mitigations:', self.mitigation.encode('utf-8') ) 94 | print ('URL:', self.url) 95 | 96 | 97 | m_urlbase = 'https://collaborate.mitre.org/attackics/index.php/Technique' 98 | 99 | m_ICSlist = collections.defaultdict(list) 100 | 101 | m_ignorelist = ['Permissions Required', 'Asset Specific Descriptions', 'Asset Specific Description', 'Free_Text'] 102 | 103 | 104 | class ICSHandler (xml.sax.ContentHandler): 105 | def __init__(self): 106 | self.currTag = '' 107 | self.currICS = None 108 | self.skipflag = False 109 | self.attributes = None 110 | self.fieldbuf = '' 111 | 112 | def checkAttributes (self, attributes, trace): 113 | ret = False 114 | names = attributes.getNames() 115 | if 'ID' in names: 116 | if trace: 117 | print ('\nParsing:', attributes.getValue('Title') ) 118 | ret = True 119 | elif len(names) == 1: 120 | if attributes.getValue(names[0]) in m_ignorelist: 121 | if trace: 122 | print ('...ignoring', attributes.getValue(names[0]) ) 123 | self.skipflag = True 124 | else: 125 | if trace: 126 | print ('Attribute:', attributes.getValue(names[0])) 127 | self.skipflag = False 128 | self.currTag = attributes.getValue(names[0]) 129 | else: 130 | if trace: 131 | print ('empty attribute list..') 132 | 133 | return ret 134 | 135 | def startElement(self, tag, attributes): 136 | self.skipflag = False 137 | if self.checkAttributes (attributes, False ): 138 | self.currID = attributes['ID'] 139 | techID = attributes['Title'].split('/')[1] 140 | self.currICS = ICSTTP( self.currID, techID ) 141 | m_ICSlist[self.currID] = self.currICS 142 | 143 | def endElement(self, tag): 144 | buf = self.fieldbuf.rstrip() 145 | self.fieldbuf = buf 146 | if tag == 'Field': 147 | # print ('Field value:', self.fieldbuf.lstrip() 148 | if self.currTag == 'Technical Description': 149 | self.currICS.setDesc(self.fieldbuf.lstrip() ) 150 | elif self.currTag == 'Name': 151 | self.currICS.setName(self.fieldbuf.lstrip() ) 152 | elif self.currTag == 'Category': 153 | self.currICS.setTactic(self.fieldbuf.lstrip().split(', ' )) 154 | elif self.currTag == 'Data Sources': 155 | self.currICS.setDatasources(list(self.fieldbuf.lstrip().split(', ')) ) 156 | elif self.currTag == 'Levels': 157 | self.currICS.setLevels(self.fieldbuf.lstrip().split(',') ) 158 | elif self.currTag == 'Platform': 159 | self.currICS.setPlatform (self.fieldbuf.lstrip().split(', ') ) 160 | elif self.currTag == 'Assets': 161 | tmp = self.fieldbuf.lstrip().replace(',', '/') 162 | self.currICS.setAssets (tmp.split('/' ) ) 163 | elif self.currTag == 'Contributors': 164 | self.currICS.setContributor(self.fieldbuf.lstrip() ) 165 | elif self.currTag == 'Mitigation': 166 | self.currICS.setMitigation (self.fieldbuf.lstrip() ) 167 | self.fieldbuf = '' 168 | self.currTag = '' 169 | 170 | if self.skipflag: 171 | self.skipflag = False 172 | 173 | def characters(self, content): 174 | self.fieldbuf = self.fieldbuf + content 175 | 176 | 177 | class ICSFactory(): 178 | def __init__ (self, trace): 179 | self.file = '' 180 | self.trace = trace 181 | if self.trace: 182 | print ('ICSTTP factory constructed..') 183 | 184 | 185 | def load (self, xmlfile ): 186 | self.file = xmlfile 187 | parser = xml.sax.make_parser() 188 | parser.setFeature(xml.sax.handler.feature_namespaces, 0) 189 | Handler = ICSHandler() 190 | parser.setContentHandler( Handler ) 191 | 192 | if self.trace: 193 | print ('Loading ICSTTP data..') 194 | 195 | parser.parse(self.file) 196 | 197 | if self.trace: 198 | print ("ICS data loaded:", len(m_ICSlist), "entries") 199 | 200 | 201 | def DumpICSToSpreadsheet(fname, m_ICSlist ): 202 | 203 | tablist = ['ATK4ICS TTPs', 'ATK4ICS MITs'] 204 | 205 | wb = Workbook() 206 | for sheet in tablist: 207 | saveICSDataset (wb, sheet, m_ICSlist ) 208 | wb.save(filename = fname ) 209 | 210 | 211 | # main entry point 212 | if ( __name__ == "__main__"): 213 | 214 | 215 | infile = '..\\data\\atk4ics.xml' 216 | outfile = '..\\data\ATK4ICS.xlsx' 217 | 218 | icsreader = ICSFactory (False ) 219 | icsreader.load (infile ) 220 | 221 | # update entries to include URLs to ATK4ICS pages 222 | 223 | for i in m_ICSlist.keys(): 224 | m_ICSlist[i].setURL (m_urlbase +'/'+ m_ICSlist[i].getTECHID() ) 225 | 226 | print ('\n') 227 | print ('Creating', outfile) 228 | 229 | DumpICSToSpreadsheet (outfile, m_ICSlist ) 230 | 231 | print('End of run') 232 | 233 | -------------------------------------------------------------------------------- /cicat/generator/cicat.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | :::::::::::::::::::::::: Critical Infrastructure Cyberspace Analysis Tool (CICAT) ::::::::::::::::::::::::::::::::::::::: 4 | 5 | NOTICE 6 | 7 | The contents of this material reflect the views of the author and/or the Director of the Center for Advanced Aviation 8 | System Development (CAASD), and do not necessarily reflect the views of the Federal Aviation Administration (FAA) 9 | or the Department of Transportation (DOT). Neither the FAA nor the DOT makes any warranty or guarantee, or promise, 10 | expressed or implied, concerning the content or accuracy of the views expressed herein. 11 | 12 | This is the copyright work of The MITRE Corporation and was produced for the U.S. Government under Contract Number 13 | DTFAWA-10-C-00080 and is subject to Federal Aviation Administration Acquisition Management System Clause 3.5-13, 14 | Rights in Data-General, Alt. III and Alt. IV (Oct. 1996). No other use other than that granted to the U.S. Government, 15 | or to those acting on behalf of the U.S. Government, under that Clause is authorized without the express written permission 16 | of The MITRE Corporation. For further information, please contact The MITRE Corporation, Contract Office, 7515 Colshire Drive, 17 | McLean, VA 22102 (703) 983-6000. ©2020 The MITRE Corporation. 18 | 19 | The Government retains a nonexclusive, royalty-free right to publish or reproduce this document, or to allow others to do so, for 20 | “Government Purposes Only.” 21 | 22 | (c) 2020 The MITRE Corporation. All Rights Reserved. 23 | 24 | :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: 25 | cicat.py - Critical Infrastructure Cyber Analysis Tool 26 | :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: 27 | """ 28 | 29 | 30 | import sys, time 31 | from collections import defaultdict 32 | from loaddata import LOAD_DATA 33 | from loaddata import m_file_TESTBED_MODEL, m_file_TESTBED_SCNRO 34 | 35 | myDATAWARE = None 36 | 37 | x_TACTICS = defaultdict(list) 38 | x_PLATFORMS = defaultdict(list) 39 | x_TYPES = defaultdict(list) 40 | 41 | x_ACTTAGS = defaultdict(list) 42 | x_TTPTAGS = defaultdict(list) 43 | x_MALTAGS = defaultdict(list) 44 | x_COATAGS = defaultdict(list) 45 | x_TOLTAGS = defaultdict(list) 46 | 47 | 48 | def printREPORT (title, lList, verbose=False ): 49 | print (title ) 50 | for l in lList: 51 | print ('\n') 52 | l.PP(verbose) 53 | print ('\n>>> END OF REPORT <<<') 54 | 55 | def shortlist(listx, num): 56 | nxt = 0 57 | for entry in listx: 58 | entry.PP() 59 | nxt = nxt + 1 60 | if (nxt >= num): 61 | break 62 | 63 | def checkForMods (ATTLIST, tstTime ): 64 | 65 | ret = [] 66 | for x in ATTLIST: 67 | try: 68 | if x.modifiedSince (tstTime): 69 | ret.append (x) 70 | except ValueError: 71 | print ('Time check error for', x.getName(), ':', x.modified ) 72 | 73 | return ret 74 | 75 | 76 | 77 | def Timecheck (tsttime, dataset): 78 | 79 | print ('Checking for updates since', tsttime ) 80 | 81 | c1 = checkForMods (dataset['ATT&CK'], tsttime ) 82 | if not (c1): 83 | print ('No changes to TTPs') 84 | else: 85 | print ('Changes to TTPs:', len(c1)) 86 | 87 | c2 = checkForMods (dataset['ATKGROUPS'], tsttime ) 88 | if not (c2): 89 | print ('No changes to Intrustion sets') 90 | else: 91 | print ('Changes to Intrustion sets:', len(c2)) 92 | 93 | c3 = checkForMods (dataset['ATKMALWARE'], tsttime ) 94 | if not (c3): 95 | print ('No changes to Malware') 96 | else: 97 | print ('Changes to Malware:', len(c3)) 98 | 99 | c4 = checkForMods (dataset['ATKTOOL'], tsttime ) 100 | if not (c4): 101 | print ('No changes to Tools') 102 | else: 103 | print ('Changes to Tools:', len(c4)) 104 | 105 | c5 = checkForMods (dataset['ATKMITIGATION'], tsttime ) 106 | if not (c5): 107 | print ('No changes to Mitigations') 108 | else: 109 | print ('Changes to Mitigations:', len(c5)) 110 | 111 | 112 | # main entry point 113 | if ( __name__ == "__main__"): 114 | 115 | mode = 'CAPABILITY' 116 | params = sys.argv 117 | if (len(params) > 1 ): 118 | mode = params[1] 119 | 120 | if mode.lower() == 'help': 121 | print ('\nUSAGE: python', params[0], '[HELP|SCENARIO|CAPABILITY|LOCATION|ACTOR]') 122 | exit() 123 | 124 | myDATAWARE = LOAD_DATA(m_file_TESTBED_MODEL, m_file_TESTBED_SCNRO, True, False ) 125 | 126 | for q in myDATAWARE['ATKGROUPS']: 127 | for j in q.getTags(): 128 | x_ACTTAGS[j].append(q) 129 | 130 | for q in myDATAWARE['ATKMALWARE']: 131 | for j in q.getTags(): 132 | x_MALTAGS[j].append(q) 133 | 134 | for q in myDATAWARE['ATT&CK']: 135 | for j in q.getTags(): 136 | x_TTPTAGS[j].append(q) 137 | 138 | for q in myDATAWARE['ATKMITIGATION']: 139 | for j in q.getTags(): 140 | x_COATAGS[j].append(q) 141 | 142 | for q in myDATAWARE['ATKTOOL']: 143 | for j in q.getTags(): 144 | x_TOLTAGS[j].append(q) 145 | 146 | testdates = [ (2018, 11, 15, 12, 34, 56, 0, 0, 0) ] 147 | for dt in testdates: 148 | t = time.mktime(dt) 149 | ttm = time.strftime( "%Y-%m-%d %H:%M:%S", time.gmtime(t)) 150 | print ('\nTimecheck:', ttm) 151 | Timecheck (ttm, myDATAWARE) 152 | 153 | ttm = time.strftime( "%Y-%m-%d %H:%M:%S", time.localtime()) 154 | print ('\nTimecheck:', ttm) 155 | Timecheck (ttm, myDATAWARE) 156 | 157 | if (mode.lower() == 'scenario'): 158 | printREPORT('\n\n\n>>> SCENARIO REPORT <<<<', myDATAWARE['SCENARIOX'], True ) 159 | elif (mode.lower() == 'capability'): 160 | printREPORT('\n\n\n>>> CAPABILITY REPORT <<<<', myDATAWARE['CAPABILITY'], True ) 161 | elif (mode.lower() == 'location'): 162 | printREPORT('\n\n\n>>> LOCATION REPORT <<<<', myDATAWARE['LOCATION'], True ) 163 | elif (mode.lower() == 'actor'): 164 | printREPORT('\n\n\n>>> ACTOR REPORT <<<<', myDATAWARE['ACTOR'], True ) 165 | else: 166 | print ('\nUSAGE: python cicat.py [SCENARIO|CAPABILITY|LOCATION|ACTOR]') 167 | 168 | 169 | 170 | print ("end of run") 171 | -------------------------------------------------------------------------------- /cicat/generator/columnloader.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | :::::::::::::::::::::::: Critical Infrastructure Cyberspace Analysis Tool (CICAT) ::::::::::::::::::::::::::::::::::::::: 4 | 5 | NOTICE 6 | 7 | The contents of this material reflect the views of the author and/or the Director of the Center for Advanced Aviation 8 | System Development (CAASD), and do not necessarily reflect the views of the Federal Aviation Administration (FAA) 9 | or the Department of Transportation (DOT). Neither the FAA nor the DOT makes any warranty or guarantee, or promise, 10 | expressed or implied, concerning the content or accuracy of the views expressed herein. 11 | 12 | This is the copyright work of The MITRE Corporation and was produced for the U.S. Government under Contract Number 13 | DTFAWA-10-C-00080 and is subject to Federal Aviation Administration Acquisition Management System Clause 3.5-13, 14 | Rights in Data-General, Alt. III and Alt. IV (Oct. 1996). No other use other than that granted to the U.S. Government, 15 | or to those acting on behalf of the U.S. Government, under that Clause is authorized without the express written permission 16 | of The MITRE Corporation. For further information, please contact The MITRE Corporation, Contract Office, 7515 Colshire Drive, 17 | McLean, VA 22102 (703) 983-6000. ©2020 The MITRE Corporation. 18 | 19 | The Government retains a nonexclusive, royalty-free right to publish or reproduce this document, or to allow others to do so, for 20 | “Government Purposes Only.” 21 | 22 | (c) 2020 The MITRE Corporation. All Rights Reserved. 23 | 24 | :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: 25 | columnloader.py - Imports and exports column data from/to spreadsheets 26 | :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: 27 | """ 28 | 29 | import openpyxl 30 | from collections import defaultdict 31 | 32 | m_DATASET = defaultdict(list ) 33 | 34 | m_testdata = [['MASTER', ['one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'ten']], 35 | ['C001', ['one', 'two', 'three', 'five', 'nine', 'eleven']], # values not in master are not stored to or retrieved from spreadsheet 36 | ['C002', ['two', 'four', 'six', 'eight', 'ten']], 37 | ['C003', ['four', 'one', 'three', 'five']] ] 38 | 39 | def initData(): 40 | for t in m_testdata: 41 | m_DATASET[t[0]] = t[1] 42 | 43 | """ 44 | Routines to store column data to a spreadsheet 45 | 46 | Spreadsheet organized to with MASTER list of values as first column. 47 | MASTER is the first column in sheet containing the full list of entries 48 | All other columns store X's to indicate column contains MASTER entry for that row. 49 | 50 | """ 51 | 52 | def saveMaster (sheet, dataset, ID): 53 | entries = dataset[ID] 54 | print ('Master', ID,'contains', len(entries), 'entries') 55 | sheet.cell (1, 1, ID) 56 | i = 2 57 | for k in entries: 58 | sheet.cell (i, 1, k) 59 | i = i + 1 60 | 61 | def findMasterRow(dataset, val): 62 | indx = 1 63 | for j in dataset['MASTER']: 64 | if val == j: 65 | return indx 66 | indx = indx + 1 67 | return 0 68 | 69 | def saveColumn (sheet, dataset, column, ID): 70 | entries = dataset[ID] 71 | print ('column', ID,'contains', len(entries), 'entries') 72 | sheet.cell (1, column, ID) 73 | for k in entries: 74 | i = findMasterRow (dataset, k) 75 | if i > 0: 76 | sheet.cell (i+1, column, 'X') 77 | 78 | def saveColData (wb, sheetname, dataset): 79 | sheet = wb.create_sheet (sheetname ) 80 | cols = dataset.keys() 81 | colindex = 1 82 | for c in cols: 83 | if colindex == 1: 84 | saveMaster (sheet, dataset, c) 85 | else: 86 | saveColumn (sheet, dataset, colindex, c ) 87 | colindex = colindex + 1 88 | 89 | # Exported interface to store dataset into spreadsheet as column data 90 | 91 | def DumpColsToSpreadsheet(fname, sheetname, dataset ): 92 | wb = openpyxl.Workbook() 93 | saveColData (wb, sheetname, dataset ) 94 | wb.save(filename = fname ) 95 | 96 | """ 97 | Routines to retrieve column data from a spreadsheet 98 | 99 | Data retrieved into defaultdict(list) structure. 100 | Column headings used as defaultdict keys. 101 | MASTER dictionary entry contains full list of entries 102 | X's from spreadsheet converted to corresponding list element from MASTER 103 | (Don't use X's in MASTER list...) 104 | 105 | """ 106 | 107 | def loadMaster (sheet ): 108 | ret = [] 109 | colNum = findColbyKey (sheet, 'MASTER') 110 | if colNum > 0: 111 | for row in sheet.rows: 112 | val = row[colNum-1].value 113 | ret.append (val) 114 | if ret: 115 | del[ret[0]] 116 | return ret 117 | 118 | def findColbyKey (sheet, key): 119 | indx = 1 120 | for c in sheet.columns: 121 | if c[0].value == key: 122 | return indx 123 | indx = indx + 1 124 | return 0 125 | 126 | def loadColumn (sheet, key ): 127 | masterdata = loadMaster (sheet ) 128 | ret = [] 129 | # print ('loading column', key) 130 | colNum = findColbyKey (sheet, key) 131 | if not(colNum > 0): 132 | print ('column', key, 'not found.') 133 | else: 134 | rowcount = 0 135 | for row in sheet.rows: 136 | val = row[colNum-1].value 137 | # print('loading value', val) 138 | rowcount = rowcount + 1 139 | if not(val): 140 | continue 141 | ret.append (masterdata[rowcount-2]) 142 | 143 | if ret: 144 | del[ret[0]] 145 | return ret 146 | 147 | def loadColData (wb, sheetname, dataset): 148 | sheet = wb[sheetname] 149 | for c in sheet.columns: 150 | # print ('loading', c[0].value) 151 | dataset[c[0].value] = loadColumn (sheet, c[0].value ) 152 | 153 | 154 | # Call to load column data from specific spreadsheet (fname, sheetname) 155 | def LoadColsInSpreadsheet(fname, sheetname ): 156 | ret = defaultdict(list ) 157 | wb = openpyxl.load_workbook(fname, data_only=True) 158 | loadColData (wb, sheetname, ret ) 159 | return ret 160 | 161 | def LoadColNames (fname, sheetname): 162 | ret = [] 163 | book = openpyxl.load_workbook(fname, data_only=True) 164 | sheet = book[sheetname] 165 | for row in sheet.rows: 166 | for x in row: 167 | ret.append(x.value) 168 | 169 | del ret[0] 170 | return ret 171 | 172 | 173 | 174 | # main entry point 175 | if ( __name__ == "__main__"): 176 | 177 | fname = '..\data\coltest.xlsx' 178 | 179 | # initialize the test data 180 | initData() 181 | 182 | # create the spreadsheet 183 | DumpColsToSpreadsheet (fname, 'column test', m_DATASET) 184 | 185 | # test the import 186 | coldata = LoadColsInSpreadsheet (fname, 'column test' ) 187 | 188 | print ('End of run') 189 | 190 | -------------------------------------------------------------------------------- /cicat/generator/fastcache.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | :::::::::::::::::::::::: Critical Infrastructure Cyberspace Analysis Tool (CICAT) ::::::::::::::::::::::::::::::::::::::: 4 | 5 | NOTICE 6 | 7 | The contents of this material reflect the views of the author and/or the Director of the Center for Advanced Aviation 8 | System Development (CAASD), and do not necessarily reflect the views of the Federal Aviation Administration (FAA) 9 | or the Department of Transportation (DOT). Neither the FAA nor the DOT makes any warranty or guarantee, or promise, 10 | expressed or implied, concerning the content or accuracy of the views expressed herein. 11 | 12 | This is the copyright work of The MITRE Corporation and was produced for the U.S. Government under Contract Number 13 | DTFAWA-10-C-00080 and is subject to Federal Aviation Administration Acquisition Management System Clause 3.5-13, 14 | Rights in Data-General, Alt. III and Alt. IV (Oct. 1996). No other use other than that granted to the U.S. Government, 15 | or to those acting on behalf of the U.S. Government, under that Clause is authorized without the express written permission 16 | of The MITRE Corporation. For further information, please contact The MITRE Corporation, Contract Office, 7515 Colshire Drive, 17 | McLean, VA 22102 (703) 983-6000. ©2020 The MITRE Corporation. 18 | 19 | The Government retains a nonexclusive, royalty-free right to publish or reproduce this document, or to allow others to do so, for 20 | “Government Purposes Only.” 21 | 22 | (c) 2020 The MITRE Corporation. All Rights Reserved. 23 | 24 | :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: 25 | fastcache.py - Utility to import ATT&CK data from portal and construct a JSON (cache) file 26 | :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: 27 | """ 28 | 29 | from attackcti import attack_client 30 | import json 31 | import os 32 | import sys 33 | 34 | 35 | attack_data = None 36 | 37 | 38 | # main entry point 39 | if ( __name__ == "__main__"): 40 | 41 | cachefilename = 'attack.json' 42 | 43 | params = sys.argv 44 | if len(params) > 1: 45 | cachefilename = params[1].lower() 46 | 47 | try: 48 | with open(os.path.normpath(os.path.join(os.path.dirname(__file__), '..', 'data', 'ATK', cachefilename ))) as data_file: 49 | attack_data = json.load(data_file) 50 | 51 | print ('ATT&CK cache loaded.') 52 | 53 | except FileNotFoundError: 54 | print ('ATT&CK cache', cachefilename, 'not found. Refreshing from STIX/TAXII service.' ) 55 | 56 | ac = attack_client() 57 | attack_data = ac.get_all_stix_objects() 58 | 59 | if not(attack_data): 60 | print ('No ATT&CK data retrieved from STIX//TAXII service.') 61 | exit 62 | 63 | try: 64 | 65 | with open ((os.path.normpath(os.path.join(os.path.dirname(__file__), '..', 'data', 'ATK', cachefilename ))), mode="w+") as outfile: 66 | json.dump(attack_data, outfile) 67 | 68 | print ('ATT&CK cache', cachefilename, 'recreated from STIX/TAXII service.' ) 69 | 70 | except: 71 | print ('Error retrieving data from ATT&CK portal.') 72 | raise 73 | 74 | 75 | print ('end of run') 76 | -------------------------------------------------------------------------------- /cicat/generator/ffactory.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | :::::::::::::::::::::::: Critical Infrastructure Cyberspace Analysis Tool (CICAT) ::::::::::::::::::::::::::::::::::::::: 4 | 5 | NOTICE 6 | 7 | The contents of this material reflect the views of the author and/or the Director of the Center for Advanced Aviation 8 | System Development (CAASD), and do not necessarily reflect the views of the Federal Aviation Administration (FAA) 9 | or the Department of Transportation (DOT). Neither the FAA nor the DOT makes any warranty or guarantee, or promise, 10 | expressed or implied, concerning the content or accuracy of the views expressed herein. 11 | 12 | This is the copyright work of The MITRE Corporation and was produced for the U.S. Government under Contract Number 13 | DTFAWA-10-C-00080 and is subject to Federal Aviation Administration Acquisition Management System Clause 3.5-13, 14 | Rights in Data-General, Alt. III and Alt. IV (Oct. 1996). No other use other than that granted to the U.S. Government, 15 | or to those acting on behalf of the U.S. Government, under that Clause is authorized without the express written permission 16 | of The MITRE Corporation. For further information, please contact The MITRE Corporation, Contract Office, 7515 Colshire Drive, 17 | McLean, VA 22102 (703) 983-6000. ©2020 The MITRE Corporation. 18 | 19 | The Government retains a nonexclusive, royalty-free right to publish or reproduce this document, or to allow others to do so, for 20 | “Government Purposes Only.” 21 | 22 | (c) 2020 The MITRE Corporation. All Rights Reserved. 23 | 24 | :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: 25 | ffactory.py - Factory class for constructing TTP Filter_by class instances 26 | :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: 27 | """ 28 | import sys 29 | import re 30 | from collections import defaultdict 31 | 32 | from loaddata import LOAD_DATA 33 | from loaddata import m_file_TESTBED_MODEL, m_file_TESTBED_SCNRO 34 | 35 | 36 | m_TACTIC_LIST = ['initial-access', 'execution', 'persistence', 'privilege-escalation', 'defense-evasion', 'credential-access', 37 | 'discovery', 'lateral-movement', 'collection', 'command-and-control', 'exfiltration', 38 | 'impact', 'inhibit-response-function', 'impair-process-control' ] 39 | 40 | 41 | m_byTacticDict = defaultdict(list) 42 | m_byPlatformDict = defaultdict(list) 43 | m_byActorDict = defaultdict(list) 44 | m_bySurfaceDict = defaultdict(list) 45 | 46 | class FILTER_FACTORY(): 47 | def __init__ (self, trace ): 48 | self.trace = trace 49 | if self.trace: 50 | print ('FILTER_FACTORY constructed..') 51 | 52 | def getFilter (self, filtertype, tag, dataset ): 53 | 54 | if (filtertype == 'byTactic'): 55 | if m_byTacticDict[tag]: 56 | return m_byTacticDict[tag] 57 | else: 58 | m_byTacticDict[tag] = FILTER_byTactic (tag, dataset, self.trace) # tag is tactic name 59 | return m_byTacticDict[tag] 60 | 61 | elif (filtertype == 'byPlatform'): 62 | if m_byPlatformDict[tag]: 63 | return m_byPlatformDict[tag] 64 | else: 65 | m_byPlatformDict[tag] = FILTER_byPlatform (tag, dataset, self.trace) # tag is platform type 66 | return m_byPlatformDict[tag] 67 | 68 | elif (filtertype == 'byActor'): 69 | if m_byActorDict[tag]: 70 | return m_byActorDict[tag] 71 | else: 72 | m_byActorDict[tag] = FILTER_byActor (tag, dataset, self.trace) # tag is actorname 73 | return m_byActorDict[tag] 74 | 75 | elif (filtertype == 'bySurface'): 76 | if m_bySurfaceDict[tag]: 77 | return m_bySurfaceDict[tag] 78 | else: 79 | m_bySurfaceDict[tag] = FILTER_bySurface (tag, dataset, self.trace) # tag is surfacename 80 | return m_bySurfaceDict[tag] 81 | 82 | else: 83 | if self.trace: 84 | print ('WARNING! FILTER_FACTORY: unrecognized filter:', filtertype ) 85 | 86 | 87 | class BASEFILTER(): 88 | def __init__ (self, tag, dataset, trace): 89 | self.tag = tag 90 | self.dataset = dataset 91 | self.trace = trace 92 | self.ttplist = [] 93 | 94 | def getTTPList(self): 95 | return self.ttplist 96 | 97 | def getTTPSet(self): 98 | return set(self.ttplist) 99 | 100 | # setTTPList() used to initialize the filter for the deny tactic, which is loaded as supplimental TTP data 101 | def setTTPList(self, ttplist): 102 | self.ttplist = ttplist.copy() 103 | 104 | 105 | class FILTER_byTactic (BASEFILTER): 106 | def __init__ (self, tag, dataset, trace ): 107 | BASEFILTER.__init__(self, tag, dataset, trace ) 108 | if self.trace: 109 | print ('byTactic Filter constructed..') 110 | for ttp in dataset: 111 | if not ttp.getTactic(): 112 | if trace: 113 | print ('WARNING! No tactic specified', ttp.getTECHID(), ':', ttp.getName() ) 114 | continue 115 | 116 | if self.tag in ttp.getTactic(): 117 | self.ttplist.append (ttp) 118 | return 119 | 120 | 121 | class FILTER_byPlatform (BASEFILTER): 122 | def __init__(self, tag, dataset, trace): 123 | BASEFILTER.__init__(self, tag, dataset, trace) 124 | if self.trace: 125 | print ('byPlatform Filter constructed..') 126 | for ttp in dataset: 127 | if not ttp.getPlatform(): 128 | if trace: 129 | print ('WARNING! No platform specified', ttp.getTECHID(), ':', ttp.getName() ) 130 | continue 131 | 132 | if self.tag in ttp.getPlatform(): 133 | self.ttplist.append (ttp) 134 | return 135 | 136 | 137 | class FILTER_byActor(BASEFILTER): 138 | def __init__ (self, tag, dataset, trace): 139 | BASEFILTER.__init__(self, tag, dataset, trace) 140 | if self.trace: 141 | print ('byPlatform Filter constructed..') 142 | 143 | for act in dataset: 144 | if self.tag == act.getGroupID(): 145 | self.ttplist = act.getTTPList().copy() 146 | return 147 | 148 | 149 | class FILTER_bySurface(BASEFILTER): 150 | def __init__ (self, tag, dataset, trace): 151 | BASEFILTER.__init__(self, tag, dataset, trace) 152 | if self.trace: 153 | print ('bySurface Filter constructed..') 154 | 155 | p = re.compile(tag, re.IGNORECASE) 156 | for ttp in dataset: 157 | if not(ttp.getDesc()): 158 | if trace: 159 | print ('WARNING! TTP has no description', ttp.getTECHID(), ':', ttp.getName() ) 160 | continue 161 | 162 | if p.search (ttp.getDesc().casefold() ): 163 | self.ttplist.append (ttp) 164 | 165 | return 166 | 167 | 168 | def getTTPs(ffactory, dataset, tactic, platform, actor): 169 | 170 | if not(tactic): 171 | return 172 | 173 | rset = set() 174 | l1 = ffactory.getFilter('byTactic', tactic, dataset['ATT&CK'] + dataset['ATK4ICS TTPs']).getTTPSet() 175 | rset = l1 176 | 177 | if platform: 178 | l2 = ffactory.getFilter('byPlatform', platform, dataset['ATT&CK'] + dataset['ATK4ICS TTPs'] ).getTTPSet() 179 | rset = rset & l2 180 | 181 | if not(tactic == 'deny') and actor: 182 | l3 = ffactory.getFilter('byActor', actor, dataset['ATKGROUPS'] ).getTTPSet() 183 | rset = rset & l3 184 | 185 | return list(rset) 186 | 187 | 188 | def getTTPsforCTYPE(ffactory, dataset, ctypeID): 189 | 190 | slist = [] 191 | for c in dataset['CTYPE']: 192 | if c.getID() == ctypeID: 193 | slist = c.getSurfaceList() 194 | break 195 | 196 | if not(slist): 197 | return 198 | 199 | fullttplist = dataset['ATT&CK'] + dataset['TTP_SUP'] 200 | 201 | rset = set() 202 | for surf in slist: 203 | sset = ffactory.getFilter('bySurface', surf.getSurface(), fullttplist ).getTTPSet() 204 | rset = rset | sset 205 | 206 | return list(rset) 207 | 208 | def showTTPs (title, ttplist): 209 | if ttplist: 210 | print ('\n' + title, '(' + str(len(ttplist)), 'entries)') 211 | for t in ttplist: 212 | print (t.getTECHID(), ':', t.getName()) 213 | else: 214 | print ('\n' + title, '(no entries)') 215 | 216 | 217 | def INIT_FILTERS (ffactory, dataset): 218 | 219 | # build tactic and platform and surface filters to include ATT&CK and the TTP_SUP set of TTPs 220 | fullttplist = dataset['ATT&CK'] + dataset['ATK4ICS TTPs'] 221 | 222 | for t in m_TACTIC_LIST: 223 | ffactory.getFilter('byTactic', t, fullttplist ) 224 | 225 | for p in ['Windows', 'Linux', 'macOS', 'ICS']: 226 | ffactory.getFilter('byPlatform', p, fullttplist ) 227 | 228 | for surf in dataset['SURFACE']: 229 | ffactory.getFilter('bySurface', surf.getSurface(), fullttplist ) 230 | 231 | for actor in dataset['ATKGROUPS']: 232 | ffactory.getFilter ('byActor', actor.getGroupID(), dataset['ATKGROUPS'] ) 233 | 234 | return 235 | 236 | 237 | def FILTER_TEST1 (ffactory, dataset, platlist): 238 | print ('\n' ) 239 | print ('>> Platform Filter Tests <<') 240 | 241 | for t in m_TACTIC_LIST: 242 | showTTPs ('FILTER TEST1: '+ t + ', none, ---', getTTPs(ffactory, dataset, t, '', '') ) 243 | for p in platlist: 244 | showTTPs ('FILTER TEST1: ' + t + ', ' + p + ', ---', getTTPs(ffactory, dataset, t, p, '') ) 245 | 246 | def FILTER_TEST2 (ffactory, dataset, actorlist): 247 | print ('\n' ) 248 | print ('>> Threat Actor Profile Tests <<') 249 | 250 | for a in actorlist: 251 | for t in m_TACTIC_LIST: 252 | showTTPs ('FILTER TEST2: ' + t + ', Linux, ' + a, getTTPs(ffactory, dataset, t, 'Linux', a ) ) 253 | showTTPs ('FILTER TEST2: ' + t + ', Windows, ' + a, getTTPs(ffactory, dataset, t, 'Windows', a ) ) 254 | showTTPs ('FILTER TEST2: ' + t + ', ICS, ' + a, getTTPs(ffactory, dataset, t, 'ICS', a ) ) 255 | 256 | 257 | def FILTER_TEST3 (ffactory, dataset): 258 | print ('\n' ) 259 | print ('>> Attack Surfaces Frequency Distribution <<') 260 | print ('\n' ) 261 | 262 | slist = [] 263 | for k in m_bySurfaceDict.keys(): 264 | filtr = m_bySurfaceDict[k] 265 | slist.append ([len(filtr.getTTPList() ), k] ) 266 | 267 | for l in sorted (slist, reverse=True): 268 | print (str(l[0]), 'TTPs contain tag:', l[1]) 269 | 270 | return 271 | 272 | def FILTER_TEST4 (ffactor, dataset, ctypelist): 273 | print ('\n' ) 274 | print ('>> Attack Surface Filter Tests <<') 275 | 276 | 277 | for s in ctypelist: 278 | surflist = [] 279 | for x in dataset['SURFACE']: 280 | if x.getCTYPE() == s: 281 | surflist.append(x.getSurface()) 282 | 283 | print ('\nSurfaces list for ' + s + ' (' + str(len(surflist)) + ' TTPs):', surflist ) 284 | showTTPs ('Maps to', getTTPsforCTYPE(ffactory, dataset, s) ) 285 | 286 | 287 | 288 | def FILTER_TEST5 (ffactory, dataset): 289 | 290 | ttplist = [] 291 | for k in m_bySurfaceDict.keys(): 292 | filtr = m_bySurfaceDict[k] 293 | flist = filtr.getTTPList() 294 | for f in flist: 295 | ttplist.append( f.getTECHID() ) 296 | 297 | ttpset = set(ttplist) 298 | 299 | allist = [] 300 | for q in dataset['ATT&CK']: 301 | allist.append(q.getTECHID()) 302 | 303 | allset = set (allist) 304 | 305 | diffset = allset - ttpset 306 | difflist = list(diffset) 307 | 308 | print ('\n' ) 309 | print ('>> Surface Filter Exclusion Test <<') 310 | 311 | print ('There are', len(difflist), 'TTPs not referenced by keyword:') 312 | 313 | for d in difflist: 314 | for a in dataset['ATT&CK']: 315 | if a.getTECHID() == d: 316 | if a.getPlatform(): 317 | print (a.getTECHID(), ':', a.getName(), 'Tactic:', a.getTactic(), 'Platform:', a.getPlatform(), 'URL:', a.getURL() ) 318 | break 319 | 320 | return 321 | 322 | 323 | # Helper function for reading options from command line 324 | def optionReader(params, flag): 325 | idx = params.index(flag) 326 | if len(params) > idx + 1 and '-' not in params[idx + 1]: 327 | return params[idx + 1] 328 | else: 329 | print(flag + ' flag must include an option!') 330 | exit() 331 | 332 | 333 | # main entry point 334 | if ( __name__ == "__main__"): 335 | 336 | Ispread = m_file_TESTBED_MODEL 337 | Tspread = m_file_TESTBED_SCNRO #testbed data used for unit tests 338 | 339 | params = sys.argv 340 | if len(params) > 1: 341 | if 'help' in params[1].lower(): 342 | print ('\nUSAGE: python', params[0], '[-i ] [-s ]') 343 | exit() 344 | 345 | if '-i' in params: 346 | Ispread = optionReader(params, '-i') 347 | 348 | if '-s' in params: 349 | Tspread = optionReader(params, '-s') 350 | 351 | myDATASET = LOAD_DATA (Ispread, Tspread, False, False ) 352 | ffactory = FILTER_FACTORY(False ) 353 | INIT_FILTERS (ffactory, myDATASET) 354 | 355 | FILTER_TEST1 (ffactory, myDATASET, ['Windows', 'Linux', 'ICS'] ) 356 | FILTER_TEST2 (ffactory, myDATASET, ['APT28', 'IS01']) 357 | FILTER_TEST3 (ffactory, myDATASET) 358 | FILTER_TEST4 (ffactory, myDATASET, ['S0001', 'S0002', 'S0003', 'S0004'] ) 359 | FILTER_TEST5 (ffactory, myDATASET) 360 | 361 | print ('End of run') 362 | 363 | -------------------------------------------------------------------------------- /cicat/generator/ifactory.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | :::::::::::::::::::::::: Critical Infrastructure Cyberspace Analysis Tool (CICAT) ::::::::::::::::::::::::::::::::::::::: 4 | 5 | NOTICE 6 | 7 | The contents of this material reflect the views of the author and/or the Director of the Center for Advanced Aviation 8 | System Development (CAASD), and do not necessarily reflect the views of the Federal Aviation Administration (FAA) 9 | or the Department of Transportation (DOT). Neither the FAA nor the DOT makes any warranty or guarantee, or promise, 10 | expressed or implied, concerning the content or accuracy of the views expressed herein. 11 | 12 | This is the copyright work of The MITRE Corporation and was produced for the U.S. Government under Contract Number 13 | DTFAWA-10-C-00080 and is subject to Federal Aviation Administration Acquisition Management System Clause 3.5-13, 14 | Rights in Data-General, Alt. III and Alt. IV (Oct. 1996). No other use other than that granted to the U.S. Government, 15 | or to those acting on behalf of the U.S. Government, under that Clause is authorized without the express written permission 16 | of The MITRE Corporation. For further information, please contact The MITRE Corporation, Contract Office, 7515 Colshire Drive, 17 | McLean, VA 22102 (703) 983-6000. ©2020 The MITRE Corporation. 18 | 19 | The Government retains a nonexclusive, royalty-free right to publish or reproduce this document, or to allow others to do so, for 20 | “Government Purposes Only.” 21 | 22 | (c) 2020 The MITRE Corporation. All Rights Reserved. 23 | 24 | :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: 25 | ifactory.py - factory class for infrastructure data 26 | :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: 27 | """ 28 | 29 | 30 | import openpyxl 31 | 32 | from imodel import COMPONENT 33 | from imodel import CTYPE 34 | from imodel import SYSTEM 35 | from imodel import FSMAP 36 | from imodel import FX 37 | from imodel import LOCATION 38 | from imodel import CAPABILITY 39 | from imodel import SURFACE 40 | from imodel import CONNECTION 41 | from columnloader import LoadColsInSpreadsheet 42 | 43 | class CI_FACTORY(): 44 | def __init__ (self, filename, trace ): 45 | self.filename = filename 46 | self.trace = trace 47 | if self.trace: 48 | print ('CI_FACTORY constructed..') 49 | 50 | def getLoader (self, sheetname ): 51 | 52 | if (sheetname == 'CTYPE'): 53 | return CTYPE_FACTORY (self.filename, sheetname, self.trace) 54 | elif (sheetname == 'COMPONENT'): 55 | return COMPONENT_FACTORY(self.filename, sheetname, self.trace) 56 | elif (sheetname == 'CONNECTION'): 57 | return CONNECTION_FACTORY (self.filename, sheetname, self.trace ) 58 | elif (sheetname == 'SYSTEM'): 59 | return SYSTEM_FACTORY(self.filename, sheetname, self.trace) 60 | elif (sheetname == 'FSMAP'): 61 | return FSMAP_FACTORY(self.filename, sheetname, self.trace) 62 | elif (sheetname == 'FUNCTION'): 63 | return FX_FACTORY (self.filename, sheetname, self.trace) 64 | elif (sheetname == 'LOCATION'): 65 | return LOCATION_FACTORY(self.filename, sheetname, self.trace ) 66 | elif (sheetname == 'CAPABILITY'): 67 | return CAPABILITY_FACTORY(self.filename, sheetname, self.trace ) 68 | elif (sheetname == 'SURFACE'): 69 | return SURFACE_FACTORY (self.filename, sheetname, self.trace ) 70 | else: 71 | if self.trace: 72 | print ('WARNING! CI_FACTORY: unrecognized object:', sheetname ) 73 | 74 | def initRelationships(self, myDATAWARE): 75 | if self.trace: 76 | print ('CI_FACTORY loading relationships..') 77 | 78 | if self.trace: 79 | print ('Link_2: Systems to Components' ) 80 | for c in myDATAWARE['COMPONENT']: 81 | c.link_2 (myDATAWARE['SYSTEM'], self.trace) 82 | 83 | if self.trace: 84 | print ('Link_3: Components to Systems') 85 | for r in myDATAWARE['SYSTEM']: 86 | r.link_3 (myDATAWARE['COMPONENT'], self.trace) 87 | 88 | if self.trace: 89 | print ('Mapping Component Types to Controlled Interfaces') 90 | for r in myDATAWARE['CONNECTION']: 91 | r.mapCTYPE(myDATAWARE['CTYPE']) 92 | 93 | if self.trace: 94 | print ('Link_4: Attack Surfaces to CTYPEs') 95 | for q in myDATAWARE['CTYPE']: 96 | q.link_4 (myDATAWARE['SURFACE'], self.trace) 97 | 98 | if self.trace: 99 | print ('Link_5: Functions to Systems using the FSMAP' ) 100 | for r in myDATAWARE['SYSTEM']: 101 | r.link_5 (myDATAWARE['FSMAP'], myDATAWARE['FUNCTION'], self.trace ) 102 | 103 | if self.trace: 104 | print ('Link_6: Systems to Functions') 105 | for s in myDATAWARE['FUNCTION']: 106 | s.link_6 (myDATAWARE['SYSTEM'], self.trace ) 107 | 108 | if self.trace: 109 | print ('Link_7: Systems to Locations') 110 | for l in myDATAWARE['LOCATION']: 111 | l.link_7 (myDATAWARE['SYSTEM'], self.trace ) 112 | 113 | if self.trace: 114 | print ('Link_8: Capabilities to Functions') 115 | for s in myDATAWARE['FUNCTION']: 116 | s.link_8 (myDATAWARE['CAPABILITY'], self.trace) 117 | 118 | if self.trace: 119 | print ('Link_9: Functions to Capabilities') 120 | for p in myDATAWARE['CAPABILITY']: 121 | p.link_9 (myDATAWARE['FUNCTION'], self.trace ) 122 | 123 | if self.trace: 124 | print ('CI_FACTORY relationships loaded.') 125 | 126 | class COMPONENT_FACTORY(CI_FACTORY): 127 | def __init__ (self, filename, sheetname, trace): 128 | self.filename = filename 129 | self.sheetname = sheetname 130 | self.trace = trace 131 | if self.trace: 132 | print ('INDICATOR factory constructed..') 133 | return 134 | 135 | def findctype(self, ctID, ctArray): 136 | for j in ctArray: 137 | if ctID == j.getID(): 138 | return j 139 | 140 | def load (self, ctypeList ): 141 | if self.trace: 142 | print ('Loading COMPONENT data..') 143 | book = openpyxl.load_workbook(self.filename, data_only=True) 144 | sheet = book[self.sheetname] 145 | ret = [] 146 | for row in sheet.rows: 147 | ctype = self.findctype(row[1].value, ctypeList ) 148 | ret.append(COMPONENT (row[0].value, ctype, row[2].value, row[3].value, row[4].value, row[5].value )) 149 | 150 | del ret[0] 151 | return ret 152 | 153 | 154 | class CTYPE_FACTORY(CI_FACTORY): 155 | def __init__ (self, filename, sheetname, trace): 156 | self.filename = filename 157 | self.sheetname = sheetname 158 | self.trace = trace 159 | if self.trace: 160 | print ('CTYPE factory constructed..') 161 | return 162 | 163 | def load (self): 164 | if self.trace: 165 | print ('Loading CTYPE data..') 166 | book = openpyxl.load_workbook(self.filename, data_only=True) 167 | sheet = book[self.sheetname] 168 | ret = [] 169 | for row in sheet.rows: 170 | ret.append(CTYPE (row[0].value, row[1].value, row[2].value, row[3].value, row[4].value )) 171 | 172 | del ret[0] 173 | return ret 174 | 175 | class SYSTEM_FACTORY(CI_FACTORY): 176 | def __init__ (self, filename, sheetname, trace): 177 | self.filename = filename 178 | self.sheetname = sheetname 179 | self.trace = trace 180 | if self.trace: 181 | print ('SYSTEM factory constructed..') 182 | return 183 | 184 | def load (self): 185 | if self.trace: 186 | print ('Loading SYSTEM data..') 187 | book = openpyxl.load_workbook(self.filename, data_only=True) 188 | sheet = book[self.sheetname] 189 | ret = [] 190 | for row in sheet.rows: 191 | ret.append(SYSTEM (row[0].value, row[1].value, row[2].value, row[3].value, row[4].value )) 192 | 193 | del ret[0] 194 | return ret 195 | 196 | class FSMAP_FACTORY(CI_FACTORY): 197 | def __init__ (self, filename, sheetname, trace): 198 | self.filename = filename 199 | self.sheetname = sheetname 200 | self.trace = trace 201 | if self.trace: 202 | print ('FSMAP factory constructed..') 203 | return 204 | 205 | def load (self): 206 | if self.trace: 207 | print ('Loading FSMAP data..') 208 | book = openpyxl.load_workbook(self.filename, data_only=True) 209 | sheet = book[self.sheetname] 210 | ret = [] 211 | for row in sheet.rows: 212 | ret.append(FSMAP (row[0].value, row[1].value )) 213 | 214 | del ret[0] 215 | return ret 216 | 217 | class CONNECTION_FACTORY(CI_FACTORY): 218 | def __init__ (self, filename, sheetname, trace): 219 | self.filename = filename 220 | self.sheetname = sheetname 221 | self.trace = trace 222 | if self.trace: 223 | print ('CONNECTION factory constructed..') 224 | return 225 | 226 | def load (self): 227 | if self.trace: 228 | print ('Loading CONNECTION data..') 229 | book = openpyxl.load_workbook(self.filename, data_only=True) 230 | sheet = book[self.sheetname] 231 | ret = [] 232 | cnt = 0 233 | for row in sheet.rows: 234 | entry = CONNECTION (cnt, row[0].value, row[1].value, row[2].value, 235 | row[3].value, row[4].value, row[5].value ) 236 | cnt = cnt+1 237 | ret.append (entry) 238 | 239 | # create second entry for reverse direction if not a oneway flow 240 | if not (entry.isOneway()): 241 | entry = CONNECTION (cnt, row[1].value, row[0].value, row[2].value, 242 | row[3].value, row[4].value, row[5].value ) 243 | cnt = cnt+1 244 | ret.append (entry) 245 | 246 | del ret[0] 247 | del ret[0] 248 | return ret 249 | 250 | 251 | class LOCATION_FACTORY(CI_FACTORY): 252 | def __init__ (self, filename, sheetname, trace): 253 | self.filename = filename 254 | self.sheetname = sheetname 255 | self.trace = trace 256 | if self.trace: 257 | print ('LOCATION factory constructed..') 258 | return 259 | 260 | def load (self): 261 | if self.trace: 262 | print ('Loading LOCATION data..') 263 | book = openpyxl.load_workbook(self.filename, data_only=True) 264 | sheet = book[self.sheetname] 265 | ret = [] 266 | for row in sheet.rows: 267 | ret.append(LOCATION (row[0].value, row[1].value, row[2].value, row[3].value, row[4].value )) 268 | 269 | del ret[0] 270 | return ret 271 | 272 | class FX_FACTORY(CI_FACTORY): 273 | def __init__ (self, filename, sheetname, trace): 274 | self.filename = filename 275 | self.sheetname = sheetname 276 | self.trace = trace 277 | if self.trace: 278 | print ('FX factory constructed..') 279 | return 280 | 281 | def load (self): 282 | if self.trace: 283 | print ('Loading FX data..') 284 | book = openpyxl.load_workbook(self.filename, data_only=True) 285 | sheet = book[self.sheetname] 286 | ret = [] 287 | for row in sheet.rows: 288 | ret.append(FX (row[0].value, row[1].value, row[2].value, row[3].value )) 289 | 290 | del ret[0] 291 | return ret 292 | 293 | class CAPABILITY_FACTORY(CI_FACTORY): 294 | def __init__ (self, filename, sheetname, trace): 295 | self.filename = filename 296 | self.sheetname = sheetname 297 | self.trace = trace 298 | if self.trace: 299 | print ('CAPABILITY factory constructed..') 300 | return 301 | 302 | def load (self): 303 | if self.trace: 304 | print ('Loading CAPABILITY data..') 305 | book = openpyxl.load_workbook(self.filename, data_only=True) 306 | sheet = book[self.sheetname] 307 | ret = [] 308 | for row in sheet.rows: 309 | ret.append(CAPABILITY (row[0].value, row[1].value, row[2].value )) 310 | 311 | del ret[0] 312 | return ret 313 | 314 | class SURFACE_FACTORY(CI_FACTORY): 315 | def __init__ (self, filename, sheetname, trace): 316 | self.filename = filename 317 | self.sheetname = sheetname 318 | self.trace = trace 319 | if self.trace: 320 | print ('SURFACE factory constructed..') 321 | return 322 | 323 | def load (self): 324 | if self.trace: 325 | print ('Loading SURFACE data..') 326 | 327 | ret = [] 328 | surfacedict = LoadColsInSpreadsheet (self.filename, self.sheetname ) 329 | for s in surfacedict.keys(): 330 | if (s == 'MASTER') or (s == 'deny tag'): 331 | continue 332 | for l in surfacedict[s]: 333 | ret.append (SURFACE(s, l, 'Easy' )) 334 | 335 | return ret 336 | -------------------------------------------------------------------------------- /cicat/generator/loaddata.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | :::::::::::::::::::::::: Critical Infrastructure Cyberspace Analysis Tool (CICAT) ::::::::::::::::::::::::::::::::::::::: 4 | 5 | NOTICE 6 | 7 | The contents of this material reflect the views of the author and/or the Director of the Center for Advanced Aviation 8 | System Development (CAASD), and do not necessarily reflect the views of the Federal Aviation Administration (FAA) 9 | or the Department of Transportation (DOT). Neither the FAA nor the DOT makes any warranty or guarantee, or promise, 10 | expressed or implied, concerning the content or accuracy of the views expressed herein. 11 | 12 | This is the copyright work of The MITRE Corporation and was produced for the U.S. Government under Contract Number 13 | DTFAWA-10-C-00080 and is subject to Federal Aviation Administration Acquisition Management System Clause 3.5-13, 14 | Rights in Data-General, Alt. III and Alt. IV (Oct. 1996). No other use other than that granted to the U.S. Government, 15 | or to those acting on behalf of the U.S. Government, under that Clause is authorized without the express written permission 16 | of The MITRE Corporation. For further information, please contact The MITRE Corporation, Contract Office, 7515 Colshire Drive, 17 | McLean, VA 22102 (703) 983-6000. ©2020 The MITRE Corporation. 18 | 19 | The Government retains a nonexclusive, royalty-free right to publish or reproduce this document, or to allow others to do so, for 20 | “Government Purposes Only.” 21 | 22 | (c) 2020 The MITRE Corporation. All Rights Reserved. 23 | 24 | :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: 25 | loaddata.py - Master data loader 26 | :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: 27 | """ 28 | 29 | from collections import defaultdict 30 | from afactory import ATTACK_FACTORY 31 | from ifactory import CI_FACTORY 32 | from tfactory import THREAT_FACTORY 33 | from vfactory import VULNERABILIY_FACTORY 34 | from stats import sortSystemsbyFunction 35 | import os 36 | 37 | m_CVEFiles = [] 38 | for cve in os.listdir(os.path.normpath(os.path.join(os.path.dirname(__file__), "..", "data", "CVE"))): 39 | m_CVEFiles.append(os.path.normpath(os.path.join(os.path.dirname(__file__), "..", "data", "CVE", cve))) 40 | 41 | m_file_INFRASTRUCTURE = os.path.normpath(os.path.join(os.path.dirname(__file__), "..", "data", "INFRASTRUCTURE.xlsx")) 42 | m_file_SCENARIOS = os.path.normpath(os.path.join(os.path.dirname(__file__), "..", "data", "SCENARIOS.xlsx")) 43 | m_file_OSPREAD = os.path.normpath(os.path.join(os.path.dirname(__file__), "..", "data", "OUTPUT.xlsx")) 44 | m_file_ESPREAD = os.path.normpath(os.path.join(os.path.dirname(__file__), "..", "data", "EXPORT.xlsx")) 45 | 46 | m_file_EXTENSIONS = os.path.normpath(os.path.join(os.path.dirname(__file__), "..", "data", "ATTACK_EXTENSIONS.xlsx")) 47 | 48 | m_file_ATK4ICS = os.path.normpath(os.path.join(os.path.dirname(__file__), "..", "data", "ATK4ICS.xlsx")) 49 | 50 | m_file_TESTBED_MODEL = os.path.normpath(os.path.join(os.path.dirname(__file__), "..", "data", "TB_infrastructure.xlsx")) 51 | m_file_TESTBED_SCNRO = os.path.normpath(os.path.join(os.path.dirname(__file__), "..", "data", "TB_scenarios.xlsx")) 52 | 53 | # variables used for unit testing found in tb_infrstructure and tb_scenarios spreadsheets 54 | m_IT_test_list = ['ASSET_111', 'ASSET_711', 'ASSET_971', 'ASSET_811'] 55 | m_ICS_test_list = ['ASSET_111', 'ASSET_711', 'ASSET_971', 'ASSET_1101'] 56 | m_filter_test_list = ['ASSET_111', 'ASSET_711', 'ASSET_811', 'ASSET_1101'] 57 | m_test_actor_list = ['APT28', 'APT1', 'IS01'] 58 | 59 | def LOAD_DATA (infraSpread, threatSpread, CVEflag, trace): 60 | ret = defaultdict(list) 61 | 62 | CI_spreadsheet = infraSpread 63 | print('\nloaddata: Loading infrastructure data from', CI_spreadsheet) 64 | 65 | # load infrastructure model data from spreadsheet 66 | ciFACTORY = CI_FACTORY(CI_spreadsheet, trace) 67 | ret['CAPABILITY'] = ciFACTORY.getLoader('CAPABILITY').load() 68 | ret['FUNCTION'] = ciFACTORY.getLoader('FUNCTION').load() 69 | ret['SYSTEM'] = ciFACTORY.getLoader('SYSTEM').load() 70 | ret['FSMAP'] = ciFACTORY.getLoader('FSMAP').load() 71 | ret['LOCATION'] = ciFACTORY.getLoader ('LOCATION').load() 72 | ret['CTYPE'] = ciFACTORY.getLoader('CTYPE').load() 73 | ret['COMPONENT'] = ciFACTORY.getLoader('COMPONENT').load(ret['CTYPE']) 74 | ret['SURFACE'] = ciFACTORY.getLoader('SURFACE').load() 75 | ret['CONNECTION'] = ciFACTORY.getLoader('CONNECTION').load() 76 | 77 | # if CVEflag, load CVE data from list of CVE files 78 | 79 | if CVEflag: 80 | print('Loading CVE data from', m_CVEFiles) 81 | vlnFACTORY = VULNERABILIY_FACTORY(True) 82 | ret['VULNERABILITY'] = vlnFACTORY.load(m_CVEFiles, ret['CTYPE']) 83 | 84 | # Initialize infrastructure relationships [requires vulnerability data be preloaded] 85 | ciFACTORY.initRelationships(ret ) 86 | 87 | for c in ret['COMPONENT']: 88 | c.getAccessibility() 89 | c.getSusceptibility() 90 | 91 | # Initalize infrastructure criticality data 92 | for j in ret['FUNCTION']: 93 | j.assertCriticality(trace) 94 | 95 | for x in ret['SYSTEM']: 96 | x.assertCriticality(trace) 97 | 98 | loadOpt = 'JSON' # 'STIX' 'SPREAD' 99 | loadFile = os.path.normpath(os.path.join(os.path.dirname(__file__), '..', 'data', 'ATTACK.xlsx')) 100 | if loadOpt == 'SPREAD': 101 | print('Loading ATT&CK data from spreadsheet', loadFile) 102 | elif loadOpt == 'JSON': 103 | print('Loading ATT&CK data from local JSON file') 104 | elif loadOpt == 'STIX': 105 | print('Loading ATT&CK data from STIX//TAXII service') 106 | 107 | # Load ATT&CK data from spreadsheet 108 | atkFACTORY = ATTACK_FACTORY(loadOpt, loadFile, trace) 109 | ret['ATKGROUPS'] = atkFACTORY.loadGroups() 110 | ret['ATKMALWARE'] = atkFACTORY.loadMalwares() 111 | ret['ATT&CK'] = atkFACTORY.loadTechniques() 112 | ret['ATKMITIGATION'] = atkFACTORY.loadMitigations() 113 | ret['ATKTOOL'] = atkFACTORY.loadTools() 114 | ret['ATKRELS'] = atkFACTORY.initRelationships(ret) 115 | 116 | 117 | # Load ATT&CK4ICS 118 | ret['ATK4ICS TTPs'] = atkFACTORY.loadTechniquesFromSheet (m_file_ATK4ICS, 'ATK4ICS TTPs') 119 | ret['ATK4ICS MITs'] = atkFACTORY.loadMitigationsFromSheet (m_file_ATK4ICS, 'ATK4ICS MITs') 120 | 121 | # link TTPs with associated MITs and vice versa 122 | for t in ret['ATK4ICS TTPs']: 123 | for m in ret['ATK4ICS MITs']: 124 | if t.getTECHID() == m.getTECHID(): 125 | t.addCOA(m, None) 126 | m.addMitigates(t, None) 127 | break 128 | 129 | # Loading scerario spreadsheet data 130 | print('loaddata: Loading Scenario data from', threatSpread) 131 | 132 | # Load Threat Actor profiles 133 | profilist = atkFACTORY.loadProfileNames(threatSpread) 134 | for p in profilist: 135 | actor = atkFACTORY.loadGroupProfile (ret, threatSpread, p) 136 | ret['ATKGROUPS'].append (actor) 137 | 138 | # Calculated sophistication metric for each threat actor 139 | for a in ret['ATKGROUPS']: 140 | a.getSophisticationLevel() 141 | 142 | trFACTORY = THREAT_FACTORY(threatSpread, trace) 143 | ret['TARGET'] = trFACTORY.getLoader('TARGET').load() 144 | ret['ENTRYPOINT'] = trFACTORY.getLoader('ENTRYPOINT').load() 145 | ret['SCENARIO'] = trFACTORY.getLoader('SCENARIO').load() 146 | 147 | # Load H800-53 controls 148 | # ret['COA'] = trFACTORY.getLoader('COA').load(os.path.normpath(os.path.join(os.path.dirname(__file__), '..', 'data', '800-53', 'controls.2.xlsx'))) 149 | 150 | for t in ret['TARGET']: 151 | t.initTarget (ret['COMPONENT']) 152 | 153 | for e in ret['ENTRYPOINT']: 154 | e.initEntrypoint (ret['COMPONENT']) 155 | 156 | trFACTORY.initRelationships(ret) 157 | 158 | return ret 159 | 160 | """ 161 | def LOAD_TTP_EXTENSION (dataset, fname=m_file_EXTENSIONS, sheetname='TTP_EXT'): 162 | atkFACTORY = ATTACK_FACTORY('SPREAD', fname, False) 163 | atkFACTORY.loadTTPExtension(dataset, fname, sheetname) 164 | """ 165 | 166 | def LOAD_TTP_SUPPLEMENT (dataset, fname=m_file_EXTENSIONS, sheetname='TTP_SUP'): 167 | atkFACTORY = ATTACK_FACTORY('SPREAD', fname, False) 168 | dataset[sheetname] = atkFACTORY.loadTechniquesFromSheet(fname, sheetname) 169 | 170 | # main entry point 171 | if ( __name__ == "__main__"): 172 | 173 | mydataset = LOAD_DATA(m_file_TESTBED_MODEL, m_file_TESTBED_SCNRO, True, False) 174 | sysdeplist = sortSystemsbyFunction(mydataset) 175 | 176 | print('End of run') 177 | 178 | -------------------------------------------------------------------------------- /cicat/generator/mitGEN.py: -------------------------------------------------------------------------------- 1 | 2 | """ 3 | :::::::::::::::::::::::: Critical Infrastructure Cyberspace Analysis Tool (CICAT) ::::::::::::::::::::::::::::::::::::::: 4 | 5 | NOTICE 6 | 7 | The contents of this material reflect the views of the author and/or the Director of the Center for Advanced Aviation 8 | System Development (CAASD), and do not necessarily reflect the views of the Federal Aviation Administration (FAA) 9 | or the Department of Transportation (DOT). Neither the FAA nor the DOT makes any warranty or guarantee, or promise, 10 | expressed or implied, concerning the content or accuracy of the views expressed herein. 11 | 12 | This is the copyright work of The MITRE Corporation and was produced for the U.S. Government under Contract Number 13 | DTFAWA-10-C-00080 and is subject to Federal Aviation Administration Acquisition Management System Clause 3.5-13, 14 | Rights in Data-General, Alt. III and Alt. IV (Oct. 1996). No other use other than that granted to the U.S. Government, 15 | or to those acting on behalf of the U.S. Government, under that Clause is authorized without the express written permission 16 | of The MITRE Corporation. For further information, please contact The MITRE Corporation, Contract Office, 7515 Colshire Drive, 17 | McLean, VA 22102 (703) 983-6000. ©2020 The MITRE Corporation. 18 | 19 | The Government retains a nonexclusive, royalty-free right to publish or reproduce this document, or to allow others to do so, for 20 | “Government Purposes Only.” 21 | 22 | (c) 2020 The MITRE Corporation. All Rights Reserved. 23 | 24 | :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: 25 | mitGEN.py - Report generation routines 26 | :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: 27 | """ 28 | 29 | #from collections import defaultdict 30 | from stats import sortByEntrypointSuccesses, sortByTTPUsage, sortByVisitedNode, sortActorsBySophistication, countTTPsByTactic 31 | 32 | 33 | def showNode (dataset, ipaddr, bTarget, verbose ): 34 | for j in dataset['COMPONENT']: 35 | if ipaddr == j.getIPAddress(): 36 | if verbose: 37 | j.PP(bTarget, True ) 38 | else: 39 | j.PP(bTarget, False ) 40 | 41 | return 42 | 43 | def showTechnique(ttpobj, verbose=True): 44 | print ('\n'+ttpobj.getTECHID()+' : '+ttpobj.getName()) 45 | print ('URL:', ttpobj.getURL() ) 46 | print ('Tactic(s):', ttpobj.getTactic() ) 47 | if verbose: 48 | print ('Details:', ttpobj.getDesc().encode('utf8').decode('utf8')) 49 | else: 50 | print ('Tag(s):', ttpobj.getTags()) 51 | return 52 | 53 | def showMitigation (ttpobj ): 54 | mitlist = ttpobj.getCOA() 55 | for m in mitlist: 56 | print ('\n'+m[0].getTECHID()+' : '+m[0].getName() ) 57 | print ('Mitigation:', m[0].getDesc().encode('utf8').decode('utf8') ) 58 | return 59 | 60 | def showDetection (ttpobj ): 61 | print ('\n'+ttpobj.getTECHID()+' : '+ttpobj.getName() ) 62 | if ttpobj.getDET(): 63 | print ('Forensics:', ttpobj.getDET().encode('utf8').decode('utf8') ) 64 | else: 65 | print ('No forensic markers identified.') 66 | return 67 | 68 | def showActor (dataset, Aname, verbose): 69 | for j in dataset['ATKGROUPS']: 70 | if j.getName() == Aname: 71 | print ('\nGroup ID:', j.getGroupID(), 'Name:', j.getName(), 'Sophistication:', j.getSophisticationLevel() ) 72 | if not(verbose): 73 | print ('URL:', j.getURL() ) 74 | else: 75 | print ('Description:', j.getDesc().encode('utf8').decode('utf8') ) 76 | print ('Aliases:', j.getAliases() ) 77 | return 78 | 79 | def GENERATE_MITIGATIONS (dataset, stats, mode): 80 | 81 | ret = sortByEntrypointSuccesses(stats) 82 | if ret: 83 | print ('\nTop 10 most successful entrypoints:') 84 | count = 0 85 | for j in ret: 86 | showNode(dataset, j) 87 | count = count + 1 88 | if count > 10: 89 | break 90 | 91 | ret = sortByVisitedNode (stats) 92 | if ret: 93 | print ('\nTop 10 most frequented IPs across all scenarios:') 94 | count = 0 95 | for j in ret: 96 | showNode (dataset, j) 97 | count = count + 1 98 | if count > 10: 99 | break 100 | 101 | ret = sortByTTPUsage(stats) 102 | if ret: 103 | print ('\nTop 10 most used ATT&CK TTPs:') 104 | count = 0 105 | for j in ret: 106 | showTechnique(dataset, j) 107 | showMitigation(dataset, j) 108 | count = count + 1 109 | if count > 10: 110 | break 111 | 112 | 113 | def getTTP (dataset, ttpid): 114 | for j in dataset['ATT&CK']: 115 | if ttpid == j.getTECHID(): 116 | return j 117 | for j in dataset['ATK4ICS TTPs']: 118 | if ttpid == j.getTECHID(): 119 | return j 120 | return None 121 | 122 | def SCENARIO_REPORT(dataset, stats, flagslist): 123 | print ('\n>>> SCENARIO REPORT <<<') 124 | for entry in stats.keys(): 125 | SCENARIO_ENTRY (dataset, stats[entry][0], flagslist) 126 | 127 | def SCENARIO_ENTRY (dataset, entry, flagslist ): 128 | print ('\nScenario:', entry['name']) 129 | if ('Details' in flagslist): 130 | showActor(dataset, entry['actor'], True) 131 | else: 132 | showActor(dataset, entry['actor'], False) 133 | 134 | print ('\nScenario Target:', entry['target'], 'Intended Effect:', entry['effect'], 'Impact score:', str(entry['score']) ) 135 | print ('\nATTACK PATH:', entry['path']) 136 | for ip in entry['path']: 137 | bTarget = False 138 | if ip == entry['target']: 139 | bTarget = True 140 | if ('Details' in flagslist): 141 | showNode (dataset, ip, bTarget, True ) 142 | else: 143 | showNode (dataset, ip, bTarget, False ) 144 | print ('\nATT&CK TTP sequences:') 145 | x = 0 146 | for ttpseq in entry['ttps']: 147 | print ('\nAsset:', entry['path'][x] + ':') 148 | x = x + 1 149 | for ttpid in ttpseq: 150 | ttpobj = getTTP (dataset, ttpid) 151 | if ttpobj: 152 | if ('Details' in flagslist): 153 | showTechnique (ttpobj, False ) 154 | else: 155 | showTechnique (ttpobj, False ) 156 | if ('Mitigations' in flagslist): 157 | showMitigation( ttpobj ) 158 | if ('Forensics' in flagslist): 159 | showDetection( ttpobj ) 160 | 161 | 162 | def getACTOR (dataset, groupID): 163 | for j in dataset['ATKGROUPS']: 164 | if groupID == j.getGroupID(): 165 | return j 166 | 167 | def getIDs ( objlist ): 168 | ret = [] 169 | for x in objlist: 170 | if x.typex == 'attack-pattern': 171 | ret.append (x.getTECHID() ) 172 | elif x.typex =='malware': 173 | ret.append (x.getSID() ) 174 | elif x.typex == 'tool': 175 | ret.append (x.getSID() ) 176 | 177 | return ret 178 | 179 | 180 | 181 | def ACTOR_REPORT (dataset, tactlist): 182 | print ('\nACTOR REPORT') 183 | alist = sortActorsBySophistication(dataset) 184 | rank = 1 185 | for actorID in alist: 186 | print ('\nThreat Actor:', actorID, 'Rank:', str(rank), 'in list of', str(len(alist)) ) 187 | showActor (dataset, actorID, True ) 188 | mlist = getCAPABILITIES (dataset, actorID, ['MALWARE'] ) 189 | if mlist: 190 | print ('MALWARE:', mlist) 191 | 192 | tlist = getCAPABILITIES (dataset, actorID, ['TOOL'] ) 193 | if tlist: 194 | print ('TOOLS:', tlist) 195 | 196 | plist = getCAPABILITIES (dataset, actorID, ['TTP'] ) 197 | if plist: 198 | print ('TTPs:', plist ) 199 | TTP_DISTRIBUTION (dataset, tactlist, actorID) 200 | 201 | rank = rank + 1 202 | 203 | 204 | tactlist = ['initial-access', 'discovery', 'credential-access', 'privilege-escalation', 205 | 'execution', 'collection', 'exfiltration', 'lateral-movement', 'command-and-control', 206 | 'persistence', 'defense-evasion' ] 207 | 208 | 209 | def TTP_DISTRIBUTION (dataset, xxx, actorID): 210 | for t in tactlist: 211 | ttpcount = countTTPsByTactic (dataset, t, actorID ) 212 | if ttpcount > 0: 213 | print ('Tactic:', t, 'Attributed TTPs:', ttpcount) 214 | 215 | return 216 | 217 | def getCAPABILITIES (dataset, actorID, caplist): 218 | actOBJ = getACTOR (dataset, actorID) 219 | ret = [] 220 | if actOBJ: 221 | for cap in caplist: 222 | if cap == 'TTP': 223 | ret.append(getIDs (actOBJ.getTTPList() )) 224 | elif cap == 'MALWARE': 225 | ret.append(getIDs (actOBJ.getMalwareList())) 226 | elif cap == 'TOOL': 227 | ret.append( getIDs( actOBJ.getToolsList() ) ) 228 | return ret[0] 229 | 230 | 231 | 232 | 233 | -------------------------------------------------------------------------------- /cicat/generator/spreadout.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | :::::::::::::::::::::::: Critical Infrastructure Cyberspace Analysis Tool (CICAT) ::::::::::::::::::::::::::::::::::::::: 4 | 5 | NOTICE 6 | 7 | The contents of this material reflect the views of the author and/or the Director of the Center for Advanced Aviation 8 | System Development (CAASD), and do not necessarily reflect the views of the Federal Aviation Administration (FAA) 9 | or the Department of Transportation (DOT). Neither the FAA nor the DOT makes any warranty or guarantee, or promise, 10 | expressed or implied, concerning the content or accuracy of the views expressed herein. 11 | 12 | This is the copyright work of The MITRE Corporation and was produced for the U.S. Government under Contract Number 13 | DTFAWA-10-C-00080 and is subject to Federal Aviation Administration Acquisition Management System Clause 3.5-13, 14 | Rights in Data-General, Alt. III and Alt. IV (Oct. 1996). No other use other than that granted to the U.S. Government, 15 | or to those acting on behalf of the U.S. Government, under that Clause is authorized without the express written permission 16 | of The MITRE Corporation. For further information, please contact The MITRE Corporation, Contract Office, 7515 Colshire Drive, 17 | McLean, VA 22102 (703) 983-6000. ©2020 The MITRE Corporation. 18 | 19 | The Government retains a nonexclusive, royalty-free right to publish or reproduce this document, or to allow others to do so, for 20 | “Government Purposes Only.” 21 | 22 | (c) 2020 The MITRE Corporation. All Rights Reserved. 23 | 24 | :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: 25 | spreadout.py - Routines to generate Tom Sawyer ICD spreadsheet 26 | :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: 27 | """ 28 | 29 | from openpyxl import Workbook 30 | from collections import defaultdict 31 | 32 | m_exportSheets = ['SCENARIO_DESC', 'SCEN_ENTRY_POINT', 'THREAT_ACTOR_DESC', 'ASSET_TTP_MAP','TTP_MITIGATION_MAP', 'TTP_DESC', 'MITIGATION_DESC', 'ATTACK_PATH'] 33 | 34 | m_HEADER_tags = defaultdict(list ) 35 | 36 | m_HEADER_tags[0] = ['SCN_ID', 'SHORT_NAME', 'NAME', 'IMPACT_SCORE', 'INTENT', 'DESCRIPTION', 'EXTERNAL_INSIDER_EXTASINSIDER', 'DETAIL', 'ACTOR', 'TTP_PATTERN', 'TARGET_NAME' ] 37 | m_HEADER_tags[1] = ['SCN_ID', 'ENTRY_ASSET_NAME' ] 38 | m_HEADER_tags[2] = ['GROUP_ID', 'ACTOR', 'ALIASES', 'SOPHISTICATION'] 39 | m_HEADER_tags[3] = ['SCN_ID', 'ENTRY_ASSET_NAME', 'ASSET_ID', 'TTP_ID', 'MAP_ASSET_TO_TTP'] 40 | m_HEADER_tags[4] = ['ID', 'TTP_ID', 'MITIGATION_ID'] 41 | m_HEADER_tags[5] = ['TTP_ID', 'NAME', 'DESCRIPTION', 'URL', 'TACTICS_LIST'] 42 | m_HEADER_tags[6] = ['MITIGATION_ID', 'NAME', 'DESCRIPTION', 'URL'] 43 | m_HEADER_tags[7] = ['ID', 'SCENARIO_ID', 'ENTRY_ASSET_NAME', 'TARGET_ASSET', 'FROM_ASSET_ID', 'TO_ASSET_ID', 'ENTRY_SYSTEM_NAME'] 44 | 45 | def list2string(plist, separator): 46 | ret = 'undefined' 47 | if plist: 48 | ret = str(plist[0]) 49 | for j in range (1, len(plist)): 50 | ret = ret + separator + str(plist[j]) 51 | return ret 52 | 53 | def getTTPlist (tracedata): 54 | ttplist = [] 55 | for k in tracedata.keys(): 56 | curdict = tracedata[k] 57 | curtrace = curdict[0] 58 | tlist = curtrace['ttps'] 59 | for x in tlist: 60 | if len(x) > 0: 61 | for xx in x: 62 | ttplist.append(xx) 63 | 64 | tset = set (ttplist) # dump list into set to elimnate duplicates 65 | ret = list (tset) # dump back into list 66 | 67 | return ret 68 | 69 | def getMITsforTTP (dataset, ttpID ): 70 | for t in dataset['ATT&CK']: 71 | ret = [] 72 | if t.getTECHID() == ttpID: 73 | clist = t.getCOA() 74 | for c in clist: 75 | ret.append(c[0].getTECHID() ) 76 | return ret 77 | 78 | def getSystemforAsset (dataset, asset): 79 | for c in dataset['COMPONENT']: 80 | if c.getName() == asset: 81 | return c.getSysName() 82 | 83 | 84 | def getMITsforTTPList (dataset, ttplist): 85 | clist = [] 86 | for x in ttplist: 87 | moremits = getMITsforTTP(dataset, x) 88 | if moremits: 89 | clist = clist + moremits 90 | 91 | uset = set (clist) 92 | ret = list (uset) 93 | 94 | return ret 95 | 96 | def getFromToList(path): 97 | ret = [] 98 | idx = 0 99 | while idx < len(path)-1 : 100 | ret.append([path[idx], path[idx+1]]) 101 | idx = idx + 1 102 | 103 | return ret 104 | 105 | def saveSheetHeaders(sheetname, sheet): 106 | sheetid = m_exportSheets.index(sheetname) 107 | colindx = 1 108 | for k in m_HEADER_tags[sheetid]: 109 | sheet.cell (1, colindx, k) 110 | colindx = colindx + 1 111 | 112 | def saveSheetData (sheetname, sheet, tracedata, dataset): 113 | 114 | if sheetname == 'SCENARIO_DESC': 115 | row = 2 116 | for s in dataset['SCENARIO']: 117 | sheet.cell(row, 1, s.getID() ) 118 | sheet.cell(row, 2, s.getShortName() ) 119 | sheet.cell(row, 3, s.getName()) 120 | sheet.cell(row, 4, '#IMPACT SCORE') 121 | sheet.cell(row, 5, '#INTENT') 122 | sheet.cell(row, 6, s.getDesc() ) 123 | sheet.cell(row, 7, '#EXTERNAL_INSIDER_EXTRAINSIDER') 124 | sheet.cell(row, 8, '#DETAIL' ) 125 | sheet.cell(row, 9, s.getActorID() ) 126 | sheet.cell(row, 10, s.getIntendedEffect () ) 127 | sheet.cell(row, 11, s.getTargetID() ) 128 | row = row + 1 129 | 130 | elif sheetname == 'SCEN_ENTRY_POINT': 131 | row = 2 132 | for k in tracedata.keys(): 133 | values = k.split ('EP', maxsplit=1) 134 | sheet.cell (row, 1, values[0] ) 135 | sheet.cell (row, 2, values[1] ) 136 | row = row + 1 137 | 138 | elif sheetname == 'THREAT_ACTOR_DESC': 139 | row = 2 140 | for s in dataset['SCENARIO']: 141 | actorID = s.getActorID() 142 | for a in dataset['ATKGROUPS']: 143 | if a.getName() == actorID: 144 | sheet.cell (row, 1, a.getGroupID() ) 145 | sheet.cell (row, 2, a.getName () ) 146 | sheet.cell (row, 3, list2string (a.getAliases(), ', ' ) ) 147 | sheet.cell (row, 4, a.getSophisticationLevel() ) 148 | break 149 | row = row + 1 150 | 151 | elif sheetname == 'ASSET_TTP_MAP': 152 | row = 2 153 | rowcnt = 1 154 | for k in tracedata.keys(): 155 | values = k.split ('EP', maxsplit=1) 156 | curdict = tracedata[k] 157 | curtrace = curdict[0] 158 | clist = curtrace['path'] 159 | tlist = curtrace['ttps'] 160 | 161 | idx = 0 #index controls mapping of ttps to assets in component list 162 | 163 | for c in clist: 164 | for t in tlist[idx]: 165 | sheet.cell (row, 1, values[0] ) 166 | sheet.cell (row, 2, values[1] ) 167 | sheet.cell (row, 3, c) 168 | sheet.cell (row, 4, t) 169 | sheet.cell (row, 5, rowcnt) 170 | rowcnt = rowcnt + 1 171 | row = row + 1 172 | idx = idx + 1 173 | 174 | elif sheetname == 'TTP_DESC': 175 | ttplist = getTTPlist (tracedata) 176 | row = 2 177 | for ttp in ttplist: 178 | for d in dataset['ATT&CK']: 179 | if d.getTECHID() == ttp: 180 | sheet.cell (row, 1, d.getTECHID() ) 181 | sheet.cell (row, 2, d.getName() ) 182 | sheet.cell (row, 3, d.getDesc() ) 183 | sheet.cell (row, 4, d.getURL() ) 184 | sheet.cell (row, 5, list2string (d.getTactic(), ', ' )) 185 | break 186 | row = row + 1 187 | 188 | elif sheetname == 'TTP_MITIGATION_MAP': 189 | ttplist = getTTPlist (tracedata) 190 | row = 2 191 | idx = 1 192 | for ttp in ttplist: 193 | clist = getMITsforTTP (dataset, ttp) 194 | if clist: 195 | for c in clist: 196 | sheet.cell(row, 1, idx) 197 | sheet.cell (row, 2, ttp) 198 | sheet.cell (row, 3, c) 199 | row = row + 1 200 | idx = idx + 1 201 | 202 | elif sheetname == 'MITIGATION_DESC': 203 | ttplist = getTTPlist (tracedata) 204 | clist = getMITsforTTPList (dataset, ttplist) 205 | row = 2 206 | for c in clist: 207 | for q in dataset ['ATKMITIGATION']: 208 | if q.getTECHID() == c: 209 | sheet.cell (row, 1, q.getTECHID() ) 210 | sheet.cell (row, 2, q.getName() ) 211 | sheet.cell (row, 3, q.getDesc() ) 212 | sheet.cell (row, 4, q.getURL() ) 213 | break 214 | row = row + 1 215 | 216 | elif sheetname == 'ATTACK_PATH': 217 | row = 2 218 | rowcnt = 1 219 | for k in tracedata.keys(): 220 | values = k.split ('EP', maxsplit=1) 221 | curdict = tracedata[k] 222 | curtrace = curdict[0] 223 | clist = curtrace['path'] 224 | target = curtrace['target'] 225 | sysName = getSystemforAsset (dataset, values[1]) 226 | fromtolist = getFromToList (clist) 227 | if fromtolist: 228 | for p in fromtolist: 229 | sheet.cell (row, 1, rowcnt ) 230 | sheet.cell (row, 2, values[0] ) 231 | sheet.cell (row, 3, values[1] ) 232 | sheet.cell (row, 4, target ) 233 | sheet.cell (row, 5, p[0] ) 234 | sheet.cell (row, 6, p[1] ) 235 | sheet.cell (row, 7, sysName) 236 | rowcnt = rowcnt + 1 237 | row = row + 1 238 | 239 | 240 | def ExportSheet (wb, sheetname, tracedata, dataset): 241 | sheet = wb.create_sheet (title=sheetname ) 242 | saveSheetHeaders(sheetname, sheet) 243 | saveSheetData (sheetname, sheet, tracedata, dataset ) 244 | 245 | def ExportData (fname, tracedata, dataset ): 246 | wb = Workbook() 247 | for sheetname in m_exportSheets: 248 | ExportSheet (wb, sheetname, tracedata, dataset) 249 | wb.save(filename = fname ) 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | -------------------------------------------------------------------------------- /cicat/generator/stats.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | :::::::::::::::::::::::: Critical Infrastructure Cyberspace Analysis Tool (CICAT) ::::::::::::::::::::::::::::::::::::::: 4 | 5 | NOTICE 6 | 7 | The contents of this material reflect the views of the author and/or the Director of the Center for Advanced Aviation 8 | System Development (CAASD), and do not necessarily reflect the views of the Federal Aviation Administration (FAA) 9 | or the Department of Transportation (DOT). Neither the FAA nor the DOT makes any warranty or guarantee, or promise, 10 | expressed or implied, concerning the content or accuracy of the views expressed herein. 11 | 12 | This is the copyright work of The MITRE Corporation and was produced for the U.S. Government under Contract Number 13 | DTFAWA-10-C-00080 and is subject to Federal Aviation Administration Acquisition Management System Clause 3.5-13, 14 | Rights in Data-General, Alt. III and Alt. IV (Oct. 1996). No other use other than that granted to the U.S. Government, 15 | or to those acting on behalf of the U.S. Government, under that Clause is authorized without the express written permission 16 | of The MITRE Corporation. For further information, please contact The MITRE Corporation, Contract Office, 7515 Colshire Drive, 17 | McLean, VA 22102 (703) 983-6000. ©2020 The MITRE Corporation. 18 | 19 | The Government retains a nonexclusive, royalty-free right to publish or reproduce this document, or to allow others to do so, for 20 | “Government Purposes Only.” 21 | 22 | (c) 2020 The MITRE Corporation. All Rights Reserved. 23 | 24 | :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: 25 | stats.py - Routines to generate sorted lists and counts 26 | :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: 27 | """ 28 | 29 | 30 | from collections import defaultdict 31 | 32 | """ 33 | TAG USAGE STATISTICS 34 | """ 35 | 36 | x_TYPES = defaultdict(list) 37 | x_TACTICS = defaultdict(list) 38 | x_PLATFORMS = defaultdict(list) 39 | 40 | x_ACTTAGS = defaultdict(list) 41 | x_TTPTAGS = defaultdict(list) 42 | x_MITTAGS = defaultdict(list) 43 | x_MALTAGS = defaultdict(list) 44 | x_TOLTAGS = defaultdict(list) 45 | 46 | def TAGS_INDEX (dataset, trace): 47 | 48 | if trace: 49 | print ('Initializing X_TYPES index..') 50 | 51 | for a in dataset['ATKGROUPS']: 52 | x_TYPES[a.getType()].append(a) 53 | 54 | for a in dataset['ATKTOOL']: 55 | x_TYPES[a.getType()].append(a) 56 | 57 | for a in dataset['ATKMALWARE']: 58 | x_TYPES[a.getType()].append(a) 59 | 60 | for a in dataset['ATT&CK']: 61 | x_TYPES[a.getType()].append(a) 62 | 63 | for a in dataset['ATKMITIGATION']: 64 | x_TYPES[a.getType()].append(a) 65 | 66 | if trace: 67 | print ('Initializing X_TACTICS index..') 68 | 69 | for t in dataset['ATT&CK']: 70 | tctlist = t.getTactic() 71 | if (tctlist): 72 | for c in tctlist: 73 | x_TACTICS[c].append(t) 74 | if trace: 75 | print ('Initializing X_PLATFORMS index..') 76 | 77 | for t in dataset['ATT&CK']: 78 | platlist = t.getPlatform() 79 | if (platlist): 80 | for p in platlist: 81 | x_PLATFORMS[p].append(t) 82 | 83 | for x in dataset['ATKTOOL']: 84 | platlist = x.getPlatform() 85 | if (platlist): 86 | for p in platlist: 87 | x_PLATFORMS[p].append(x) 88 | 89 | for mal in dataset['ATKMALWARE']: 90 | platlist = mal.getPlatform() 91 | if (platlist): 92 | for p in platlist: 93 | x_PLATFORMS[p].append(mal) 94 | 95 | if trace: 96 | print ('Initializing X_ACTTAGS index..') 97 | 98 | for j in dataset['ATKGROUPS']: 99 | for tag in j.getTags(): 100 | x_ACTTAGS[tag].append(j) 101 | 102 | if trace: 103 | print ('Initializing X_TTPTAGS index..') 104 | 105 | for j in dataset['ATT&CK']: 106 | for tag in j.getTags(): 107 | x_TTPTAGS[tag].append(j) 108 | 109 | if trace: 110 | print ('Initializing X_MITTAGS index..') 111 | 112 | for j in dataset['ATKMITIGATION']: 113 | for tag in j.getTags(): 114 | x_MITTAGS[tag].append(j) 115 | 116 | if trace: 117 | print ('Initializing X_TOOLTAGS index..') 118 | 119 | for j in dataset['ATKTOOL']: 120 | for tag in j.getTags(): 121 | x_TOLTAGS[tag].append(j) 122 | 123 | if trace: 124 | print ('Initializing X_MALTAGS index..') 125 | 126 | for j in dataset['ATKMALWARE']: 127 | for tag in j.getTags(): 128 | x_MALTAGS[tag].append(j) 129 | 130 | if trace: 131 | print ('TAGs indexed..') 132 | 133 | 134 | """ 135 | INFRASTRUCTURE STATISTICS 136 | """ 137 | #returns locations by decreasing system counts 138 | def sortLocationsbySize(dataset, minsize ): 139 | 140 | raw_t = [] 141 | sorted_t = [] 142 | 143 | for j in dataset['LOCATION']: 144 | if j.system and (len(j.system) > minsize): 145 | 146 | raw_t.append([len(j.system), j.getZoneDesignation() ]) 147 | 148 | sorted_t = sorted(raw_t, key=lambda raw: raw[0], reverse=True ) 149 | 150 | ret = [] 151 | for l in sorted_t: 152 | ret.append(l[1]) 153 | 154 | return ret 155 | 156 | #returns zones by decreasing connections 157 | def sortZonesbyConnections (zonemap): 158 | 159 | raw_t = [] 160 | sorted_t = [] 161 | 162 | for z in zonemap.keys(): 163 | raw_t.append ([z, len(zonemap[z])]) 164 | 165 | sorted_t = sorted(raw_t, key=lambda raw: raw[1], reverse = True ) 166 | 167 | ret = [] 168 | for l in sorted_t: 169 | ret.append(l[0]) 170 | 171 | return ret 172 | 173 | #returns systems by decreasing function usage 174 | def sortSystemsbyFunction(dataset): 175 | 176 | raw_t = [] 177 | sorted_t = [] 178 | 179 | tdix = defaultdict(list) 180 | for f in dataset['FSMAP']: 181 | if f.getSystem(): 182 | tdix[f.getSystem()].append (f.getFunction() ) 183 | 184 | for s in tdix.keys(): 185 | raw_t.append( [s, len(tdix[s]) ] ) 186 | 187 | sorted_t = sorted(raw_t, key=lambda raw: raw[1], reverse = True ) 188 | 189 | ret = [] 190 | for l in sorted_t: 191 | ret.append(l[0]) 192 | 193 | return ret 194 | 195 | """ 196 | SCENARIO STATISTICS 197 | """ 198 | 199 | #returns scenarios sorted by attack path length 200 | def sortByAttackPathsbyLength(statsdata): 201 | 202 | raw_t = [] 203 | sort_t = [] 204 | 205 | for s in statsdata: 206 | raw_t.append ( [s['name'], len(s['path']) ]) 207 | 208 | sort_t = sorted (raw_t, key=lambda raw: raw[1], reverse=True ) 209 | 210 | ret = [] 211 | for l in sort_t: 212 | ret.append(l[0]) 213 | 214 | return ret 215 | 216 | #returns list of IPs most visited by scenarios 217 | def sortByVisitedNode(statsdata): 218 | 219 | raw_t = [] 220 | sort_t = [] 221 | 222 | ipx = defaultdict(list) 223 | 224 | for s in statsdata.keys(): 225 | entry = statsdata[s][0] 226 | ap = entry['path'] 227 | for ip in ap: 228 | ipx[ip].append ('X') 229 | 230 | for k in ipx.keys(): 231 | raw_t.append ( [k, len(ipx[k])] ) 232 | 233 | sort_t = sorted (raw_t, key=lambda raw: raw[1], reverse=True ) 234 | 235 | ret = [] 236 | for l in sort_t: 237 | ret.append(l[0]) 238 | 239 | return ret 240 | 241 | #returns list of entrypoints sorted by usage 242 | def sortByEntrypointSuccesses(statsdata): 243 | 244 | raw_t = [] 245 | sort_t = [] 246 | 247 | epdata = defaultdict(list) 248 | 249 | for s in statsdata: 250 | ep = s.split('EP') 251 | epdata[ep[1]].append(s) 252 | 253 | for ex in epdata.keys(): 254 | raw_t.append([ex, len(epdata[ex])]) 255 | 256 | sort_t = sorted (raw_t, key=lambda raw: raw[1], reverse=True ) 257 | 258 | ret = [] 259 | for l in sort_t: 260 | ret.append(l[0]) 261 | 262 | return ret 263 | 264 | #returns list of TTPs sorted by usage 265 | def sortByTTPUsage(statsdata): 266 | 267 | raw_t = [] 268 | sort_t = [] 269 | 270 | ttpdata = defaultdict(list) 271 | 272 | for s in statsdata.keys(): 273 | entry = statsdata[s][0] 274 | for seq in entry['ttps']: 275 | for q in seq: 276 | ttpdata[q].append('X') 277 | 278 | for k in ttpdata.keys(): 279 | raw_t.append([k, len(ttpdata[k])]) 280 | 281 | sort_t = sorted (raw_t, key=lambda raw: raw[1], reverse=True ) 282 | 283 | ret = [] 284 | for l in sort_t: 285 | ret.append(l[0]) 286 | 287 | return ret 288 | 289 | def sortActorsBySophistication (dataset): 290 | raw_t = [] 291 | sort_t = [] 292 | 293 | for t in dataset['ATKGROUPS']: 294 | raw_t.append ([t.getGroupID(), t.getSophisticationLevel()]) 295 | 296 | sort_t = sorted (raw_t, key=lambda raw: raw[1], reverse=True ) 297 | 298 | ret = [] 299 | for s in sort_t: 300 | ret.append(s[0]) 301 | 302 | return ret 303 | 304 | def countTTPsByTactic (dataset, tactic, groupID): 305 | for t in dataset['ATKGROUPS']: 306 | if t.getGroupID() == groupID: 307 | ttplist = [] 308 | pb = t.getPlaybook() 309 | for x in pb: 310 | if x[0].typex == 'attack-pattern': 311 | ttplist.append (x[0]) 312 | ret = 0 313 | for t in ttplist: 314 | if (tactic in t.getTactic()): 315 | ret = ret + 1 316 | return ret 317 | 318 | -------------------------------------------------------------------------------- /cicat/generator/tfactory.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | :::::::::::::::::::::::: Critical Infrastructure Cyberspace Analysis Tool (CICAT) ::::::::::::::::::::::::::::::::::::::: 4 | 5 | NOTICE 6 | 7 | The contents of this material reflect the views of the author and/or the Director of the Center for Advanced Aviation 8 | System Development (CAASD), and do not necessarily reflect the views of the Federal Aviation Administration (FAA) 9 | or the Department of Transportation (DOT). Neither the FAA nor the DOT makes any warranty or guarantee, or promise, 10 | expressed or implied, concerning the content or accuracy of the views expressed herein. 11 | 12 | This is the copyright work of The MITRE Corporation and was produced for the U.S. Government under Contract Number 13 | DTFAWA-10-C-00080 and is subject to Federal Aviation Administration Acquisition Management System Clause 3.5-13, 14 | Rights in Data-General, Alt. III and Alt. IV (Oct. 1996). No other use other than that granted to the U.S. Government, 15 | or to those acting on behalf of the U.S. Government, under that Clause is authorized without the express written permission 16 | of The MITRE Corporation. For further information, please contact The MITRE Corporation, Contract Office, 7515 Colshire Drive, 17 | McLean, VA 22102 (703) 983-6000. ©2020 The MITRE Corporation. 18 | 19 | The Government retains a nonexclusive, royalty-free right to publish or reproduce this document, or to allow others to do so, for 20 | “Government Purposes Only.” 21 | 22 | (c) 2020 The MITRE Corporation. All Rights Reserved. 23 | 24 | :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: 25 | tfactory.py - Factory class to construct target, scenario, entrypoint object instances 26 | :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: 27 | """ 28 | 29 | 30 | import openpyxl 31 | 32 | from tmodel import TARGET 33 | from tmodel import SCENARIO 34 | #from tmodel import INDICATOR 35 | #from tmodel import COA 36 | from tmodel import ENTRYPOINT 37 | 38 | def findActor(aid, dataset ): 39 | for j in dataset['ATKGROUPS']: 40 | if j.getGroupID() == aid: 41 | return j 42 | 43 | def findTargetRecord(tid, dataset): 44 | for j in dataset['TARGET']: 45 | if j.getName() == tid: 46 | return j 47 | 48 | def findTarget(tgtobj, dataset ): 49 | tname = tgtobj.getName() 50 | for j in dataset['COMPONENT']: 51 | if j.getName().lower() == tname.lower(): 52 | return j 53 | 54 | 55 | class THREAT_FACTORY(): 56 | def __init__ (self, filename, trace ): 57 | self.filename = filename 58 | self.trace = trace 59 | if self.trace: 60 | print ('THREAT factory constructed..') 61 | 62 | def getLoader (self, sheetname ): 63 | if (sheetname == 'TARGET'): 64 | return INFRASTRUCTURE_FACTORY (self.filename, 'INFRASTRUCTURE', True, self.trace ) 65 | elif (sheetname == 'ENTRYPOINT'): 66 | return INFRASTRUCTURE_FACTORY (self.filename, 'INFRASTRUCTURE', False, self.trace ) 67 | elif (sheetname == 'SCENARIO'): 68 | return SCENARIO_FACTORY(self.filename, sheetname, self.trace) 69 | # elif (sheetname == 'COA'): 70 | # return COA_FACTORY (self.filename, sheetname, self.trace) 71 | return 72 | 73 | def initRelationships(self, dataset ): 74 | for s in dataset['SCENARIO']: 75 | tid = s.getTargetID() 76 | target = findTargetRecord(tid, dataset) 77 | if target: 78 | s.setTarget(target ) 79 | else: 80 | if self.trace: 81 | print ('WARNING: Could not find target', tid) 82 | 83 | aid = s.getActorID() 84 | actor = findActor (aid, dataset) 85 | if actor: 86 | s.setActor(actor) 87 | else: 88 | if self.trace: 89 | print ('WARNING: Could not find actor', aid) 90 | 91 | """ 92 | class INDICATOR_FACTORY(THREAT_FACTORY): 93 | def __init__ (self, filename, sheetname, trace): 94 | self.filename = filename 95 | self.sheetname = sheetname 96 | self.trace = trace 97 | if self.trace: 98 | print ('INDICATOR factory constructed..') 99 | return 100 | 101 | def load (self): 102 | if self.trace: 103 | print ('Loading Indicators..') 104 | book = openpyxl.load_workbook(self.filename, data_only=True) 105 | sheet = book[self.sheetname] 106 | ret = [] 107 | for row in sheet.rows: 108 | ret.append(INDICATOR (row[0].value, row[1].value )) 109 | 110 | del ret[0] 111 | return ret 112 | """ 113 | 114 | class INFRASTRUCTURE_FACTORY (THREAT_FACTORY): 115 | def __init__ (self, filename, sheetname, btargetFlag, trace): 116 | self.filename = filename 117 | self.sheetname = sheetname 118 | self.btargetFlag = btargetFlag 119 | self.trace = trace 120 | if self.trace: 121 | if btargetFlag: 122 | print ('TARGET factory constructed..') 123 | else: 124 | print ('ENTRYPOINT factory constructed..') 125 | return 126 | 127 | def load (self): 128 | if self.trace: 129 | if self.btargetFlag: 130 | print ('Loading target information..') 131 | else: 132 | print ('Loading entry point information..') 133 | book = openpyxl.load_workbook(self.filename, data_only=True) 134 | sheet = book[self.sheetname] 135 | ret = [] 136 | 137 | if self.btargetFlag: 138 | for row in sheet.rows: 139 | if row[3].value: 140 | ret.append (TARGET (row[0].value, row[1].value ) ) 141 | else: 142 | for row in sheet.rows: 143 | if row[4].value: 144 | ret.append (ENTRYPOINT (row[0].value, row[1].value )) 145 | del ret[0] 146 | return ret 147 | 148 | """ 149 | class COA_FACTORY (THREAT_FACTORY): 150 | def __init__ (self, filename, sheetname, trace): 151 | self.filename = filename 152 | self.sheetname = sheetname 153 | self.trace = trace 154 | if self.trace: 155 | print ('COA factory constructed..') 156 | return 157 | 158 | def load (self, filename): 159 | if self.trace: 160 | print ('Loading COAs..') 161 | book = openpyxl.load_workbook(filename, data_only=True) 162 | sheet = book['Controls'] 163 | ret = [] 164 | control = None 165 | for row in sheet.rows: 166 | if (row[5].value.startswith ('[Withdrawn:')): 167 | continue 168 | if row[0].value and row[2].value: 169 | control = COA (row[0].value, row[1].value, row[2].value, row[3].value, 170 | row[4].value, row[5].value, row[6].value, row[7].value ) 171 | ret.append(control) 172 | else: 173 | control.appendDesc(row[1].value, row[5].value) 174 | 175 | del ret[0] 176 | return ret 177 | """ 178 | 179 | class SCENARIO_FACTORY(THREAT_FACTORY): 180 | def __init__ (self, filename, sheetname, trace): 181 | self.filename = filename 182 | self.sheetname = sheetname 183 | self.trace = trace 184 | if self.trace: 185 | print ('SCENARIO factory constructed..') 186 | return 187 | 188 | def load (self): 189 | if self.trace: 190 | print ('Loading Scenarios..') 191 | 192 | book = openpyxl.load_workbook(self.filename, data_only=True) 193 | sheet = book[self.sheetname] 194 | ret = [] 195 | for row in sheet.rows: 196 | ret.append (SCENARIO (row[0].value, row[1].value, row[2].value, 197 | row[3].value, row[4].value, row[5].value, 198 | row[6].value, row[7].value ) ) 199 | 200 | del ret[0] 201 | return ret 202 | 203 | -------------------------------------------------------------------------------- /cicat/generator/tmodel.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | :::::::::::::::::::::::: Critical Infrastructure Cyberspace Analysis Tool (CICAT) ::::::::::::::::::::::::::::::::::::::: 4 | 5 | NOTICE 6 | 7 | The contents of this material reflect the views of the author and/or the Director of the Center for Advanced Aviation 8 | System Development (CAASD), and do not necessarily reflect the views of the Federal Aviation Administration (FAA) 9 | or the Department of Transportation (DOT). Neither the FAA nor the DOT makes any warranty or guarantee, or promise, 10 | expressed or implied, concerning the content or accuracy of the views expressed herein. 11 | 12 | This is the copyright work of The MITRE Corporation and was produced for the U.S. Government under Contract Number 13 | DTFAWA-10-C-00080 and is subject to Federal Aviation Administration Acquisition Management System Clause 3.5-13, 14 | Rights in Data-General, Alt. III and Alt. IV (Oct. 1996). No other use other than that granted to the U.S. Government, 15 | or to those acting on behalf of the U.S. Government, under that Clause is authorized without the express written permission 16 | of The MITRE Corporation. For further information, please contact The MITRE Corporation, Contract Office, 7515 Colshire Drive, 17 | McLean, VA 22102 (703) 983-6000. ©2020 The MITRE Corporation. 18 | 19 | The Government retains a nonexclusive, royalty-free right to publish or reproduce this document, or to allow others to do so, for 20 | “Government Purposes Only.” 21 | 22 | (c) 2020 The MITRE Corporation. All Rights Reserved. 23 | 24 | :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: 25 | tmodel.py - Object classes for vulnerability, indicator, target, scenario, entrypoint, COA 26 | :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: 27 | """ 28 | 29 | import re 30 | 31 | class VULNERABILITY: 32 | def __init__ (self, idx ): 33 | self.idx = idx 34 | self.vendor = '' 35 | self.title = '' 36 | self.target = '' 37 | self.desc = '' 38 | self.ref = [] 39 | self.effects = [] 40 | self.access = [] 41 | self.indicator = [] 42 | self.reserved = False 43 | 44 | effectkeys = ['DoS','denial of service', 'denial-of-service', 'hang', 'crash', 'panic', 'outage', 'reload', 'reboot', 'restart', \ 45 | 'degrade', 'flood', 'slow', 'delay', 'replay', 'exhaust', 'leak', 'deplete', 'consume', 'overwrite', 'corrupt', 'modify', 'alter', \ 46 | 'read', 'exfiltrate', 'steal', 'capture', 'obtain sensitive information', 'spoof', 'disrupt', 'execute code', 'arbitrary code', \ 47 | 'inject', 'unauthenticate', 'unauthorize', 'mascarade', 'bypass', 'privilege', 'escalat', 'elevate', 'root access', 'damage', 'destroy' ] 48 | 49 | accesskeys = ['remote', 'web interface', 'tcp connection', 'session', 'ethernet', 'local', 'privilege', 'crafted packet'] 50 | 51 | 52 | def getCVE(self): 53 | return self.title 54 | 55 | def setTitle(self, title): 56 | if self.title == "": 57 | self.title = title 58 | else: 59 | self.title = self.title + title 60 | 61 | def getTitle(self): 62 | return self.title 63 | 64 | def setDescription(self, desc): 65 | if self.desc == "": 66 | self.desc = desc.replace('\n', ' ') 67 | else: 68 | self.desc = self.desc + desc.replace('\n', ' ') 69 | 70 | def getDescription(self): 71 | return self.desc 72 | 73 | def addReference(self, ref ): 74 | self.ref.append(ref) 75 | 76 | def getReferences(self): 77 | return self.ref 78 | 79 | def setTarget(self, target): 80 | self.target = target 81 | 82 | def getTarget(self): 83 | return self.target 84 | 85 | def searchbyKeys (self, keylist): 86 | ret = [] 87 | for k in keylist: 88 | p = re.compile(k, re.IGNORECASE) 89 | if p.search(self.getDescription().casefold() ): 90 | ret.append(k) 91 | return ret 92 | 93 | def getEffects(self): 94 | if (self.effects): 95 | return self.effects 96 | self.effects = self.searchbyKeys(self.effectkeys) 97 | if not(self.effects): 98 | self.effects = ['None found'] 99 | return self.effects 100 | 101 | def getAccess(self): 102 | if (self.access): 103 | return self.access 104 | self.access = self.searchbyKeys(self.accesskeys) 105 | if not(self.access): 106 | self.access = ['None found'] 107 | return self.access 108 | 109 | def isUniqueIndy (self, entry): 110 | if (entry in self.indicator): 111 | return False 112 | return True 113 | 114 | def link_11 (self, indyArray, trace=False): 115 | for indy in indyArray: 116 | if (indy.getCVE() == self.getCVE()): 117 | if (self.isUniqueIndy(indy)): 118 | self.indicator.append (indy ) 119 | if (trace): 120 | print ('link_11: INDICATOR', indy.getCVE(), 'added to VULNERABILITY', self.getCVE() ) 121 | 122 | def PP(self, verbose=False): 123 | if self.ref: 124 | print (self.title, 'URL:', self.ref[0]) 125 | else: 126 | print (self.title, 'URL: none') 127 | 128 | if verbose: 129 | print ('Effects:', self.effects, 'Access:', self.access ) 130 | cnt = 0 131 | maxwc = 30 132 | shortdesc = '' 133 | wlist = self.desc.split(' ') 134 | if wlist: 135 | for j in wlist: 136 | shortdesc = shortdesc + ' ' + j 137 | cnt = cnt + 1 138 | if cnt > maxwc: 139 | shortdesc = shortdesc + '...' 140 | break 141 | print (shortdesc) 142 | 143 | """ 144 | class INDICATOR: 145 | def __init__ (self, cveID, desc): 146 | self.cveID = cveID 147 | self.desc = desc 148 | 149 | def getCVE(self): 150 | return self.cveID 151 | 152 | def getDescription(self): 153 | return self.desc 154 | 155 | def PP(self, verbose=False ): 156 | print ('INDICATOR:', self.desc) 157 | """ 158 | 159 | class TARGET: 160 | def __init__ (self, cid, cname): 161 | self.cid = cid 162 | self.cname = cname 163 | self.component = None 164 | 165 | def initTarget(self, clist ): 166 | if self.component: 167 | return self.component 168 | 169 | for x in clist: 170 | if x.getID() == self.cid: 171 | self.component = x 172 | return self.component 173 | 174 | def getCID(self): 175 | return self.cid 176 | 177 | def getName(self): 178 | return self.cname 179 | 180 | def getComponent (self): 181 | return self.component 182 | 183 | def getIPAddr(self): 184 | if self.component: 185 | return self.component.getIPAddress() 186 | 187 | def getZone(self): 188 | if self.component: 189 | return self.component.getZone() 190 | 191 | def getCriticality(self ): 192 | if self.component: 193 | return self.component.getCriticality() 194 | 195 | def PP(self, verbose =True ): 196 | print ('TARGET:', self.cid, ' :', self.cname) 197 | self.component.PP(verbose ) 198 | 199 | class SCENARIO: 200 | def __init__(self, dbID, shortname, name, desc, detail, actorID, intendedEffect, targetID ): 201 | self.dbID = dbID 202 | self.shortname = shortname 203 | self.name = name 204 | self.desc = desc 205 | self.detail = detail 206 | self.actorID = actorID 207 | self.intendedEffect = intendedEffect 208 | self.targetID = targetID 209 | self.targetIP = None 210 | self.actor = [] 211 | self.target = [] 212 | 213 | def getID(self): 214 | return self.dbID 215 | 216 | def getShortName(self): 217 | return self.shortname 218 | 219 | def getName(self): 220 | return self.name 221 | 222 | def getDesc(self): 223 | return self.desc 224 | 225 | def setDetail(self, trace): 226 | self.detail = trace 227 | 228 | def getDetail(self): 229 | return self.detail 230 | 231 | def setActor(self, actor): 232 | self.actor.append(actor) 233 | 234 | def getActor(self): 235 | return self.actor 236 | 237 | def getTargetID(self): 238 | return self.targetID 239 | 240 | def getActorID(self): 241 | return self.actorID 242 | 243 | def getIntendedEffect(self): 244 | return self.intendedEffect 245 | 246 | def getStartHint(self): 247 | return self.startHint 248 | 249 | def setTarget(self, target): 250 | self.target.append(target) 251 | 252 | def getTarget(self): 253 | return self.target 254 | 255 | def PP(self): 256 | print ('\nSCENARIOX ' + self.name + ":") 257 | print (self.desc) 258 | 259 | 260 | class ENTRYPOINT: 261 | def __init__(self, cid, cname ): 262 | self.cid = cid 263 | self.cname = cname 264 | self.component = None 265 | 266 | def initEntrypoint(self, clist ): 267 | if self.component: 268 | return self.component 269 | 270 | for x in clist: 271 | if x.getID() == self.cid: 272 | self.component = x 273 | return self.component 274 | 275 | def getCID(self): 276 | return self.cid 277 | 278 | def getName(self): 279 | return self.cname 280 | 281 | def getComponent (self): 282 | return self.component 283 | 284 | def getIPAddr (self): 285 | if self.component: 286 | return self.component.getIPAddress() 287 | 288 | def getSysName(self): 289 | if self.component: 290 | return self.component.getSysName() 291 | 292 | def getZone(self): 293 | if self.component: 294 | return self.component.getZone() 295 | """ 296 | class COA: 297 | def __init__ (self, family, name, title, priority, impact, desc, supp, related ): 298 | self.family = family 299 | self.name = name 300 | self.title = title 301 | self.priority = priority 302 | self.impact = impact 303 | self.desc = desc 304 | self.supplmental = supp 305 | self.related = related 306 | self.parameters = [] 307 | self.myKeys = [] 308 | self.coakeys = ['prevent', 'policy', 'detect', 'audit', 'assess', 'alert', 'warn', 'analy', 309 | 'scan','inspect', 'aware', 'verify', 'train', 'respond', 'monitor', 'recover', 310 | 'certify'] 311 | 312 | def appendDesc(self, name, desc): 313 | self.desc = self.desc+'\n'+name+': '+desc 314 | 315 | def getParameterList(self): 316 | if self.parameters: 317 | return self.parameters 318 | wlist = self.desc.split(':') 319 | for w in wlist: 320 | if w.find(']') > 0: 321 | endst = w.index(']') 322 | self.parameters.append(w[1:endst]) 323 | return self.parameters 324 | 325 | def getName(self): 326 | return self.name 327 | 328 | def searchbyKeys (self, keylist): 329 | ret = [] 330 | for k in keylist: 331 | p = re.compile(k, re.IGNORECASE) 332 | if p.search(self.desc.casefold() ): 333 | ret.append(k) 334 | elif p.search(self.supplmental.casefold() ): 335 | ret.append(k) 336 | return ret 337 | 338 | def getCOAkeys(self): 339 | if self.myKeys: 340 | return self.myKeys 341 | self.myKeys = self.searchbyKeys(self.coakeys) 342 | return self.myKeys 343 | """ 344 | 345 | -------------------------------------------------------------------------------- /cicat/generator/topology.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | :::::::::::::::::::::::: Critical Infrastructure Cyberspace Analysis Tool (CICAT) ::::::::::::::::::::::::::::::::::::::: 4 | 5 | NOTICE 6 | 7 | The contents of this material reflect the views of the author and/or the Director of the Center for Advanced Aviation 8 | System Development (CAASD), and do not necessarily reflect the views of the Federal Aviation Administration (FAA) 9 | or the Department of Transportation (DOT). Neither the FAA nor the DOT makes any warranty or guarantee, or promise, 10 | expressed or implied, concerning the content or accuracy of the views expressed herein. 11 | 12 | This is the copyright work of The MITRE Corporation and was produced for the U.S. Government under Contract Number 13 | DTFAWA-10-C-00080 and is subject to Federal Aviation Administration Acquisition Management System Clause 3.5-13, 14 | Rights in Data-General, Alt. III and Alt. IV (Oct. 1996). No other use other than that granted to the U.S. Government, 15 | or to those acting on behalf of the U.S. Government, under that Clause is authorized without the express written permission 16 | of The MITRE Corporation. For further information, please contact The MITRE Corporation, Contract Office, 7515 Colshire Drive, 17 | McLean, VA 22102 (703) 983-6000. ©2020 The MITRE Corporation. 18 | 19 | The Government retains a nonexclusive, royalty-free right to publish or reproduce this document, or to allow others to do so, for 20 | “Government Purposes Only.” 21 | 22 | (c) 2020 The MITRE Corporation. All Rights Reserved. 23 | 24 | :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: 25 | topology.py - Routines to build network topology based on CONNECTION table 26 | :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: 27 | """ 28 | 29 | 30 | import sys 31 | from collections import defaultdict 32 | 33 | from loaddata import LOAD_DATA 34 | from loaddata import m_file_TESTBED_MODEL, m_file_TESTBED_SCNRO #testbed data used for unit tests 35 | 36 | m_topology = defaultdict (list) 37 | m_zoneCIs = defaultdict (list) 38 | m_zoneMMap = defaultdict(list) 39 | m_zoneTopo = defaultdict (list) 40 | 41 | def addLeveltoTopo (level): 42 | if not(m_zoneTopo[level]): 43 | m_zoneTopo[level] = [] 44 | 45 | def addZonetoTopo(level, zone): 46 | addLeveltoTopo(level) 47 | m_zoneTopo[level].append ([level, zone]) 48 | 49 | def zonesInLevel(level): 50 | if m_zoneTopo[level]: 51 | return m_zoneTopo[level] 52 | 53 | print ('Unrecognized security level:', str(level) ) 54 | return [] 55 | 56 | def initZoneList(dataset ): 57 | for cx in dataset['LOCATION']: 58 | lvl = cx.getLevel() 59 | zon = cx.getZone() 60 | addZonetoTopo(lvl,zon) 61 | 62 | def getSystemTopology(dataset, sysName): 63 | topo = [sysName] 64 | iplist = [] 65 | for sysm in dataset['SYSTEM']: 66 | if (sysm.getName() == sysName): 67 | for c in sysm.getComponentList(): 68 | iplist.append(c.getIPAddress() ) 69 | topo.append(iplist) 70 | return topo 71 | 72 | def getZoneTopology(dataset, level, zone ): 73 | topo = [[level, zone]] 74 | for sysm in dataset['SYSTEM']: 75 | if (sysm.getLevel() == level) and (sysm.getZone() == zone): 76 | topo.append(getSystemTopology(dataset, sysm.getName() )) 77 | return topo 78 | 79 | def getLevelTopology (dataset, level ): 80 | topo =[] 81 | zones = [] 82 | for loc in dataset['LOCATION']: 83 | if (loc.getLevel() == level): 84 | zones.append (loc.getZone()) 85 | 86 | for z in zones: 87 | topo.append(getZoneTopology(dataset, level, z )) 88 | return topo 89 | 90 | def buildZoneMap(systemList, zoneCIs): 91 | for j in zoneCIs.keys(): 92 | for ci in zoneCIs[j]: 93 | m_zoneMMap[j].append(ci.getDstZoneID(systemList )) 94 | return m_zoneMMap 95 | 96 | def INIT_TOPOLOGY (dataset, trace): 97 | 98 | print ('Initalizing topology...') 99 | 100 | initZoneList (dataset) 101 | 102 | if trace: 103 | print ('Initializing Level Topologies..') 104 | 105 | for level in m_zoneTopo.keys(): 106 | m_topology[level] = getLevelTopology(dataset, level ) 107 | 108 | if trace: 109 | print ('Initializing network routing..') 110 | 111 | for k in dataset['CONNECTION']: 112 | lvl = k.getSrcLevel(dataset['SYSTEM']) 113 | zon = k.getSrcZone(dataset['SYSTEM']) 114 | ztx = str(str(lvl)+zon) 115 | m_zoneCIs[ztx].append(k) 116 | 117 | return buildZoneMap (dataset['SYSTEM'], m_zoneCIs) 118 | 119 | 120 | # Helper function for reading options from command line 121 | def optionReader(params, flag): 122 | idx = params.index(flag) 123 | if len(params) > idx + 1 and '-' not in params[idx + 1]: 124 | return params[idx + 1] 125 | else: 126 | print(flag + ' flag must include an option!') 127 | exit() 128 | 129 | # main entry point 130 | if ( __name__ == "__main__"): 131 | 132 | Ispread = m_file_TESTBED_MODEL 133 | Tspread = m_file_TESTBED_SCNRO 134 | 135 | params = sys.argv 136 | if len(params) > 1: 137 | if 'help' in params[1].lower(): 138 | print ('\nUSAGE: python', params[0], '[-i ] [-s ]') 139 | exit() 140 | 141 | if '-i' in params: 142 | Ispread = optionReader(params, '-i') 143 | 144 | if '-s' in params: 145 | Tspread = optionReader(params, '-s') 146 | 147 | 148 | myDATASET = LOAD_DATA (Ispread, Tspread, False, False ) 149 | zonemap = INIT_TOPOLOGY(myDATASET, True ) 150 | 151 | 152 | print ('End of run') 153 | -------------------------------------------------------------------------------- /cicat/generator/vfactory.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | :::::::::::::::::::::::: Critical Infrastructure Cyberspace Analysis Tool (CICAT) ::::::::::::::::::::::::::::::::::::::: 4 | 5 | NOTICE 6 | 7 | The contents of this material reflect the views of the author and/or the Director of the Center for Advanced Aviation 8 | System Development (CAASD), and do not necessarily reflect the views of the Federal Aviation Administration (FAA) 9 | or the Department of Transportation (DOT). Neither the FAA nor the DOT makes any warranty or guarantee, or promise, 10 | expressed or implied, concerning the content or accuracy of the views expressed herein. 11 | 12 | This is the copyright work of The MITRE Corporation and was produced for the U.S. Government under Contract Number 13 | DTFAWA-10-C-00080 and is subject to Federal Aviation Administration Acquisition Management System Clause 3.5-13, 14 | Rights in Data-General, Alt. III and Alt. IV (Oct. 1996). No other use other than that granted to the U.S. Government, 15 | or to those acting on behalf of the U.S. Government, under that Clause is authorized without the express written permission 16 | of The MITRE Corporation. For further information, please contact The MITRE Corporation, Contract Office, 7515 Colshire Drive, 17 | McLean, VA 22102 (703) 983-6000. ©2020 The MITRE Corporation. 18 | 19 | The Government retains a nonexclusive, royalty-free right to publish or reproduce this document, or to allow others to do so, for 20 | “Government Purposes Only.” 21 | 22 | (c) 2020 The MITRE Corporation. All Rights Reserved. 23 | 24 | :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: 25 | vfactory.py - SAX parser for importing CVE XML file data 26 | :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: 27 | """ 28 | 29 | 30 | import xml.sax 31 | import collections 32 | import re 33 | 34 | from tmodel import VULNERABILITY 35 | 36 | m_CVElist = collections.defaultdict(list) 37 | 38 | 39 | class CVEHandler (xml.sax.ContentHandler): 40 | def __init__(self): 41 | self.CurrentData = "" 42 | self.currCVE = "" 43 | self.skipflag = False 44 | 45 | def startElement(self, tag, attributes): 46 | self.CurrentData = tag 47 | if tag == "Vulnerability": 48 | self.currCVE = VULNERABILITY(attributes["Ordinal"]) 49 | elif (( tag == "Note") and (attributes["Type"] == "Other")): 50 | self.skipflag = True 51 | 52 | def endElement(self, tag): 53 | if tag == "Vulnerability": 54 | m_CVElist[self.currCVE.getTitle()] = self.currCVE 55 | elif ((tag == "Note") and self.skipflag ): 56 | self.skipflag = False 57 | 58 | def characters(self, content): 59 | if self.currCVE == '': 60 | return 61 | elif self.CurrentData == "Title": 62 | if self.currCVE.getTitle() == '': 63 | self.currCVE.setTitle (content) 64 | elif ((self.CurrentData == "Note") and not(self.skipflag)): 65 | self.currCVE.setDescription(content ) 66 | elif self.CurrentData == "URL": 67 | if (content.find('http') >= 0): 68 | self.currCVE.addReference (content ) 69 | 70 | def findCVEs( pattern): 71 | ret = [] 72 | p = re.compile(pattern, re.IGNORECASE) 73 | for j in m_CVElist: 74 | entry = m_CVElist.get(j) 75 | if p.search(entry.getDescription().casefold() ): 76 | ret.append(j) 77 | return ret 78 | 79 | def CVEsetbyVendor(vendor): 80 | ret = set() 81 | vlst = findCVEs(vendor) 82 | for v in vlst: 83 | ret.add(v) 84 | return ret 85 | 86 | def CVEsetbyType (eqtype): 87 | ret = set() 88 | vlst = findCVEs(eqtype) 89 | for v in vlst: 90 | ret.add (v) 91 | return ret 92 | 93 | def CVEsetbyModel (modelinfo): 94 | ret = set() 95 | vlst = findCVEs(modelinfo) 96 | for v in vlst: 97 | ret.add(v) 98 | return ret 99 | 100 | 101 | def showCVE(name): 102 | m_CVElist[name].PP() 103 | 104 | 105 | class VULNERABILIY_FACTORY(): 106 | def __init__ (self, trace): 107 | self.filelist = [] 108 | self.trace = trace 109 | if self.trace: 110 | print ('VULNERABILITY factory constructed..') 111 | 112 | def load (self, filelist, ctypelist ): 113 | self.filelist = filelist 114 | parser = xml.sax.make_parser() 115 | parser.setFeature(xml.sax.handler.feature_namespaces, 0) 116 | Handler = CVEHandler() 117 | parser.setContentHandler( Handler ) 118 | 119 | if self.trace: 120 | print ('Loading CVE data..') 121 | 122 | for file in self.filelist: 123 | if self.trace: 124 | print ("Loading file:", file ) 125 | parser.parse(file) 126 | 127 | if self.trace: 128 | print ("CVE data loaded:", len(m_CVElist), "entries") 129 | 130 | # Once m_CVElist includes all of the CVEs read in, go through and filter out entries that are reserved or rejected 131 | 132 | filist_1 = findCVEs('RESERVED') 133 | for entry in filist_1: 134 | del m_CVElist [entry] 135 | 136 | if self.trace: 137 | print ("dropped RESERVED CVEs:", len(filist_1), "entries") 138 | 139 | filist_2 = findCVEs('REJECT') 140 | for entry in filist_2: 141 | del m_CVElist [entry] 142 | 143 | if self.trace: 144 | print ("dropped REJECTED CVEs:", len(filist_2), "entries") 145 | 146 | 147 | ret = [] 148 | for v in ctypelist: 149 | 150 | vset = CVEsetbyVendor(str(v.getVendor())) 151 | mset = CVEsetbyModel(str(v.getDesc())) 152 | iset = vset & mset # intersection set of vendor and model, e.g., Ford and F150 153 | 154 | if self.trace: 155 | print ("Search for", v.getVendor(), v.getDesc(), 'found', len(iset), 'CVEs' ) 156 | 157 | if not(v.getType() == None ): 158 | if not(bool(iset)): 159 | eset = CVEsetbyType (str(v.getType())) 160 | iset = vset & eset # intersection set of vendor and type, e.g., Ford and Pickup 161 | if self.trace: 162 | print ("Alt. search for", v.getVendor(), v.getType(), 'found', len(iset), 'CVEs' ) 163 | 164 | if bool(iset): 165 | if self.trace: 166 | print ('CVE list:', iset) 167 | cvelist = list(iset) 168 | 169 | for s in cvelist: 170 | m_CVElist[s].setTarget (v.getDesc()) 171 | m_CVElist[s].getEffects() 172 | m_CVElist[s].getAccess() 173 | ret.append (m_CVElist[s]) 174 | v.addVulnerability (m_CVElist[s]) 175 | 176 | return ret 177 | 178 | -------------------------------------------------------------------------------- /cicat/requirements: -------------------------------------------------------------------------------- 1 | certifi>=2020.6.20 2 | chardet>=3.0.4 3 | et-xmlfile>=1.0.1 4 | idna>=2.10 5 | jdcal>=1.4.1 6 | mysql-connector-python>=8.0.21 7 | openpyxl>=3.0.5 8 | protobuf>=3.13.0 9 | requests>=2.24.0 10 | six>=1.15.0 11 | urllib3>=1.25.10 12 | -------------------------------------------------------------------------------- /cicat/utests/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitre/CICAT/501e87047e884f61133e1dd165593b4f1c803815/cicat/utests/.gitkeep -------------------------------------------------------------------------------- /cicat/utests/utests.bat: -------------------------------------------------------------------------------- 1 | python ..\generator\loaddata.py > loaddata_result.txt 2 | python ..\generator\topology.py > topology_result.txt 3 | python ..\generator\actorGEN.py > actorGEN_result.txt 4 | python ..\generator\TACSequence.py > TACSequence_result.txt 5 | python ..\generator\TTPFilter.py > TTPFilter_result.txt 6 | python ..\generator\ffactory.py > ffactory_result.txt 7 | python ..\generator\scenGEN.py > scenGEN_result.txt 8 | 9 | -------------------------------------------------------------------------------- /docker/Dockerfile: -------------------------------------------------------------------------------- 1 | # build docker image 2 | # docker build -f docker/Dockerfile -t cicat . 3 | # 4 | # run docker image 5 | # docker run -it --mount src=$(pwd)/shared,target=/home/cicat/shared,type=bind cicat 6 | # 7 | 8 | FROM python:3.6-slim-stretch 9 | 10 | RUN useradd -ms /bin/bash cicat 11 | WORKDIR /home/cicat 12 | RUN mkdir shared 13 | RUN apt-get update \ 14 | && apt-get install -y --no-install-recommends 15 | COPY cicat ./cicat 16 | RUN pwd 17 | RUN ls -Al 18 | RUN pip install --proxy="$http_proxy" -r ./cicat/requirements -I --no-cache-dir 19 | VOLUME /home/cicat/cicat/ 20 | ENTRYPOINT ["/bin/bash"] 21 | 22 | -------------------------------------------------------------------------------- /docker_info.txt: -------------------------------------------------------------------------------- 1 | *** Instructions for using docker *** 2 | 3 | From the cicat-master directory: 4 | 5 | Building the docker image: 6 | 7 | docker build -f docker/Dockerfile -t cicat . 8 | 9 | Running the docker image: 10 | 11 | docker run -it --mount src=$(pwd)/shared,target=/home/cicat/shared,type=bind cicat 12 | Note: --mount connects a location on the host to a set location in the docker image, which allows you to share files between the host and the docker image. 13 | 14 | From a terminal window: 15 | 16 | python cicat/generator/scenGEN.py >> shared/scenGEN_result.txt 17 | Note: This command runs the scenGEN.py utility and sends output to a file stored at the shared location. 18 | 19 | exit 20 | -------------------------------------------------------------------------------- /shared/placeholder.txt: -------------------------------------------------------------------------------- 1 | This file exists to prevent git from ignoring empty directories. 2 | The shared folder is used to share files between the host and the docker image. 3 | --------------------------------------------------------------------------------