├── .gitignore ├── LICENSE ├── README.md ├── go.py └── utils.py /.gitignore: -------------------------------------------------------------------------------- 1 | atomic-red-team 2 | *.pyc 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 JimmyAstle 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Python Red Canary Atomic Yaml Parser 2 | 3 | This script will recursivly loop through the Atomic yamls and then print out attack behavior and detection/prevention 4 | rules based the command executors observed. 5 | 6 | To run this script you'll need to follow the below steps in a terminal window: 7 | 8 | 1. clone the Red Canary Atomic Red Team git repo into this project 9 | * `git clone https://github.com/redcanaryco/atomic-red-team.git` 10 | 11 | 2. Run the script 12 | * `python go.py` 13 | 14 | 15 | Note: I am not printing out the manual executor test cases but you can easily add that :) 16 | -------------------------------------------------------------------------------- /go.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import requests 4 | import json 5 | import os 6 | import pprint 7 | from time import sleep 8 | from sets import Set 9 | import traceback 10 | 11 | from utils import * 12 | 13 | test_sets = grabYamls() 14 | raw_tests = parseYamls(test_sets) 15 | 16 | #parsed powershell executor list 17 | executors_powershell = raw_tests[0] 18 | #parsed cmd executor list 19 | executors_command = raw_tests[1] 20 | #parsed manual executor list 21 | executors_manual = raw_tests[2] 22 | 23 | #normalize Yamls into eligible signature types for executor - "command" 24 | getSigData_command_cmd_interps = Cbsig.is_command_interpreter(executors_command) 25 | getSigData_command_lol_bins = Cbsig.is_lol_bin(executors_command) 26 | getSigData_command_dev_bins = Cbsig.is_dev_bin(executors_command) 27 | 28 | #normalize Yamls into eligible signature types for executor - "powershell" 29 | getSigData_powershell_cmd_interps = Cbsig.is_command_interpreter(executors_powershell) 30 | getSigData_powershell_lol_bins = Cbsig.is_lol_bin(executors_powershell) 31 | getSigData_powershell_dev_bins = Cbsig.is_dev_bin(executors_powershell) 32 | 33 | 34 | #create lists of eligable rules to be created 35 | getRuleData_command_cmd_interps = Cbsig.cmd_interp_rules(getSigData_command_cmd_interps) 36 | getRuleData_command_lol_bins = Cbsig.lol_bin_rules(getSigData_command_lol_bins) 37 | getRuleData_command_dev_bins = Cbsig.dev_tools_rules(getSigData_command_dev_bins) 38 | getRuleData_powershell_cmd_interps = Cbsig.cmd_interp_rules(getSigData_powershell_cmd_interps) 39 | getRuleData_powershell_lol_bins = Cbsig.lol_bin_rules(getSigData_powershell_lol_bins) 40 | getRuleData_powershell_dev_bins = Cbsig.dev_tools_rules(getSigData_powershell_dev_bins) 41 | 42 | 43 | #print rules for command executor cmd_interp atomics 44 | print "Command executor - cmd_interps" 45 | for tid,ruledata in getRuleData_command_cmd_interps.iteritems(): 46 | dedupe_ruledata = Set(ruledata) 47 | for rule in dedupe_ruledata: 48 | print "Potential detection indicator: {}".format(rule) 49 | 50 | #print rules for command executor lol_bin atomics 51 | print "Command executor - lol bins" 52 | for tid,ruledata in getRuleData_command_lol_bins.iteritems(): 53 | dedupe_ruledata = Set(ruledata) 54 | for rule in dedupe_ruledata: 55 | print "Potential detection indicator: {}".format(rule) 56 | 57 | #print rules for command executor dev_tools atomics 58 | print "Command executor - dev bins" 59 | for tid,ruledata in getRuleData_command_dev_bins.iteritems(): 60 | dedupe_ruledata = Set(ruledata) 61 | for rule in dedupe_ruledata: 62 | print "Potential detection indicator: {}".format(rule) 63 | 64 | 65 | #print rules for powershell executor cmd_interp atomics 66 | print "Powershell executor - cmd_interps" 67 | for tid,ruledata in getRuleData_powershell_cmd_interps.iteritems(): 68 | dedupe_ruledata = Set(ruledata) 69 | for rule in dedupe_ruledata: 70 | print "Potential detection indicator: {}".format(rule) 71 | 72 | #print rules for powershell executor lol_bin atomics 73 | print "Powershell executor - lol bins" 74 | for tid,ruledata in getRuleData_powershell_lol_bins.iteritems(): 75 | dedupe_ruledata = Set(ruledata) 76 | for rule in dedupe_ruledata: 77 | print "Potential detection indicator: {}".format(rule) 78 | 79 | #print rules for powershell executor dev_tools atomics 80 | print "Powershell executor - dev bins" 81 | for tid,ruledata in getRuleData_powershell_dev_bins.iteritems(): 82 | dedupe_ruledata = Set(ruledata) 83 | for rule in dedupe_ruledata: 84 | print "Potential detection indicator: {}".format(rule) 85 | 86 | 87 | 88 | 89 | 90 | -------------------------------------------------------------------------------- /utils.py: -------------------------------------------------------------------------------- 1 | import yaml 2 | import os 3 | import time 4 | import requests 5 | import json 6 | from pprint import pprint 7 | 8 | #Return list of yamls to parse relative to the atomic-red-team dir 9 | 10 | windows_executors = ['powershell', 'command_prompt'] 11 | 12 | def grabYamls(): 13 | yamls = [] 14 | #Grab the current dir 15 | currentdir = os.path.dirname(__file__) 16 | #Get relative path for atomic test descriptions 17 | atomic_red_dir = os.path.join(currentdir, 'atomic-red-team', 'atomics') 18 | 19 | #Grab all the yamls 20 | for root, subdirs, files in os.walk(atomic_red_dir): 21 | for filename in files: 22 | file_path = os.path.join(root, filename) 23 | if ".yaml" in file_path: 24 | yamls.append(file_path) 25 | 26 | return yamls 27 | 28 | def parseYamls(yaml_list): 29 | executor_counts = {} 30 | executor_counts['powershell'] = 0 31 | executor_counts['command_prompt'] = 0 32 | executor_counts['manual'] = 0 33 | powershell_command_dict = {} 34 | cmd_command_dict = {} 35 | manual_command_dict = {} 36 | powershell_commands = [] 37 | cmd_commands = [] 38 | manual_commands = [] 39 | for atomic_yaml in yaml_list: 40 | with open(atomic_yaml, 'r') as atomic_set: 41 | objAtomicYaml = yaml.load(atomic_set) 42 | attckTID = objAtomicYaml['attack_technique'] 43 | powershell_command_dict.setdefault(attckTID, None) 44 | cmd_command_dict.setdefault(attckTID, None) 45 | manual_command_dict.setdefault(attckTID, None) 46 | testCases = objAtomicYaml['atomic_tests'] 47 | for item in testCases: 48 | if "windows" in item.get("supported_platforms"): 49 | #print objAtomicYaml['attack_technique'] 50 | executors = item.get("executor") 51 | #print executors['name'] 52 | if "powershell" in executors['name']: 53 | #print item.get("name") 54 | #print "Executor: Powershell" 55 | #print "Powershell command: ", executors['command'] 56 | executor_counts['powershell'] += 1 57 | powershell_commands.append(executors['command']) 58 | elif "command_prompt" in executors['name']: 59 | #print item.get("name") 60 | #print "Executor: Command Prompt" 61 | #print "Interactive Command: ", executors['command'] 62 | executor_counts['command_prompt'] += 1 63 | cmd_commands.append(executors['command']) 64 | elif "manual" in executors['name']: 65 | #print item.get("name") 66 | #print "Executor: Manual" 67 | #print "Manual Test Case: ", executors['steps'] 68 | executor_counts['manual'] += 1 69 | manual_commands.append(executors['steps']) 70 | elif "sh" in executors['name']: 71 | break 72 | else: 73 | print "Could match executor for count" 74 | print executors 75 | time.sleep(10) 76 | 77 | powershell_command_dict[attckTID] = powershell_commands 78 | powershell_commands = [] 79 | cmd_command_dict[attckTID] = cmd_commands 80 | cmd_commands = [] 81 | manual_command_dict[attckTID] = manual_commands 82 | manual_commands = [] 83 | 84 | return powershell_command_dict, cmd_command_dict, manual_command_dict 85 | 86 | 87 | class Cbsig: 88 | windows_cmd_interps = ["powershell.exe", "wmic.exe", "cmd.exe", "wscript.exe", "cscript.exe", "mshta.exe"] 89 | lol_bins = ["certutil.exe", "pcalua.exe", "forfiles", "mavinject", "regsvr32.exe", "rundll32.exe", "cmstp.exe"] 90 | dev_bins = ["csc.exe", "installutil.exe", "msbuild.exe", "regsvcs.exe", "regasm.exe"] 91 | special_bins = ["services.exe", "sc", "wmiprvse.exe", "sc.exe", "bitsadmin.exe"] 92 | manual_strings = ["word"] 93 | 94 | credential = ["mimi"] 95 | exec_memory = ["iex"] 96 | makes_netconn = ["http", "javascript", "url"] 97 | injects = ["mavinject"] 98 | 99 | @staticmethod 100 | def is_command_interpreter(command_list): 101 | command_interpreter_dict = {} 102 | command_interpreter_list = [] 103 | for tid, attack in command_list.iteritems(): 104 | if attack: 105 | for one_liner in attack: 106 | if any(x in one_liner.lower() for x in Cbsig.windows_cmd_interps): 107 | #print "*******IS COMMAND INTERP*******" 108 | #print "{} - {}".format(tid,one_liner) 109 | command_interpreter_list.append(one_liner) 110 | if command_interpreter_list: 111 | command_interpreter_dict[tid] = command_interpreter_list 112 | command_interpreter_list = [] 113 | return command_interpreter_dict 114 | 115 | @staticmethod 116 | def is_lol_bin(command_list): 117 | lol_bin_dict = {} 118 | lol_bin_list = [] 119 | for tid, attack in command_list.iteritems(): 120 | if attack: 121 | for one_liner in attack: 122 | if any(x in one_liner.lower() for x in Cbsig.lol_bins): 123 | #print "*******IS LOL BIN*******" 124 | #print "{} - {}".format(tid,one_liner) 125 | lol_bin_list.append(one_liner) 126 | if lol_bin_list: 127 | lol_bin_dict[tid] = lol_bin_list 128 | lol_bin_list = [] 129 | return lol_bin_dict 130 | 131 | @staticmethod 132 | def is_special_bin(command_list): 133 | special_bin_dict = {} 134 | special_bin_list = [] 135 | for tid, attack in command_list.iteritems(): 136 | if attack: 137 | for one_liner in attack: 138 | if any(x in one_liner.lower() for x in Cbsig.special_bins): 139 | #print "*******IS SPECIAL BIN*******" 140 | #print "{} - {}".format(tid,one_liner) 141 | special_bin_list.append(one_liner) 142 | if special_bin_list: 143 | special_bin_dict[tid] = special_bin_list 144 | special_bin_list = [] 145 | return special_bin_dict 146 | 147 | @staticmethod 148 | def is_dev_bin(command_list): 149 | dev_bin_dict = {} 150 | dev_bin_list = [] 151 | for tid, attack in command_list.iteritems(): 152 | if attack: 153 | for one_liner in attack: 154 | if any(x in one_liner.lower() for x in Cbsig.dev_bins): 155 | #print "*******IS DEV BIN*******" 156 | #print "{} - {}".format(tid,one_liner) 157 | dev_bin_list.append(one_liner) 158 | if dev_bin_list: 159 | dev_bin_dict[tid] = dev_bin_list 160 | dev_bin_list = [] 161 | return dev_bin_dict 162 | 163 | @staticmethod 164 | def cmd_interp_rules(indicators): 165 | print "Generating command rules" 166 | print "*************************************" 167 | cmd_interp_rules_dict = {} 168 | cmd_interp_rule_list = [] 169 | for tid,attack in indicators.iteritems(): 170 | for one_liner in attack: 171 | #Check the list of cmd interps for cred theft atomic indicators 172 | if any(x in one_liner.lower() for x in Cbsig.credential): 173 | print "Indicator for command_interpreter performing credential theft" 174 | if "powershell" in one_liner.lower(): 175 | print "Powershell wants to perform credential theft" 176 | print "{} - {}".format(tid,one_liner) 177 | cmd_interp_rule_list.append("Powershell*.exe:MEMORY_SCRAPE") 178 | elif "cscript" in one_liner.lower(): 179 | print "cscript wants to perform credential theft" 180 | print "{} - {}".format(tid,one_liner) 181 | cmd_interp_rule_list.append("cscript.exe:MEMORY_SCRAPE") 182 | elif "wscript" in one_liner.lower(): 183 | print "wscript wants to perform credential theft" 184 | print "{} - {}".format(tid,one_liner) 185 | cmd_interp_rule_list.append("wscript.exe:MEMORY_SCRAPE") 186 | #Check ths list of cmd interps for execution of code from memory atomic indicators 187 | if any(x in one_liner.lower() for x in Cbsig.exec_memory): 188 | print "Indicator for command_interpreter executing code from memory" 189 | if "powershell" in one_liner.lower(): 190 | print "Powershell wants to execute code from memory" 191 | print "{} - {}".format(tid,one_liner) 192 | cmd_interp_rule_list.append("Powershell*.exe:RUN_INMEMORY_CODE") 193 | cmd_interp_rule_list.append("Powershell*.exe:INVOKE_CMD_INTERPRETER") 194 | #Check the list of cmd interps for netconn activity from atomic indicators 195 | if any(x in one_liner.lower() for x in Cbsig.makes_netconn): 196 | print "Indicator for command_interpreter attempting to make netconns" 197 | if "powershell" in one_liner.lower(): 198 | print "Powershell wants to make a netconn" 199 | print "{} - {}".format(tid,one_liner) 200 | cmd_interp_rule_list.append("Powershell*.exe:NETWORK") 201 | if "mshta" in one_liner.lower(): 202 | print "mshta wants to make a netconn" 203 | print "{} - {}".format(tid,one_liner) 204 | cmd_interp_rule_list.append("mshta.exe:NETWORK") 205 | if cmd_interp_rule_list: 206 | cmd_interp_rules_dict[tid] = cmd_interp_rule_list 207 | 208 | cmd_interp_rule_list = [] 209 | 210 | return cmd_interp_rules_dict 211 | 212 | 213 | 214 | @staticmethod 215 | def lol_bin_rules(indicators): 216 | print "Generatng LOL Bin Rules" 217 | print "*****************************" 218 | lol_bin_rules_dict = {} 219 | lol_bin_rule_list = [] 220 | for tid,attack in indicators.iteritems(): 221 | for one_liner in attack: 222 | #Check the list of lol bins for netconn activity from atomic indicators 223 | if any(x in one_liner.lower() for x in Cbsig.makes_netconn): 224 | print "Indicator for command_interpreter attempting to make netconns" 225 | if "certutil" in one_liner.lower(): 226 | print "certutil wants to make a netconn" 227 | print "{} - {}".format(tid,one_liner) 228 | lol_bin_rule_list.append("certutil.exe:NETWORK") 229 | if "regsvr32.exe" in one_liner.lower(): 230 | print "regsvr32 want to make a netconn" 231 | print "{} - {}".format(tid,one_liner) 232 | lol_bin_rule_list.append("regsvr32.exe:NETWORK") 233 | if "rundll32.exe" in one_liner.lower(): 234 | print "rundll32 wants to invoke cmd-interp" 235 | print "{} - {}".format(tid,one_liner) 236 | lol_bin_rule_list.append("rundll32.exe:INVOKE_CMD_INTERPRETER") 237 | #prevention rules for pcalua atomics 238 | if "pcalua" in one_liner.lower(): 239 | print "pcalua is being used to execute code" 240 | print "{} - {}".format(tid,one_liner) 241 | lol_bin_rule_list.append("pcalua.exe:NETWORK") 242 | lol_bin_rule_list.append("pcalua.exe:INVOKE_CMD_INTERPRETER") 243 | lol_bin_rule_list.append("pcalua.exe:POL_INVOKE_NOT_TRUSTED") 244 | #prevention for forfiles atomics 245 | if "forfiles" in one_liner.lower(): 246 | print "forfiles is being used to execute code" 247 | print "{} - {}".format(tid,one_liner) 248 | lol_bin_rule_list.append("forfiles.exe:INVOKE_CMD_INTERPRETER") 249 | lol_bin_rule_list.append("forfiles.exe:POL_INVOKE_NOT_TRUSTED") 250 | #prevention for cmstp.exe atomic 251 | if "cmstp.exe" in one_liner.lower(): 252 | print "cmstp is being used to execute code" 253 | print "{} - {}".format(tid,one_liner) 254 | lol_bin_rule_list.append("cmstp.exe:RUN") 255 | #general prevention for sketch atomic regsvr32 atomics 256 | if "regsvr32.exe" in one_liner.lower(): 257 | if "dll_name" in one_liner.lower(): 258 | print "regsvr32 is loading sketchy stuff too!" 259 | print "{} - {}".format(tid,one_liner) 260 | lol_bin_rule_list.append("regsvr32.exe:POL_INVOKE_NOT_TRUSTED") 261 | if lol_bin_rule_list: 262 | lol_bin_rules_dict[tid] = lol_bin_rule_list 263 | 264 | lol_bin_rule_list = [] 265 | 266 | return lol_bin_rules_dict 267 | 268 | @staticmethod 269 | def dev_tools_rules(indicators): 270 | print "Generating Dev Tools Rules" 271 | print "*************************************" 272 | dev_tools_rules_dict = {} 273 | dev_tools_rules_list = [] 274 | for tid,attack in indicators.iteritems(): 275 | for one_liner in attack: 276 | #Check the list of msbuild is being invoked as an atomic 277 | if "msbuild" in one_liner.lower(): 278 | print "MSbuild is being used to execute code" 279 | print "{} - {}".format(tid,one_liner) 280 | dev_tools_rules_list.append("msbuild.exe:NETWORK") 281 | if "csc.exe" in one_liner.lower(): 282 | print "csc is being used to compile/execute payloads" 283 | print "{} - {}".format(tid,one_liner) 284 | dev_tools_rules_list.append("csc.exe:NETWORK") 285 | if "regasm.exe" in one_liner.lower(): 286 | print "regasm is being used to execute code" 287 | print "{} - {}".format(tid,one_liner) 288 | dev_tools_rules_list.append("regasm.exe:NETWORK") 289 | if "regsvcs.exe" in one_liner.lower(): 290 | print "regsvcs.exe is being used to execute code" 291 | print "{} - {}".format(tid,one_liner) 292 | dev_tools_rules_list.append("regsvcs.exe:NETWORK") 293 | if "installutil" in one_liner.lower(): 294 | print "installutil is being used to execute code" 295 | print "{} - {}".format(tid,one_liner) 296 | dev_tools_rules_list.append("installutil.exe:RUN") 297 | if dev_tools_rules_list: 298 | dev_tools_rules_dict[tid] = dev_tools_rules_list 299 | dev_tools_rules_list = [] 300 | 301 | return dev_tools_rules_dict 302 | 303 | 304 | 305 | 306 | @staticmethod 307 | def strict_rules_generic(enabled): 308 | pass 309 | 310 | 311 | 312 | 313 | --------------------------------------------------------------------------------