From d2add200559994f457263fc062f6b5dd1cc4999d Mon Sep 17 00:00:00 2001 From: salvacybersec Date: Sat, 11 Apr 2026 21:19:12 +0300 Subject: [PATCH] reorganize --- .gitignore | 2 + README.md | 14 + build.py | 580 ++- .../.claude-plugin/marketplace.json | 37 + .../.claude-plugin/plugin.json | 5 + .../.github/FUNDING.yml | 2 + .../.github/ISSUE_TEMPLATE/bug-report.yml | 50 + .../.github/ISSUE_TEMPLATE/config.yml | 8 + .../.github/ISSUE_TEMPLATE/improve-skill.md | 19 + .../ISSUE_TEMPLATE/new-skill-request.yml | 58 + .../.github/ISSUE_TEMPLATE/new-skill.md | 25 + .../ISSUE_TEMPLATE/skill-improvement.yml | 41 + .../workflows/sync-marketplace-version.yml | 39 + .../.github/workflows/update-index.yml | 70 + .../.github/workflows/validate-skills.yml | 128 + .../ATTACK_COVERAGE.md | 509 +++ .../CITATION.cff | 32 + .../CODE_OF_CONDUCT.md | 83 + .../CONTRIBUTING.md | 74 + .../anthropic-cybersecurity-skills/LICENSE | 201 + .../anthropic-cybersecurity-skills/README.md | 358 ++ .../SECURITY.md | 47 + .../assets/.gitkeep | 0 .../assets/banner.png | Bin 0 -> 678283 bytes .../anthropic-cybersecurity-skills/index.json | 1 + .../mappings/README.md | 88 + .../mappings/attack-navigator-layer.json | 3594 +++++++++++++++++ .../mappings/mitre-attack/README.md | 102 + .../mappings/mitre-attack/coverage-summary.md | 177 + .../mappings/nist-csf/README.md | 133 + .../mappings/nist-csf/csf-alignment.md | 102 + .../mappings/owasp/README.md | 177 + .../_shared/community-skills/olla/SKILL.md | 370 ++ .../olla/references/olla-docs-links.md | 87 + .../LICENSE | 201 + .../SKILL.md | 242 ++ .../references/api-reference.md | 99 + .../scripts/agent.py | 181 + .../LICENSE | 201 + .../SKILL.md | 84 + .../references/api-reference.md | 94 + .../scripts/agent.py | 258 ++ .../LICENSE | 201 + .../SKILL.md | 61 + .../references/api-reference.md | 69 + .../scripts/agent.py | 228 ++ .../analyzing-api-gateway-access-logs/LICENSE | 201 + .../SKILL.md | 71 + .../references/api-reference.md | 58 + .../scripts/agent.py | 176 + .../LICENSE | 201 + .../SKILL.md | 285 ++ .../references/api-reference.md | 97 + .../scripts/agent.py | 244 ++ .../LICENSE | 201 + .../SKILL.md | 78 + .../references/api-reference.md | 54 + .../scripts/agent.py | 178 + .../LICENSE | 201 + .../SKILL.md | 354 ++ .../references/api-reference.md | 97 + .../scripts/agent.py | 199 + .../LICENSE | 201 + .../SKILL.md | 296 ++ .../assets/template.md | 22 + .../references/api-reference.md | 92 + .../references/standards.md | 15 + .../references/workflows.md | 19 + .../scripts/agent.py | 254 ++ .../scripts/process.py | 31 + .../LICENSE | 201 + .../SKILL.md | 237 ++ .../assets/template.md | 39 + .../references/api-reference.md | 110 + .../references/standards.md | 24 + .../references/workflows.md | 31 + .../scripts/agent.py | 253 ++ .../scripts/process.py | 169 + .../LICENSE | 201 + .../SKILL.md | 336 ++ .../references/api-reference.md | 97 + .../scripts/agent.py | 210 + .../LICENSE | 201 + .../SKILL.md | 70 + .../references/api-reference.md | 49 + .../scripts/agent.py | 200 + .../LICENSE | 201 + .../SKILL.md | 381 ++ .../assets/template.md | 95 + .../references/api-reference.md | 112 + .../references/standards.md | 94 + .../references/workflows.md | 72 + .../scripts/agent.py | 239 ++ .../scripts/process.py | 337 ++ .../LICENSE | 201 + .../SKILL.md | 61 + .../references/api-reference.md | 95 + .../scripts/agent.py | 234 ++ .../LICENSE | 201 + .../SKILL.md | 397 ++ .../references/api-reference.md | 112 + .../scripts/agent.py | 214 + .../skills/analyzing-cyber-kill-chain/LICENSE | 201 + .../analyzing-cyber-kill-chain/SKILL.md | 140 + .../references/api-reference.md | 96 + .../scripts/agent.py | 241 ++ .../analyzing-disk-image-with-autopsy/LICENSE | 201 + .../SKILL.md | 264 ++ .../references/api-reference.md | 118 + .../scripts/agent.py | 218 + .../LICENSE | 201 + .../SKILL.md | 304 ++ .../references/api-reference.md | 112 + .../scripts/agent.py | 229 ++ .../LICENSE | 201 + .../SKILL.md | 341 ++ .../references/api-reference.md | 116 + .../scripts/agent.py | 238 ++ .../LICENSE | 201 + .../SKILL.md | 327 ++ .../references/api-reference.md | 121 + .../scripts/agent.py | 229 ++ .../LICENSE | 201 + .../SKILL.md | 67 + .../references/api-reference.md | 103 + .../scripts/agent.py | 168 + .../LICENSE | 201 + .../SKILL.md | 311 ++ .../assets/template.md | 35 + .../references/api-reference.md | 90 + .../references/standards.md | 29 + .../references/workflows.md | 37 + .../scripts/agent.py | 267 ++ .../scripts/process.py | 162 + .../analyzing-heap-spray-exploitation/LICENSE | 201 + .../SKILL.md | 59 + .../references/api-reference.md | 55 + .../scripts/agent.py | 219 + .../LICENSE | 201 + .../SKILL.md | 163 + .../references/api-reference.md | 120 + .../scripts/agent.py | 251 ++ .../LICENSE | 201 + .../SKILL.md | 204 + .../assets/template.md | 80 + .../references/api-reference.md | 105 + .../references/standards.md | 43 + .../references/workflows.md | 83 + .../scripts/agent.py | 211 + .../scripts/process.py | 294 ++ .../analyzing-kubernetes-audit-logs/LICENSE | 201 + .../analyzing-kubernetes-audit-logs/SKILL.md | 73 + .../references/api-reference.md | 57 + .../scripts/agent.py | 201 + .../LICENSE | 201 + .../SKILL.md | 268 ++ .../references/api-reference.md | 89 + .../scripts/agent.py | 221 + .../analyzing-linux-elf-malware/LICENSE | 201 + .../analyzing-linux-elf-malware/SKILL.md | 341 ++ .../references/api-reference.md | 119 + .../scripts/agent.py | 230 ++ .../analyzing-linux-kernel-rootkits/LICENSE | 201 + .../analyzing-linux-kernel-rootkits/SKILL.md | 135 + .../references/api-reference.md | 92 + .../scripts/agent.py | 176 + .../analyzing-linux-system-artifacts/LICENSE | 201 + .../analyzing-linux-system-artifacts/SKILL.md | 332 ++ .../references/api-reference.md | 114 + .../scripts/agent.py | 263 ++ .../LICENSE | 201 + .../SKILL.md | 286 ++ .../assets/template.md | 26 + .../references/api-reference.md | 109 + .../references/standards.md | 22 + .../references/workflows.md | 44 + .../scripts/agent.py | 296 ++ .../scripts/process.py | 108 + .../LICENSE | 201 + .../SKILL.md | 339 ++ .../references/api-reference.md | 112 + .../scripts/agent.py | 246 ++ .../LICENSE | 201 + .../SKILL.md | 88 + .../references/api-reference.md | 126 + .../scripts/agent.py | 219 + .../LICENSE | 201 + .../SKILL.md | 107 + .../assets/template.md | 87 + .../references/api-reference.md | 68 + .../references/standards.md | 42 + .../references/workflows.md | 96 + .../scripts/agent.py | 170 + .../scripts/process.py | 439 ++ .../LICENSE | 201 + .../SKILL.md | 297 ++ .../references/api-reference.md | 121 + .../scripts/agent.py | 253 ++ .../LICENSE | 201 + .../SKILL.md | 281 ++ .../references/api-reference.md | 68 + .../scripts/agent.py | 154 + .../LICENSE | 201 + .../SKILL.md | 125 + .../assets/template.md | 25 + .../references/api-reference.md | 60 + .../references/standards.md | 9 + .../references/workflows.md | 11 + .../scripts/agent.py | 152 + .../LICENSE | 201 + .../SKILL.md | 63 + .../references/api-reference.md | 84 + .../scripts/agent.py | 204 + .../LICENSE | 201 + .../SKILL.md | 313 ++ .../references/api-reference.md | 99 + .../scripts/agent.py | 243 ++ .../LICENSE | 201 + .../SKILL.md | 82 + .../references/api-reference.md | 58 + .../scripts/agent.py | 207 + .../LICENSE | 201 + .../SKILL.md | 255 ++ .../assets/template.md | 31 + .../references/api-reference.md | 55 + .../references/standards.md | 28 + .../references/workflows.md | 46 + .../scripts/agent.py | 159 + .../scripts/process.py | 118 + .../LICENSE | 201 + .../SKILL.md | 212 + .../assets/template.md | 25 + .../references/api-reference.md | 87 + .../references/standards.md | 9 + .../references/workflows.md | 11 + .../scripts/agent.py | 190 + .../LICENSE | 201 + .../SKILL.md | 65 + .../references/api-reference.md | 48 + .../scripts/agent.py | 200 + .../LICENSE | 201 + .../SKILL.md | 57 + .../references/api-reference.md | 90 + .../scripts/agent.py | 188 + .../LICENSE | 201 + .../SKILL.md | 266 ++ .../references/api-reference.md | 108 + .../scripts/agent.py | 205 + .../LICENSE | 201 + .../SKILL.md | 334 ++ .../references/api-reference.md | 128 + .../scripts/agent.py | 243 ++ .../LICENSE | 201 + .../SKILL.md | 228 ++ .../references/api-reference.md | 98 + .../scripts/agent.py | 220 + .../LICENSE | 201 + .../SKILL.md | 59 + .../references/api-reference.md | 66 + .../scripts/agent.py | 253 ++ .../LICENSE | 201 + .../SKILL.md | 353 ++ .../references/api-reference.md | 98 + .../references/standards.md | 16 + .../references/workflows.md | 19 + .../scripts/agent.py | 178 + .../LICENSE | 201 + .../SKILL.md | 309 ++ .../references/api-reference.md | 117 + .../scripts/agent.py | 241 ++ .../analyzing-pdf-malware-with-pdfid/LICENSE | 201 + .../analyzing-pdf-malware-with-pdfid/SKILL.md | 351 ++ .../references/api-reference.md | 119 + .../scripts/agent.py | 240 ++ .../LICENSE | 201 + .../SKILL.md | 72 + .../references/api-reference.md | 100 + .../scripts/agent.py | 261 ++ .../LICENSE | 201 + .../SKILL.md | 67 + .../references/api-reference.md | 101 + .../scripts/agent.py | 307 ++ .../LICENSE | 201 + .../SKILL.md | 68 + .../references/api-reference.md | 59 + .../scripts/agent.py | 195 + .../LICENSE | 201 + .../SKILL.md | 323 ++ .../references/api-reference.md | 116 + .../scripts/agent.py | 215 + .../LICENSE | 201 + .../SKILL.md | 337 ++ .../references/api-reference.md | 147 + .../scripts/agent.py | 352 ++ .../LICENSE | 201 + .../SKILL.md | 338 ++ .../references/api-reference.md | 100 + .../scripts/agent.py | 197 + .../LICENSE | 201 + .../SKILL.md | 67 + .../references/api-reference.md | 83 + .../scripts/agent.py | 216 + .../LICENSE | 201 + .../SKILL.md | 173 + .../references/api-reference.md | 97 + .../scripts/agent.py | 218 + .../LICENSE | 201 + .../SKILL.md | 303 ++ .../references/api-reference.md | 275 ++ .../scripts/agent.py | 770 ++++ .../LICENSE | 201 + .../SKILL.md | 267 ++ .../references/api-reference.md | 95 + .../scripts/agent.py | 163 + .../LICENSE | 201 + .../SKILL.md | 395 ++ .../references/api-reference.md | 95 + .../scripts/agent.py | 171 + .../LICENSE | 201 + .../SKILL.md | 167 + .../assets/template.md | 25 + .../references/api-reference.md | 85 + .../references/standards.md | 9 + .../references/workflows.md | 11 + .../scripts/agent.py | 164 + .../LICENSE | 201 + .../SKILL.md | 282 ++ .../assets/template.md | 87 + .../references/api-reference.md | 89 + .../references/standards.md | 89 + .../references/workflows.md | 90 + .../scripts/agent.py | 151 + .../scripts/process.py | 353 ++ .../LICENSE | 201 + .../SKILL.md | 84 + .../references/api-reference.md | 84 + .../scripts/agent.py | 216 + .../LICENSE | 201 + .../SKILL.md | 118 + .../references/api-reference.md | 93 + .../scripts/agent.py | 180 + .../LICENSE | 201 + .../SKILL.md | 73 + .../references/api-reference.md | 64 + .../scripts/agent.py | 177 + .../LICENSE | 201 + .../SKILL.md | 78 + .../references/api-reference.md | 59 + .../scripts/agent.py | 175 + .../LICENSE | 201 + .../SKILL.md | 323 ++ .../references/api-reference.md | 75 + .../scripts/agent.py | 96 + .../LICENSE | 201 + .../SKILL.md | 363 ++ .../references/api-reference.md | 138 + .../scripts/agent.py | 563 +++ .../LICENSE | 201 + .../SKILL.md | 364 ++ .../references/api-reference.md | 73 + .../scripts/agent.py | 182 + .../LICENSE | 201 + .../SKILL.md | 68 + .../references/api-reference.md | 48 + .../scripts/agent.py | 219 + .../LICENSE | 201 + .../SKILL.md | 247 ++ .../references/api-reference.md | 67 + .../scripts/agent.py | 164 + .../LICENSE | 201 + .../SKILL.md | 298 ++ .../references/api-reference.md | 67 + .../scripts/agent.py | 165 + .../LICENSE | 201 + .../SKILL.md | 322 ++ .../references/api-reference.md | 87 + .../scripts/agent.py | 184 + .../LICENSE | 201 + .../SKILL.md | 114 + .../references/api-reference.md | 60 + .../scripts/agent.py | 188 + .../LICENSE | 201 + .../SKILL.md | 293 ++ .../references/api-reference.md | 70 + .../scripts/agent.py | 221 + .../LICENSE | 201 + .../SKILL.md | 217 + .../assets/template.md | 18 + .../references/api-reference.md | 83 + .../references/standards.md | 14 + .../references/workflows.md | 15 + .../scripts/agent.py | 183 + .../scripts/process.py | 38 + .../LICENSE | 201 + .../SKILL.md | 267 ++ .../references/api-reference.md | 90 + .../scripts/agent.py | 190 + .../LICENSE | 201 + .../SKILL.md | 268 ++ .../references/api-reference.md | 77 + .../scripts/agent.py | 191 + .../LICENSE | 201 + .../SKILL.md | 264 ++ .../references/api-reference.md | 66 + .../scripts/agent.py | 151 + .../auditing-gcp-iam-permissions/LICENSE | 201 + .../auditing-gcp-iam-permissions/SKILL.md | 319 ++ .../references/api-reference.md | 78 + .../scripts/agent.py | 160 + .../auditing-kubernetes-cluster-rbac/LICENSE | 201 + .../auditing-kubernetes-cluster-rbac/SKILL.md | 324 ++ .../references/api-reference.md | 72 + .../scripts/agent.py | 191 + .../LICENSE | 201 + .../SKILL.md | 351 ++ .../references/api-reference.md | 75 + .../scripts/agent.py | 172 + .../LICENSE | 201 + .../SKILL.md | 195 + .../references/api-reference.md | 133 + .../scripts/agent.py | 1027 +++++ .../skills/automating-ioc-enrichment/LICENSE | 201 + .../skills/automating-ioc-enrichment/SKILL.md | 210 + .../references/api-reference.md | 84 + .../scripts/agent.py | 227 ++ .../LICENSE | 201 + .../SKILL.md | 340 ++ .../references/api-reference.md | 53 + .../scripts/agent.py | 116 + .../LICENSE | 201 + .../SKILL.md | 322 ++ .../references/api-reference.md | 48 + .../scripts/agent.py | 126 + .../LICENSE | 201 + .../SKILL.md | 499 +++ .../references/api-reference.md | 76 + .../scripts/agent.py | 205 + .../LICENSE | 201 + .../SKILL.md | 206 + .../assets/template.md | 69 + .../references/api-reference.md | 52 + .../references/standards.md | 32 + .../references/workflows.md | 72 + .../scripts/agent.py | 102 + .../scripts/process.py | 180 + .../building-cloud-siem-with-sentinel/LICENSE | 201 + .../SKILL.md | 317 ++ .../references/api-reference.md | 70 + .../scripts/agent.py | 169 + .../LICENSE | 201 + .../SKILL.md | 254 ++ .../assets/template.md | 99 + .../references/api-reference.md | 49 + .../references/standards.md | 68 + .../references/workflows.md | 93 + .../scripts/agent.py | 142 + .../scripts/process.py | 338 ++ .../LICENSE | 201 + .../SKILL.md | 331 ++ .../references/api-reference.md | 73 + .../scripts/agent.py | 173 + .../LICENSE | 201 + .../SKILL.md | 258 ++ .../assets/template.md | 35 + .../references/api-reference.md | 56 + .../references/standards.md | 48 + .../references/workflows.md | 84 + .../scripts/agent.py | 135 + .../scripts/process.py | 174 + .../LICENSE | 201 + .../SKILL.md | 232 ++ .../assets/template.md | 51 + .../references/api-reference.md | 56 + .../references/standards.md | 53 + .../references/workflows.md | 112 + .../scripts/agent.py | 104 + .../scripts/process.py | 220 + .../LICENSE | 201 + .../SKILL.md | 691 ++++ .../references/api-reference.md | 73 + .../scripts/agent.py | 184 + .../LICENSE | 201 + .../SKILL.es.md | 291 ++ .../SKILL.md | 302 ++ .../references/api-reference.md | 79 + .../scripts/agent.py | 185 + .../LICENSE | 201 + .../SKILL.md | 266 ++ .../references/api-reference.md | 79 + .../scripts/agent.py | 230 ++ .../LICENSE | 201 + .../SKILL.md | 267 ++ .../assets/template.md | 79 + .../references/api-reference.md | 80 + .../references/standards.md | 44 + .../references/workflows.md | 155 + .../scripts/agent.py | 166 + .../scripts/process.py | 355 ++ .../LICENSE | 201 + .../SKILL.md | 372 ++ .../references/api-reference.md | 60 + .../scripts/agent.py | 172 + .../LICENSE | 201 + .../SKILL.md | 267 ++ .../assets/template.md | 85 + .../references/api-reference.md | 104 + .../references/standards.md | 77 + .../references/workflows.md | 103 + .../scripts/agent.py | 170 + .../scripts/process.py | 444 ++ .../LICENSE | 201 + .../SKILL.md | 342 ++ .../assets/template.md | 51 + .../references/api-reference.md | 55 + .../references/standards.md | 36 + .../references/workflows.md | 104 + .../scripts/agent.py | 175 + .../scripts/process.py | 292 ++ .../LICENSE | 201 + .../SKILL.md | 218 + .../assets/template.md | 30 + .../references/api-reference.md | 62 + .../references/standards.md | 29 + .../references/workflows.md | 56 + .../scripts/agent.py | 161 + .../scripts/process.py | 195 + .../LICENSE | 201 + .../SKILL.md | 99 + .../assets/template.md | 28 + .../references/api-reference.md | 64 + .../references/standards.md | 28 + .../references/workflows.md | 69 + .../scripts/agent.py | 167 + .../scripts/process.py | 304 ++ .../LICENSE | 201 + .../SKILL.md | 180 + .../references/api-reference.md | 94 + .../scripts/agent.py | 281 ++ .../LICENSE | 201 + .../SKILL.md | 313 ++ .../assets/template.md | 129 + .../references/api-reference.md | 81 + .../references/standards.md | 62 + .../references/workflows.md | 177 + .../scripts/agent.py | 156 + .../scripts/process.py | 355 ++ .../LICENSE | 201 + .../SKILL.md | 255 ++ .../assets/template.md | 52 + .../references/api-reference.md | 66 + .../references/standards.md | 46 + .../references/workflows.md | 82 + .../scripts/agent.py | 160 + .../scripts/process.py | 251 ++ .../building-soc-escalation-matrix/LICENSE | 201 + .../building-soc-escalation-matrix/SKILL.md | 216 + .../assets/template.md | 30 + .../references/api-reference.md | 65 + .../references/standards.md | 27 + .../references/workflows.md | 40 + .../scripts/agent.py | 149 + .../scripts/process.py | 221 + .../LICENSE | 201 + .../SKILL.md | 308 ++ .../references/api-reference.md | 70 + .../scripts/agent.py | 197 + .../LICENSE | 201 + .../SKILL.md | 284 ++ .../references/api-reference.md | 60 + .../scripts/agent.py | 161 + .../LICENSE | 201 + .../SKILL.md | 376 ++ .../references/api-reference.md | 73 + .../scripts/agent.py | 158 + .../LICENSE | 201 + .../SKILL.md | 340 ++ .../references/api-reference.md | 88 + .../scripts/agent.py | 148 + .../LICENSE | 201 + .../SKILL.md | 91 + .../assets/template.md | 107 + .../references/api-reference.md | 66 + .../references/standards.md | 41 + .../references/workflows.md | 81 + .../scripts/agent.py | 164 + .../scripts/process.py | 77 + .../LICENSE | 201 + .../SKILL.md | 309 ++ .../assets/template.md | 48 + .../references/api-reference.md | 66 + .../references/standards.md | 42 + .../references/workflows.md | 63 + .../scripts/agent.py | 170 + .../scripts/process.py | 251 ++ .../LICENSE | 201 + .../SKILL.md | 337 ++ .../references/api-reference.md | 57 + .../scripts/agent.py | 170 + .../LICENSE | 201 + .../SKILL.md | 318 ++ .../assets/template.md | 33 + .../references/api-reference.md | 68 + .../references/standards.md | 28 + .../references/workflows.md | 31 + .../scripts/agent.py | 163 + .../scripts/process.py | 179 + .../LICENSE | 201 + .../SKILL.md | 288 ++ .../assets/template.md | 28 + .../references/api-reference.md | 48 + .../references/standards.md | 27 + .../references/workflows.md | 35 + .../scripts/agent.py | 190 + .../scripts/process.py | 176 + .../LICENSE | 201 + .../SKILL.md | 248 ++ .../assets/template.md | 69 + .../references/api-reference.md | 71 + .../references/standards.md | 36 + .../references/workflows.md | 43 + .../scripts/agent.py | 180 + .../scripts/process.py | 229 ++ .../LICENSE | 201 + .../SKILL.md | 181 + .../assets/template.md | 55 + .../references/api-reference.md | 53 + .../references/standards.md | 34 + .../references/workflows.md | 47 + .../scripts/agent.py | 169 + .../scripts/process.py | 229 ++ .../LICENSE | 201 + .../SKILL.md | 347 ++ .../references/api-reference.md | 47 + .../scripts/agent.py | 171 + .../LICENSE | 201 + .../SKILL.md | 279 ++ .../references/api-reference.md | 63 + .../scripts/agent.py | 182 + .../LICENSE | 201 + .../SKILL.md | 268 ++ .../references/api-reference.md | 60 + .../scripts/agent.py | 195 + .../LICENSE | 201 + .../SKILL.md | 143 + .../references/api-reference.md | 51 + .../scripts/agent.py | 183 + .../LICENSE | 201 + .../SKILL.md | 186 + .../assets/template.md | 107 + .../references/api-reference.md | 80 + .../references/standards.md | 47 + .../references/workflows.md | 92 + .../scripts/agent.py | 173 + .../scripts/process.py | 346 ++ .../LICENSE | 201 + .../SKILL.md | 251 ++ .../assets/template.md | 68 + .../references/api-reference.md | 79 + .../references/standards.md | 62 + .../references/workflows.md | 114 + .../scripts/agent.py | 175 + .../scripts/process.py | 418 ++ .../conducting-api-security-testing/LICENSE | 201 + .../conducting-api-security-testing/SKILL.md | 187 + .../references/api-reference.md | 62 + .../scripts/agent.py | 228 ++ .../LICENSE | 201 + .../SKILL.md | 294 ++ .../references/api-reference.md | 64 + .../scripts/agent.py | 192 + .../LICENSE | 201 + .../SKILL.md | 295 ++ .../references/api-reference.md | 60 + .../scripts/agent.py | 170 + .../LICENSE | 201 + .../SKILL.md | 206 + .../assets/template.md | 35 + .../references/api-reference.md | 46 + .../references/standards.md | 26 + .../references/workflows.md | 38 + .../scripts/agent.py | 153 + .../scripts/process.py | 180 + .../LICENSE | 201 + .../SKILL.md | 184 + .../references/api-reference.md | 51 + .../scripts/agent.py | 191 + .../LICENSE | 201 + .../SKILL.md | 198 + .../assets/template.md | 266 ++ .../references/api-reference.md | 45 + .../references/standards.md | 106 + .../references/workflows.md | 131 + .../scripts/agent.py | 165 + .../scripts/process.py | 512 +++ .../LICENSE | 201 + .../SKILL.md | 308 ++ .../assets/template.md | 61 + .../references/api-reference.md | 45 + .../references/standards.md | 29 + .../references/workflows.md | 51 + .../scripts/agent.py | 147 + .../scripts/process.py | 232 ++ .../LICENSE | 201 + .../SKILL.md | 194 + .../assets/template.md | 49 + .../references/api-reference.md | 50 + .../references/standards.md | 27 + .../references/workflows.md | 58 + .../scripts/agent.py | 137 + .../scripts/process.py | 211 + .../LICENSE | 201 + .../SKILL.md | 227 ++ .../references/api-reference.md | 47 + .../scripts/agent.py | 177 + .../LICENSE | 201 + .../SKILL.md | 290 ++ .../references/api-reference.md | 66 + .../scripts/agent.py | 180 + .../LICENSE | 201 + .../SKILL.md | 284 ++ .../references/api-reference.md | 61 + .../scripts/agent.py | 173 + .../LICENSE | 201 + .../SKILL.md | 217 + .../references/api-reference.md | 54 + .../scripts/agent.py | 188 + .../LICENSE | 201 + .../SKILL.md | 198 + .../references/api-reference.md | 66 + .../scripts/agent.py | 209 + .../conducting-pass-the-ticket-attack/LICENSE | 201 + .../SKILL.md | 102 + .../assets/template.md | 39 + .../references/api-reference.md | 42 + .../references/standards.md | 35 + .../references/workflows.md | 100 + .../scripts/agent.py | 142 + .../scripts/process.py | 329 ++ .../LICENSE | 201 + .../SKILL.md | 253 ++ .../references/api-reference.md | 56 + .../scripts/agent.py | 207 + .../LICENSE | 201 + .../SKILL.md | 189 + .../assets/template.md | 92 + .../references/api-reference.md | 43 + .../references/standards.md | 31 + .../references/workflows.md | 60 + .../scripts/agent.py | 156 + .../scripts/process.py | 254 ++ .../LICENSE | 201 + .../SKILL.md | 334 ++ .../assets/template.md | 45 + .../references/api-reference.md | 44 + .../references/standards.md | 21 + .../references/workflows.md | 35 + .../scripts/agent.py | 160 + .../scripts/process.py | 196 + .../LICENSE | 201 + .../SKILL.md | 183 + .../assets/template.md | 56 + .../references/api-reference.md | 52 + .../references/standards.md | 30 + .../references/workflows.md | 83 + .../scripts/agent.py | 138 + .../scripts/process.py | 228 ++ .../LICENSE | 201 + .../SKILL.md | 136 + .../assets/template.md | 173 + .../references/api-reference.md | 46 + .../references/standards.md | 75 + .../references/workflows.md | 238 ++ .../scripts/agent.py | 139 + .../scripts/process.py | 591 +++ .../LICENSE | 201 + .../SKILL.md | 180 + .../references/api-reference.md | 69 + .../scripts/agent.py | 183 + .../LICENSE | 201 + .../SKILL.md | 65 + .../references/api-reference.md | 49 + .../scripts/agent.py | 145 + .../LICENSE | 201 + .../SKILL.md | 456 +++ .../assets/template.md | 40 + .../references/api-reference.md | 46 + .../references/standards.md | 44 + .../references/workflows.md | 81 + .../scripts/agent.py | 133 + .../scripts/process.py | 234 ++ .../LICENSE | 201 + .../SKILL.md | 94 + .../assets/template.md | 73 + .../references/api-reference.md | 44 + .../references/standards.md | 33 + .../references/workflows.md | 88 + .../scripts/agent.py | 138 + .../scripts/process.py | 455 +++ .../LICENSE | 201 + .../SKILL.md | 222 + .../assets/template.md | 43 + .../references/api-reference.md | 47 + .../references/standards.md | 33 + .../references/workflows.md | 47 + .../scripts/agent.py | 143 + .../scripts/process.py | 152 + .../configuring-hsm-for-key-storage/LICENSE | 201 + .../configuring-hsm-for-key-storage/SKILL.md | 104 + .../assets/template.md | 36 + .../references/api-reference.md | 50 + .../references/standards.md | 46 + .../references/workflows.md | 78 + .../scripts/agent.py | 141 + .../scripts/process.py | 353 ++ .../LICENSE | 201 + .../SKILL.md | 384 ++ .../assets/template.md | 52 + .../references/api-reference.md | 42 + .../references/standards.md | 31 + .../references/workflows.md | 60 + .../scripts/agent.py | 134 + .../scripts/process.py | 218 + .../LICENSE | 201 + .../SKILL.md | 64 + .../references/api-reference.md | 39 + .../scripts/agent.py | 143 + .../LICENSE | 201 + .../SKILL.md | 171 + .../assets/template.md | 81 + .../references/api-reference.md | 38 + .../references/standards.md | 70 + .../references/workflows.md | 179 + .../scripts/agent.py | 132 + .../scripts/process.py | 338 ++ .../LICENSE | 201 + .../SKILL.md | 148 + .../assets/template.md | 37 + .../references/api-reference.md | 46 + .../references/standards.md | 26 + .../references/workflows.md | 36 + .../scripts/agent.py | 139 + .../scripts/process.py | 268 ++ .../LICENSE | 201 + .../SKILL.md | 420 ++ .../references/api-reference.md | 65 + .../scripts/agent.py | 204 + .../LICENSE | 201 + .../SKILL.md | 140 + .../assets/template.md | 72 + .../references/api-reference.md | 43 + .../references/standards.md | 52 + .../references/workflows.md | 123 + .../scripts/agent.py | 140 + .../scripts/process.py | 465 +++ .../LICENSE | 201 + .../SKILL.md | 345 ++ .../references/api-reference.md | 70 + .../scripts/agent.py | 216 + .../LICENSE | 201 + .../SKILL.md | 405 ++ .../references/api-reference.md | 83 + .../scripts/agent.py | 202 + .../LICENSE | 201 + .../SKILL.es.md | 36 + .../SKILL.md | 400 ++ .../references/api-reference.md | 96 + .../scripts/agent.py | 238 ++ .../LICENSE | 201 + .../SKILL.md | 104 + .../assets/template.md | 67 + .../references/api-reference.md | 42 + .../references/standards.md | 67 + .../references/workflows.md | 87 + .../scripts/agent.py | 140 + .../scripts/process.py | 419 ++ .../LICENSE | 201 + .../SKILL.md | 285 ++ .../assets/template.md | 93 + .../references/api-reference.md | 36 + .../references/standards.md | 57 + .../references/workflows.md | 109 + .../scripts/agent.py | 116 + .../scripts/process.py | 233 ++ .../LICENSE | 201 + .../SKILL.md | 186 + .../assets/template.md | 20 + .../references/api-reference.md | 42 + .../references/standards.md | 6 + .../references/workflows.md | 8 + .../scripts/agent.py | 144 + .../scripts/process.py | 64 + .../LICENSE | 201 + .../SKILL.md | 310 ++ .../assets/template.md | 79 + .../references/api-reference.md | 35 + .../references/standards.md | 33 + .../references/workflows.md | 78 + .../scripts/agent.py | 127 + .../scripts/process.py | 319 ++ .../skills/containing-active-breach/LICENSE | 201 + .../skills/containing-active-breach/SKILL.md | 228 ++ .../references/api-reference.md | 84 + .../containing-active-breach/scripts/agent.py | 193 + .../LICENSE | 201 + .../SKILL.md | 287 ++ .../references/api-reference.md | 80 + .../scripts/agent.py | 173 + .../correlating-threat-campaigns/LICENSE | 201 + .../correlating-threat-campaigns/SKILL.md | 160 + .../references/api-reference.md | 76 + .../scripts/agent.py | 205 + .../deobfuscating-javascript-malware/LICENSE | 201 + .../deobfuscating-javascript-malware/SKILL.md | 359 ++ .../references/api-reference.md | 86 + .../scripts/agent.py | 210 + .../LICENSE | 201 + .../SKILL.md | 385 ++ .../assets/template.md | 66 + .../references/api-reference.md | 34 + .../references/standards.md | 41 + .../references/workflows.md | 50 + .../scripts/agent.py | 132 + .../scripts/process.py | 321 ++ .../LICENSE | 201 + .../SKILL.md | 253 ++ .../references/api-reference.md | 326 ++ .../scripts/Deploy-ADHoneytokens.ps1 | 659 +++ .../scripts/agent.py | 1321 ++++++ .../LICENSE | 201 + .../SKILL.md | 397 ++ .../assets/template.md | 55 + .../references/api-reference.md | 42 + .../references/standards.md | 33 + .../references/workflows.md | 65 + .../scripts/agent.py | 111 + .../scripts/process.py | 246 ++ .../LICENSE | 201 + .../SKILL.md | 192 + .../references/api-reference.md | 121 + .../scripts/agent.py | 260 ++ .../LICENSE | 201 + .../SKILL.md | 267 ++ .../assets/template.md | 89 + .../references/api-reference.md | 41 + .../references/standards.md | 49 + .../references/workflows.md | 130 + .../scripts/agent.py | 123 + .../scripts/process.py | 236 ++ .../LICENSE | 201 + .../SKILL.md | 204 + .../assets/template.md | 22 + .../references/api-reference.md | 46 + .../references/standards.md | 7 + .../references/workflows.md | 17 + .../scripts/agent.py | 104 + .../scripts/process.py | 85 + .../LICENSE | 201 + .../SKILL.md | 303 ++ .../references/api-reference.md | 52 + .../scripts/agent.py | 141 + .../deploying-ransomware-canary-files/LICENSE | 201 + .../SKILL.md | 97 + .../references/api-reference.md | 76 + .../scripts/agent.py | 555 +++ .../LICENSE | 201 + .../SKILL.md | 186 + .../assets/template.md | 58 + .../references/api-reference.md | 56 + .../references/standards.md | 73 + .../references/workflows.md | 119 + .../scripts/agent.py | 176 + .../scripts/process.py | 283 ++ .../LICENSE | 201 + .../SKILL.md | 432 ++ .../assets/template.md | 75 + .../references/api-reference.md | 56 + .../references/standards.md | 55 + .../references/workflows.md | 97 + .../scripts/agent.py | 152 + .../scripts/process.py | 340 ++ .../LICENSE | 201 + .../SKILL.md | 172 + .../references/api-reference.md | 151 + .../scripts/agent.py | 415 ++ .../LICENSE | 201 + .../SKILL.md | 331 ++ .../references/api-reference.md | 57 + .../scripts/agent.py | 179 + .../LICENSE | 201 + .../SKILL.md | 724 ++++ .../references/api-reference.md | 82 + .../scripts/agent.py | 251 ++ .../detecting-api-enumeration-attacks/LICENSE | 201 + .../SKILL.md | 421 ++ .../references/api-reference.md | 58 + .../scripts/agent.py | 196 + .../LICENSE | 201 + .../SKILL.md | 412 ++ .../references/api-reference.md | 63 + .../scripts/agent.py | 210 + .../LICENSE | 201 + .../SKILL.md | 288 ++ .../references/api-reference.md | 48 + .../scripts/agent.py | 199 + .../LICENSE | 201 + .../SKILL.md | 714 ++++ .../references/api-reference.md | 54 + .../scripts/agent.py | 210 + .../LICENSE | 201 + .../SKILL.md | 60 + .../references/api-reference.md | 59 + .../scripts/agent.py | 183 + .../LICENSE | 201 + .../SKILL.md | 276 ++ .../references/api-reference.md | 88 + .../scripts/agent.py | 202 + .../LICENSE | 201 + .../SKILL.md | 339 ++ .../assets/template.md | 23 + .../references/api-reference.md | 69 + .../references/standards.md | 21 + .../references/workflows.md | 22 + .../scripts/agent.py | 213 + .../scripts/process.py | 128 + .../LICENSE | 201 + .../SKILL.md | 59 + .../references/api-reference.md | 79 + .../scripts/agent.py | 200 + .../detecting-azure-lateral-movement/LICENSE | 201 + .../detecting-azure-lateral-movement/SKILL.md | 78 + .../references/api-reference.md | 81 + .../scripts/agent.py | 178 + .../LICENSE | 201 + .../SKILL.md | 241 ++ .../assets/template.md | 28 + .../references/api-reference.md | 61 + .../references/standards.md | 19 + .../references/workflows.md | 22 + .../scripts/agent.py | 187 + .../scripts/process.py | 240 ++ .../LICENSE | 201 + .../SKILL.md | 66 + .../references/api-reference.md | 106 + .../scripts/agent.py | 236 ++ .../LICENSE | 201 + .../SKILL.md | 78 + .../references/api-reference.md | 66 + .../scripts/agent.py | 171 + .../LICENSE | 201 + .../SKILL.md | 307 ++ .../references/api-reference.md | 92 + .../scripts/agent.py | 623 +++ .../LICENSE | 201 + .../SKILL.md | 405 ++ .../references/api-reference.md | 76 + .../scripts/agent.py | 159 + .../LICENSE | 201 + .../SKILL.md | 111 + .../assets/template.md | 32 + .../references/api-reference.md | 69 + .../references/standards.md | 30 + .../references/workflows.md | 53 + .../scripts/agent.py | 138 + .../scripts/process.py | 328 ++ .../LICENSE | 201 + .../SKILL.md | 109 + .../assets/template.md | 32 + .../references/api-reference.md | 66 + .../references/standards.md | 31 + .../references/workflows.md | 90 + .../scripts/agent.py | 157 + .../scripts/process.py | 326 ++ .../LICENSE | 201 + .../SKILL.md | 289 ++ .../references/api-reference.md | 80 + .../scripts/agent.py | 195 + .../LICENSE | 201 + .../SKILL.md | 1373 +++++++ .../references/api-reference.md | 195 + .../scripts/agent.py | 1124 ++++++ .../LICENSE | 201 + .../SKILL.md | 339 ++ .../references/api-reference.md | 69 + .../scripts/agent.py | 192 + .../LICENSE | 201 + .../SKILL.md | 239 ++ .../assets/template.md | 35 + .../references/api-reference.md | 82 + .../references/standards.md | 28 + .../references/workflows.md | 39 + .../scripts/agent.py | 159 + .../scripts/process.py | 213 + .../LICENSE | 201 + .../SKILL.md | 320 ++ .../assets/template.md | 42 + .../references/api-reference.md | 74 + .../references/standards.md | 53 + .../references/workflows.md | 81 + .../scripts/agent.py | 168 + .../scripts/process.py | 377 ++ .../LICENSE | 201 + .../SKILL.md | 336 ++ .../assets/template.md | 64 + .../references/api-reference.md | 88 + .../references/standards.md | 60 + .../references/workflows.md | 109 + .../scripts/agent.py | 175 + .../scripts/process.py | 232 ++ .../LICENSE | 201 + .../SKILL.es.md | 45 + .../SKILL.md | 64 + .../references/api-reference.md | 98 + .../scripts/agent.py | 227 ++ .../detecting-cryptomining-in-cloud/LICENSE | 201 + .../detecting-cryptomining-in-cloud/SKILL.md | 332 ++ .../references/api-reference.md | 76 + .../scripts/agent.py | 176 + .../LICENSE | 201 + .../SKILL.md | 154 + .../assets/template.md | 47 + .../references/api-reference.md | 77 + .../references/standards.md | 49 + .../references/workflows.md | 99 + .../scripts/agent.py | 176 + .../scripts/process.py | 154 + .../LICENSE | 201 + .../SKILL.md | 248 ++ .../references/api-reference.md | 174 + .../scripts/agent.py | 610 +++ .../detecting-dll-sideloading-attacks/LICENSE | 201 + .../SKILL.md | 103 + .../assets/template.md | 21 + .../references/api-reference.md | 66 + .../references/standards.md | 49 + .../references/workflows.md | 67 + .../scripts/agent.py | 156 + .../scripts/process.py | 181 + .../detecting-dnp3-protocol-anomalies/LICENSE | 201 + .../SKILL.md | 363 ++ .../references/api-reference.md | 68 + .../scripts/agent.py | 164 + .../LICENSE | 201 + .../SKILL.md | 444 ++ .../references/api-reference.md | 64 + .../scripts/agent.py | 168 + .../LICENSE | 201 + .../SKILL.md | 65 + .../references/api-reference.md | 81 + .../scripts/agent.py | 204 + .../LICENSE | 201 + .../SKILL.md | 97 + .../assets/template.md | 106 + .../references/api-reference.md | 65 + .../references/standards.md | 40 + .../references/workflows.md | 74 + .../scripts/agent.py | 149 + .../scripts/process.py | 81 + .../LICENSE | 201 + .../SKILL.md | 261 ++ .../assets/template.md | 39 + .../references/api-reference.md | 63 + .../references/standards.md | 33 + .../references/workflows.md | 74 + .../scripts/agent.py | 160 + .../scripts/process.py | 205 + .../LICENSE | 201 + .../SKILL.md | 86 + .../references/api-reference.md | 83 + .../scripts/agent.py | 212 + .../LICENSE | 201 + .../SKILL.md | 174 + .../assets/template.md | 19 + .../references/api-reference.md | 70 + .../references/standards.md | 7 + .../references/workflows.md | 8 + .../scripts/agent.py | 159 + .../scripts/process.py | 55 + .../LICENSE | 201 + .../SKILL.md | 424 ++ .../references/api-reference.md | 81 + .../scripts/agent.py | 205 + .../LICENSE | 201 + .../SKILL.md | 100 + .../references/api-reference.md | 64 + .../scripts/agent.py | 157 + .../detecting-golden-ticket-forgery/LICENSE | 201 + .../detecting-golden-ticket-forgery/SKILL.md | 64 + .../references/api-reference.md | 82 + .../scripts/agent.py | 201 + .../LICENSE | 201 + .../SKILL.md | 75 + .../references/api-reference.md | 60 + .../scripts/agent.py | 181 + .../LICENSE | 201 + .../SKILL.md | 96 + .../assets/template.md | 106 + .../references/api-reference.md | 68 + .../references/standards.md | 40 + .../references/workflows.md | 76 + .../scripts/agent.py | 168 + .../scripts/process.py | 79 + .../LICENSE | 201 + .../SKILL.md | 64 + .../references/api-reference.md | 75 + .../scripts/agent.py | 189 + .../detecting-kerberoasting-attacks/LICENSE | 201 + .../detecting-kerberoasting-attacks/SKILL.md | 97 + .../assets/template.md | 106 + .../references/api-reference.md | 49 + .../references/standards.md | 40 + .../references/workflows.md | 78 + .../scripts/agent.py | 179 + .../scripts/process.py | 80 + .../LICENSE | 201 + .../SKILL.md | 445 ++ .../references/api-reference.md | 82 + .../scripts/agent.py | 206 + .../LICENSE | 201 + .../SKILL.md | 109 + .../assets/template.md | 45 + .../references/api-reference.md | 55 + .../references/standards.md | 63 + .../references/workflows.md | 142 + .../scripts/agent.py | 142 + .../scripts/process.py | 335 ++ .../LICENSE | 201 + .../SKILL.md | 207 + .../assets/template.md | 122 + .../references/api-reference.md | 107 + .../references/standards.md | 20 + .../references/workflows.md | 145 + .../scripts/agent.py | 530 +++ .../scripts/process.py | 149 + .../LICENSE | 201 + .../SKILL.md | 519 +++ .../references/api-reference.md | 90 + .../scripts/agent.py | 501 +++ .../LICENSE | 201 + .../SKILL.md | 67 + .../references/api-reference.md | 86 + .../scripts/agent.py | 184 + .../LICENSE | 201 + .../SKILL.md | 78 + .../references/api-reference.md | 64 + .../scripts/agent.py | 215 + .../LICENSE | 201 + .../SKILL.md | 98 + .../assets/template.md | 107 + .../references/api-reference.md | 60 + .../references/standards.md | 41 + .../references/workflows.md | 73 + .../scripts/agent.py | 153 + .../scripts/process.py | 87 + .../LICENSE | 201 + .../SKILL.md | 275 ++ .../references/api-reference.md | 78 + .../scripts/agent.py | 174 + .../detecting-mobile-malware-behavior/LICENSE | 201 + .../SKILL.md | 207 + .../assets/template.md | 35 + .../references/api-reference.md | 71 + .../references/standards.md | 20 + .../references/workflows.md | 18 + .../scripts/agent.py | 150 + .../scripts/process.py | 235 ++ .../LICENSE | 201 + .../SKILL.md | 511 +++ .../references/api-reference.md | 67 + .../scripts/agent.py | 151 + .../LICENSE | 201 + .../SKILL.md | 396 ++ .../references/api-reference.md | 64 + .../scripts/agent.py | 169 + .../LICENSE | 201 + .../SKILL.md | 404 ++ .../references/api-reference.md | 91 + .../scripts/agent.py | 294 ++ .../LICENSE | 201 + .../SKILL.md | 303 ++ .../references/api-reference.md | 66 + .../scripts/agent.py | 171 + .../LICENSE | 201 + .../SKILL.md | 766 ++++ .../references/api-reference.md | 157 + .../scripts/agent.py | 377 ++ .../scripts/audit_smb_signing.ps1 | 353 ++ .../scripts/detect_ntlm_relay.py | 632 +++ .../detecting-oauth-token-theft/LICENSE | 201 + .../detecting-oauth-token-theft/SKILL.md | 277 ++ .../references/api-reference.md | 51 + .../scripts/agent.py | 184 + .../detecting-pass-the-hash-attacks/LICENSE | 201 + .../detecting-pass-the-hash-attacks/SKILL.md | 96 + .../assets/template.md | 106 + .../references/api-reference.md | 64 + .../references/standards.md | 40 + .../references/workflows.md | 77 + .../scripts/agent.py | 112 + .../scripts/process.py | 85 + .../detecting-pass-the-ticket-attacks/LICENSE | 201 + .../SKILL.md | 64 + .../references/api-reference.md | 104 + .../scripts/agent.py | 203 + .../LICENSE | 201 + .../SKILL.md | 371 ++ .../references/api-reference.md | 98 + .../scripts/agent.py | 225 ++ .../LICENSE | 201 + .../SKILL.md | 97 + .../assets/template.md | 107 + .../references/api-reference.md | 44 + .../references/standards.md | 41 + .../references/workflows.md | 77 + .../scripts/agent.py | 111 + .../scripts/process.py | 83 + .../LICENSE | 201 + .../SKILL.md | 257 ++ .../assets/template.md | 26 + .../references/api-reference.md | 52 + .../references/standards.md | 27 + .../references/workflows.md | 31 + .../scripts/agent.py | 98 + .../scripts/process.py | 142 + .../LICENSE | 201 + .../SKILL.md | 109 + .../assets/template.md | 29 + .../references/api-reference.md | 44 + .../references/standards.md | 79 + .../references/workflows.md | 121 + .../scripts/agent.py | 109 + .../scripts/process.py | 320 ++ .../LICENSE | 201 + .../SKILL.md | 364 ++ .../references/api-reference.md | 84 + .../scripts/agent.py | 233 ++ .../LICENSE | 201 + .../SKILL.md | 121 + .../assets/template.md | 35 + .../references/api-reference.md | 101 + .../references/standards.md | 39 + .../references/workflows.md | 95 + .../scripts/agent.py | 197 + .../scripts/process.py | 341 ++ .../LICENSE | 201 + .../SKILL.md | 207 + .../references/api-reference.md | 106 + .../scripts/agent.py | 314 ++ .../LICENSE | 201 + .../SKILL.md | 295 ++ .../assets/template.md | 45 + .../references/api-reference.md | 158 + .../references/standards.md | 49 + .../references/workflows.md | 115 + .../scripts/agent.py | 190 + .../scripts/process.py | 493 +++ .../detecting-rdp-brute-force-attacks/LICENSE | 201 + .../SKILL.md | 71 + .../references/api-reference.md | 68 + .../scripts/agent.py | 171 + .../skills/detecting-rootkit-activity/LICENSE | 201 + .../detecting-rootkit-activity/SKILL.md | 307 ++ .../references/api-reference.md | 87 + .../scripts/agent.py | 204 + .../LICENSE | 201 + .../SKILL.md | 338 ++ .../references/api-reference.md | 84 + .../scripts/agent.py | 221 + .../LICENSE | 201 + .../SKILL.md | 497 +++ .../references/api-reference.md | 121 + .../scripts/agent.py | 605 +++ .../detecting-service-account-abuse/LICENSE | 201 + .../detecting-service-account-abuse/SKILL.md | 96 + .../assets/template.md | 106 + .../references/api-reference.md | 81 + .../references/standards.md | 40 + .../references/workflows.md | 75 + .../scripts/agent.py | 139 + .../scripts/process.py | 78 + .../detecting-shadow-api-endpoints/LICENSE | 201 + .../detecting-shadow-api-endpoints/SKILL.md | 375 ++ .../references/api-reference.md | 94 + .../scripts/agent.py | 143 + .../detecting-shadow-it-cloud-usage/LICENSE | 201 + .../detecting-shadow-it-cloud-usage/SKILL.md | 59 + .../references/api-reference.md | 61 + .../scripts/agent.py | 285 ++ .../LICENSE | 201 + .../SKILL.md | 111 + .../assets/template.md | 58 + .../references/api-reference.md | 102 + .../references/standards.md | 48 + .../references/workflows.md | 151 + .../scripts/agent.py | 164 + .../scripts/process.py | 576 +++ .../LICENSE | 201 + .../SKILL.md | 64 + .../references/api-reference.md | 61 + .../scripts/agent.py | 232 ++ .../detecting-stuxnet-style-attacks/LICENSE | 201 + .../detecting-stuxnet-style-attacks/SKILL.md | 487 +++ .../references/api-reference.md | 97 + .../scripts/agent.py | 178 + .../LICENSE | 201 + .../SKILL.md | 83 + .../references/api-reference.md | 55 + .../scripts/agent.py | 201 + .../LICENSE | 201 + .../SKILL.md | 59 + .../references/api-reference.md | 63 + .../scripts/agent.py | 208 + .../LICENSE | 201 + .../SKILL.md | 97 + .../assets/template.md | 106 + .../references/api-reference.md | 99 + .../references/standards.md | 40 + .../references/workflows.md | 74 + .../scripts/agent.py | 162 + .../scripts/process.py | 88 + .../LICENSE | 201 + .../SKILL.md | 156 + .../assets/template.md | 37 + .../references/api-reference.md | 94 + .../references/standards.md | 45 + .../references/workflows.md | 70 + .../scripts/agent.py | 189 + .../scripts/process.py | 119 + .../LICENSE | 201 + .../SKILL.md | 171 + .../assets/template.md | 30 + .../references/api-reference.md | 86 + .../references/standards.md | 70 + .../references/workflows.md | 90 + .../scripts/agent.py | 157 + .../scripts/process.py | 163 + .../LICENSE | 201 + .../SKILL.md | 136 + .../assets/template.md | 30 + .../references/api-reference.md | 91 + .../references/standards.md | 43 + .../references/workflows.md | 58 + .../scripts/agent.py | 197 + .../scripts/process.py | 119 + .../LICENSE | 201 + .../SKILL.md | 168 + .../references/api-reference.md | 110 + .../scripts/agent.py | 555 +++ .../skills/detecting-wmi-persistence/LICENSE | 201 + .../skills/detecting-wmi-persistence/SKILL.md | 94 + .../references/api-reference.md | 89 + .../scripts/agent.py | 206 + .../LICENSE | 201 + .../SKILL.md | 231 ++ .../assets/template.md | 65 + .../references/api-reference.md | 108 + .../references/standards.md | 65 + .../references/workflows.md | 125 + .../scripts/agent.py | 194 + .../scripts/process.py | 442 ++ .../LICENSE | 201 + .../SKILL.md | 161 + .../references/api-reference.md | 99 + .../scripts/agent.py | 230 ++ .../LICENSE | 201 + .../SKILL.md | 189 + .../references/api-reference.md | 60 + .../scripts/agent.py | 160 + .../LICENSE | 201 + .../SKILL.md | 193 + .../references/api-reference.md | 55 + .../scripts/agent.py | 174 + .../LICENSE | 201 + .../SKILL.md | 151 + .../assets/template.md | 279 ++ .../references/api-reference.md | 90 + .../references/standards.md | 93 + .../references/workflows.md | 190 + .../scripts/agent.py | 153 + .../scripts/process.py | 908 +++++ .../executing-red-team-exercise/LICENSE | 201 + .../executing-red-team-exercise/SKILL.md | 194 + .../references/api-reference.md | 48 + .../scripts/agent.py | 171 + .../LICENSE | 201 + .../SKILL.md | 178 + .../assets/template.md | 42 + .../references/api-reference.md | 89 + .../references/standards.md | 30 + .../references/workflows.md | 55 + .../scripts/agent.py | 121 + .../scripts/process.py | 218 + .../LICENSE | 201 + .../SKILL.md | 126 + .../assets/template.md | 96 + .../references/api-reference.md | 113 + .../references/standards.md | 78 + .../references/workflows.md | 215 + .../scripts/agent.py | 138 + .../scripts/process.py | 567 +++ .../LICENSE | 201 + .../SKILL.md | 473 +++ .../references/api-reference.md | 103 + .../scripts/agent.py | 142 + .../LICENSE | 201 + .../SKILL.md | 351 ++ .../references/api-reference.md | 59 + .../scripts/agent.py | 149 + .../LICENSE | 201 + .../SKILL.md | 374 ++ .../references/api-reference.md | 89 + .../scripts/agent.py | 129 + .../exploiting-broken-link-hijacking/LICENSE | 201 + .../exploiting-broken-link-hijacking/SKILL.md | 228 ++ .../references/api-reference.md | 98 + .../scripts/agent.py | 148 + .../LICENSE | 201 + .../SKILL.md | 198 + .../references/api-reference.md | 89 + .../references/standards.md | 24 + .../references/workflows.md | 22 + .../scripts/agent.py | 114 + .../LICENSE | 201 + .../SKILL.md | 182 + .../assets/template.md | 26 + .../references/api-reference.md | 106 + .../references/standards.md | 21 + .../references/workflows.md | 24 + .../scripts/agent.py | 145 + .../scripts/process.py | 179 + .../LICENSE | 201 + .../SKILL.md | 384 ++ .../references/api-reference.md | 81 + .../scripts/agent.py | 162 + .../exploiting-http-request-smuggling/LICENSE | 201 + .../SKILL.md | 337 ++ .../references/api-reference.md | 53 + .../scripts/agent.py | 196 + .../exploiting-idor-vulnerabilities/LICENSE | 201 + .../exploiting-idor-vulnerabilities/SKILL.md | 303 ++ .../references/api-reference.md | 52 + .../scripts/agent.py | 177 + .../LICENSE | 201 + .../SKILL.md | 220 + .../assets/template.md | 45 + .../references/api-reference.md | 106 + .../references/standards.md | 54 + .../references/workflows.md | 56 + .../scripts/agent.py | 176 + .../scripts/process.py | 290 ++ .../LICENSE | 201 + .../SKILL.md | 326 ++ .../references/api-reference.md | 59 + .../scripts/agent.py | 178 + .../exploiting-ipv6-vulnerabilities/LICENSE | 201 + .../exploiting-ipv6-vulnerabilities/SKILL.md | 305 ++ .../references/api-reference.md | 59 + .../scripts/agent.py | 176 + .../LICENSE | 201 + .../SKILL.md | 464 +++ .../references/api-reference.md | 99 + .../scripts/agent.py | 132 + .../LICENSE | 201 + .../SKILL.md | 223 + .../assets/template.md | 89 + .../references/api-reference.md | 105 + .../references/standards.md | 54 + .../references/workflows.md | 115 + .../scripts/agent.py | 140 + .../scripts/process.py | 321 ++ .../LICENSE | 201 + .../SKILL.md | 231 ++ .../references/api-reference.md | 100 + .../scripts/agent.py | 125 + .../LICENSE | 201 + .../SKILL.md | 101 + .../assets/template.md | 27 + .../references/api-reference.md | 86 + .../references/standards.md | 32 + .../references/workflows.md | 93 + .../scripts/agent.py | 132 + .../scripts/process.py | 300 ++ .../LICENSE | 201 + .../SKILL.md | 193 + .../assets/template.md | 32 + .../references/api-reference.md | 83 + .../references/standards.md | 26 + .../references/workflows.md | 21 + .../scripts/agent.py | 130 + .../scripts/process.py | 143 + .../LICENSE | 201 + .../SKILL.md | 212 + .../assets/template.md | 30 + .../references/api-reference.md | 85 + .../references/standards.md | 19 + .../references/workflows.md | 23 + .../scripts/agent.py | 136 + .../scripts/process.py | 222 + .../exploiting-oauth-misconfiguration/LICENSE | 201 + .../SKILL.md | 310 ++ .../references/api-reference.md | 59 + .../scripts/agent.py | 232 ++ .../LICENSE | 201 + .../SKILL.md | 232 ++ .../references/api-reference.md | 102 + .../scripts/agent.py | 156 + .../LICENSE | 201 + .../SKILL.md | 239 ++ .../references/api-reference.md | 84 + .../scripts/agent.py | 119 + .../LICENSE | 201 + .../SKILL.md | 348 ++ .../references/api-reference.md | 57 + .../scripts/agent.py | 183 + .../LICENSE | 201 + .../SKILL.md | 251 ++ .../references/api-reference.md | 62 + .../scripts/agent.py | 177 + .../LICENSE | 201 + .../SKILL.md | 196 + .../references/api-reference.md | 60 + .../scripts/agent.py | 196 + .../LICENSE | 201 + .../SKILL.md | 251 ++ .../references/api-reference.md | 74 + .../scripts/agent.py | 217 + .../LICENSE | 201 + .../SKILL.md | 315 ++ .../references/api-reference.md | 55 + .../scripts/agent.py | 205 + .../LICENSE | 201 + .../SKILL.md | 265 ++ .../references/api-reference.md | 87 + .../scripts/agent.py | 141 + .../LICENSE | 201 + .../SKILL.md | 210 + .../assets/template.md | 33 + .../references/api-reference.md | 100 + .../references/standards.md | 34 + .../references/workflows.md | 58 + .../scripts/agent.py | 131 + .../scripts/process.py | 317 ++ .../LICENSE | 201 + .../SKILL.md | 417 ++ .../references/api-reference.md | 62 + .../scripts/agent.py | 211 + .../LICENSE | 201 + .../SKILL.md | 219 + .../assets/template.md | 48 + .../references/api-reference.md | 87 + .../references/standards.md | 31 + .../references/workflows.md | 61 + .../scripts/agent.py | 130 + .../scripts/process.py | 297 ++ .../LICENSE | 201 + .../SKILL.md | 346 ++ .../references/api-reference.md | 62 + .../scripts/agent.py | 201 + .../LICENSE | 201 + .../SKILL.md | 198 + .../assets/template.md | 25 + .../references/api-reference.md | 96 + .../references/standards.md | 9 + .../references/workflows.md | 11 + .../scripts/agent.py | 173 + .../LICENSE | 201 + .../SKILL.md | 336 ++ .../references/api-reference.md | 76 + .../scripts/agent.py | 240 ++ .../LICENSE | 201 + .../SKILL.md | 391 ++ .../references/api-reference.md | 77 + .../scripts/agent.py | 271 ++ .../LICENSE | 201 + .../SKILL.md | 84 + .../references/api-reference.md | 58 + .../scripts/agent.py | 169 + .../LICENSE | 201 + .../SKILL.md | 338 ++ .../references/api-reference.md | 86 + .../scripts/agent.py | 278 ++ .../LICENSE | 201 + .../SKILL.md | 148 + .../references/api-reference.md | 80 + .../scripts/agent.py | 289 ++ .../LICENSE | 201 + .../SKILL.md | 231 ++ .../assets/template.md | 105 + .../references/api-reference.md | 84 + .../references/standards.md | 80 + .../references/workflows.md | 130 + .../scripts/agent.py | 128 + .../scripts/process.py | 441 ++ .../LICENSE | 201 + .../SKILL.md | 325 ++ .../assets/template.md | 61 + .../references/api-reference.md | 83 + .../references/standards.md | 57 + .../references/workflows.md | 107 + .../scripts/agent.py | 136 + .../scripts/process.py | 273 ++ .../LICENSE | 201 + .../SKILL.md | 231 ++ .../assets/template.md | 29 + .../references/api-reference.md | 98 + .../references/standards.md | 21 + .../references/workflows.md | 20 + .../scripts/agent.py | 157 + .../scripts/process.py | 71 + .../LICENSE | 201 + .../SKILL.md | 203 + .../assets/template.md | 68 + .../references/api-reference.md | 105 + .../references/standards.md | 53 + .../references/workflows.md | 120 + .../scripts/agent.py | 156 + .../scripts/process.py | 242 ++ .../LICENSE | 201 + .../SKILL.md | 138 + .../references/api-reference.md | 51 + .../scripts/agent.py | 164 + .../LICENSE | 201 + .../SKILL.md | 73 + .../references/api-reference.md | 49 + .../scripts/agent.py | 195 + .../LICENSE | 201 + .../SKILL.md | 73 + .../references/api-reference.md | 106 + .../scripts/agent.py | 247 ++ .../LICENSE | 201 + .../SKILL.md | 170 + .../assets/template.md | 53 + .../references/api-reference.md | 108 + .../references/standards.md | 61 + .../references/workflows.md | 150 + .../scripts/agent.py | 140 + .../scripts/process.py | 248 ++ .../hunting-for-cobalt-strike-beacons/LICENSE | 201 + .../SKILL.md | 67 + .../references/api-reference.md | 139 + .../scripts/agent.py | 190 + .../LICENSE | 201 + .../SKILL.md | 113 + .../assets/template.md | 36 + .../references/api-reference.md | 84 + .../references/standards.md | 69 + .../references/workflows.md | 137 + .../scripts/agent.py | 171 + .../scripts/process.py | 399 ++ .../LICENSE | 201 + .../SKILL.md | 115 + .../assets/template.md | 26 + .../references/api-reference.md | 54 + .../references/standards.md | 44 + .../references/workflows.md | 78 + .../scripts/agent.py | 169 + .../scripts/process.py | 261 ++ .../LICENSE | 201 + .../SKILL.md | 67 + .../references/api-reference.md | 85 + .../scripts/agent.py | 217 + .../hunting-for-dcom-lateral-movement/LICENSE | 201 + .../SKILL.md | 678 ++++ .../references/api-reference.md | 126 + .../scripts/agent.py | 348 ++ .../scripts/detect_dcom_lateral_movement.py | 495 +++ .../skills/hunting-for-dcsync-attacks/LICENSE | 201 + .../hunting-for-dcsync-attacks/SKILL.md | 93 + .../references/api-reference.md | 100 + .../scripts/agent.py | 229 ++ .../LICENSE | 201 + .../SKILL.md | 353 ++ .../references/api-reference.md | 67 + .../scripts/agent.py | 206 + .../hunting-for-dns-based-persistence/LICENSE | 201 + .../SKILL.md | 67 + .../references/api-reference.md | 81 + .../scripts/agent.py | 162 + .../LICENSE | 201 + .../SKILL.md | 147 + .../assets/template.md | 30 + .../references/api-reference.md | 59 + .../references/standards.md | 46 + .../references/workflows.md | 86 + .../scripts/agent.py | 171 + .../scripts/process.py | 165 + .../LICENSE | 201 + .../SKILL.md | 64 + .../references/api-reference.md | 78 + .../scripts/agent.py | 173 + .../LICENSE | 201 + .../SKILL.md | 60 + .../references/api-reference.md | 78 + .../scripts/agent.py | 209 + .../LICENSE | 201 + .../SKILL.md | 97 + .../assets/template.md | 106 + .../references/api-reference.md | 57 + .../references/standards.md | 40 + .../references/workflows.md | 76 + .../scripts/agent.py | 151 + .../scripts/process.py | 82 + .../LICENSE | 201 + .../SKILL.md | 115 + .../assets/template.md | 154 + .../references/api-reference.md | 55 + .../references/standards.md | 75 + .../references/workflows.md | 117 + .../scripts/agent.py | 164 + .../scripts/process.py | 511 +++ .../LICENSE | 201 + .../SKILL.md | 175 + .../assets/template.md | 36 + .../references/api-reference.md | 57 + .../references/standards.md | 54 + .../references/workflows.md | 103 + .../scripts/agent.py | 126 + .../scripts/process.py | 188 + .../hunting-for-ntlm-relay-attacks/LICENSE | 201 + .../hunting-for-ntlm-relay-attacks/SKILL.md | 65 + .../references/api-reference.md | 113 + .../scripts/agent.py | 342 ++ .../LICENSE | 201 + .../SKILL.md | 110 + .../assets/template.md | 68 + .../references/api-reference.md | 50 + .../references/standards.md | 100 + .../references/workflows.md | 102 + .../scripts/agent.py | 171 + .../scripts/process.py | 428 ++ .../LICENSE | 201 + .../SKILL.md | 142 + .../assets/template.md | 30 + .../references/api-reference.md | 54 + .../references/standards.md | 57 + .../references/workflows.md | 81 + .../scripts/agent.py | 157 + .../scripts/process.py | 174 + .../LICENSE | 201 + .../SKILL.md | 67 + .../references/api-reference.md | 92 + .../scripts/agent.py | 214 + .../LICENSE | 201 + .../SKILL.md | 98 + .../assets/template.md | 107 + .../references/api-reference.md | 51 + .../references/standards.md | 41 + .../references/workflows.md | 74 + .../scripts/agent.py | 153 + .../scripts/process.py | 82 + .../LICENSE | 201 + .../SKILL.md | 67 + .../references/api-reference.md | 103 + .../scripts/agent.py | 228 ++ .../LICENSE | 201 + .../SKILL.md | 96 + .../assets/template.md | 106 + .../references/api-reference.md | 44 + .../references/standards.md | 40 + .../references/workflows.md | 74 + .../scripts/agent.py | 133 + .../scripts/process.py | 89 + .../hunting-for-shadow-copy-deletion/LICENSE | 201 + .../hunting-for-shadow-copy-deletion/SKILL.md | 97 + .../assets/template.md | 106 + .../references/api-reference.md | 65 + .../references/standards.md | 40 + .../references/workflows.md | 73 + .../scripts/agent.py | 185 + .../scripts/process.py | 81 + .../LICENSE | 201 + .../SKILL.md | 97 + .../assets/template.md | 106 + .../references/api-reference.md | 65 + .../references/standards.md | 40 + .../references/workflows.md | 75 + .../scripts/agent.py | 182 + .../scripts/process.py | 80 + .../LICENSE | 201 + .../SKILL.md | 64 + .../references/api-reference.md | 84 + .../scripts/agent.py | 290 ++ .../LICENSE | 201 + .../SKILL.md | 96 + .../assets/template.md | 106 + .../references/api-reference.md | 71 + .../references/standards.md | 40 + .../references/workflows.md | 76 + .../scripts/agent.py | 182 + .../scripts/process.py | 80 + .../LICENSE | 201 + .../SKILL.md | 102 + .../assets/template.md | 24 + .../references/api-reference.md | 63 + .../references/standards.md | 32 + .../references/workflows.md | 35 + .../scripts/agent.py | 184 + .../scripts/process.py | 118 + .../LICENSE | 201 + .../SKILL.md | 66 + .../references/api-reference.md | 68 + .../scripts/agent.py | 223 + .../LICENSE | 201 + .../SKILL.md | 96 + .../assets/template.md | 106 + .../references/api-reference.md | 68 + .../references/standards.md | 40 + .../references/workflows.md | 76 + .../scripts/agent.py | 194 + .../scripts/process.py | 77 + .../LICENSE | 201 + .../SKILL.md | 65 + .../references/api-reference.md | 73 + .../scripts/agent.py | 193 + .../hunting-for-webshell-activity/LICENSE | 201 + .../hunting-for-webshell-activity/SKILL.md | 97 + .../assets/template.md | 106 + .../references/api-reference.md | 65 + .../references/standards.md | 40 + .../references/workflows.md | 75 + .../scripts/agent.py | 175 + .../scripts/process.py | 91 + .../LICENSE | 201 + .../SKILL.md | 107 + .../assets/template.md | 92 + .../references/api-reference.md | 67 + .../references/standards.md | 76 + .../references/workflows.md | 122 + .../scripts/agent.py | 172 + .../scripts/process.py | 330 ++ .../LICENSE | 201 + .../SKILL.md | 329 ++ .../references/api-reference.md | 49 + .../scripts/agent.py | 172 + .../LICENSE | 201 + .../SKILL.md | 99 + .../assets/template.md | 65 + .../references/api-reference.md | 69 + .../references/standards.md | 49 + .../references/workflows.md | 125 + .../scripts/agent.py | 181 + .../scripts/process.py | 366 ++ .../LICENSE | 201 + .../SKILL.md | 215 + .../references/api-reference.md | 138 + .../scripts/agent.py | 324 ++ .../LICENSE | 201 + .../SKILL.md | 390 ++ .../references/api-reference.md | 73 + .../scripts/agent.py | 192 + .../LICENSE | 201 + .../SKILL.md | 463 +++ .../references/api-reference.md | 59 + .../scripts/agent.py | 161 + .../LICENSE | 201 + .../SKILL.md | 409 ++ .../references/api-reference.md | 48 + .../scripts/agent.py | 160 + .../LICENSE | 201 + .../SKILL.md | 476 +++ .../references/api-reference.md | 65 + .../scripts/agent.py | 165 + .../LICENSE | 201 + .../SKILL.md | 429 ++ .../references/api-reference.md | 61 + .../scripts/agent.py | 167 + .../LICENSE | 201 + .../SKILL.md | 410 ++ .../references/api-reference.md | 54 + .../scripts/agent.py | 153 + .../LICENSE | 201 + .../SKILL.md | 357 ++ .../references/api-reference.md | 52 + .../scripts/agent.py | 145 + .../LICENSE | 201 + .../SKILL.md | 328 ++ .../references/api-reference.md | 59 + .../scripts/agent.py | 173 + .../LICENSE | 201 + .../SKILL.md | 252 ++ .../assets/template.md | 80 + .../references/api-reference.md | 58 + .../references/standards.md | 43 + .../references/workflows.md | 137 + .../scripts/agent.py | 181 + .../scripts/process.py | 242 ++ .../LICENSE | 201 + .../SKILL.md | 259 ++ .../assets/template.md | 27 + .../references/api-reference.md | 62 + .../references/standards.md | 34 + .../references/workflows.md | 58 + .../scripts/agent.py | 155 + .../scripts/process.py | 159 + .../LICENSE | 201 + .../SKILL.md | 257 ++ .../assets/template.md | 36 + .../references/api-reference.md | 59 + .../references/standards.md | 31 + .../references/workflows.md | 56 + .../scripts/agent.py | 146 + .../scripts/process.py | 229 ++ .../LICENSE | 19 + .../SKILL.md | 221 + .../references/asm-reference.md | 171 + .../scripts/agent.py | 921 +++++ .../LICENSE | 201 + .../SKILL.md | 345 ++ .../references/api-reference.md | 54 + .../scripts/agent.py | 178 + .../LICENSE | 201 + .../SKILL.md | 261 ++ .../assets/template.md | 50 + .../references/api-reference.md | 57 + .../references/standards.md | 41 + .../references/workflows.md | 86 + .../scripts/agent.py | 140 + .../scripts/process.py | 250 ++ .../LICENSE | 201 + .../SKILL.md | 326 ++ .../assets/template.md | 32 + .../references/api-reference.md | 61 + .../references/standards.md | 18 + .../references/workflows.md | 22 + .../scripts/agent.py | 165 + .../scripts/process.py | 162 + .../LICENSE | 201 + .../SKILL.md | 456 +++ .../references/api-reference.md | 88 + .../scripts/agent.py | 510 +++ .../LICENSE | 201 + .../SKILL.md | 310 ++ .../references/api-reference.md | 58 + .../scripts/agent.py | 179 + .../implementing-aws-security-hub/LICENSE | 201 + .../implementing-aws-security-hub/SKILL.md | 228 ++ .../references/api-reference.md | 51 + .../scripts/agent.py | 181 + .../LICENSE | 201 + .../SKILL.md | 242 ++ .../assets/template.md | 51 + .../references/api-reference.md | 60 + .../references/standards.md | 46 + .../references/workflows.md | 107 + .../scripts/agent.py | 152 + .../scripts/process.py | 219 + .../LICENSE | 201 + .../SKILL.md | 320 ++ .../references/api-reference.md | 62 + .../scripts/agent.py | 194 + .../LICENSE | 201 + .../SKILL.md | 331 ++ .../assets/template.md | 106 + .../references/api-reference.md | 57 + .../references/standards.md | 47 + .../references/workflows.md | 116 + .../scripts/agent.py | 130 + .../scripts/process.py | 391 ++ .../LICENSE | 201 + .../SKILL.md | 330 ++ .../references/api-reference.md | 56 + .../scripts/agent.py | 121 + .../LICENSE | 201 + .../SKILL.md | 383 ++ .../references/api-reference.md | 272 ++ .../scripts/agent.py | 1070 +++++ .../LICENSE | 201 + .../SKILL.md | 327 ++ .../references/api-reference.md | 190 + .../scripts/agent.py | 868 ++++ .../LICENSE | 201 + .../SKILL.md | 344 ++ .../assets/template.md | 116 + .../references/api-reference.md | 64 + .../references/standards.md | 51 + .../references/workflows.md | 102 + .../scripts/agent.py | 175 + .../scripts/process.py | 415 ++ .../LICENSE | 201 + .../SKILL.md | 457 +++ .../references/api-reference.md | 47 + .../scripts/agent.py | 196 + .../LICENSE | 201 + .../SKILL.es.md | 285 ++ .../SKILL.md | 296 ++ .../references/api-reference.md | 50 + .../scripts/agent.py | 174 + .../LICENSE | 201 + .../SKILL.md | 345 ++ .../references/api-reference.md | 57 + .../scripts/agent.py | 176 + .../LICENSE | 201 + .../SKILL.md | 226 ++ .../assets/template.md | 43 + .../references/api-reference.md | 53 + .../references/standards.md | 34 + .../references/workflows.md | 29 + .../scripts/agent.py | 133 + .../scripts/process.py | 175 + .../implementing-cloud-waf-rules/LICENSE | 201 + .../implementing-cloud-waf-rules/SKILL.md | 312 ++ .../references/api-reference.md | 56 + .../scripts/agent.py | 210 + .../LICENSE | 201 + .../SKILL.md | 75 + .../references/api-reference.md | 61 + .../scripts/agent.py | 186 + .../LICENSE | 201 + .../SKILL.md | 226 ++ .../assets/template.md | 53 + .../references/api-reference.md | 58 + .../references/standards.md | 40 + .../references/workflows.md | 42 + .../scripts/agent.py | 157 + .../scripts/process.py | 209 + .../LICENSE | 201 + .../SKILL.md | 65 + .../references/api-reference.md | 52 + .../scripts/agent.py | 137 + .../LICENSE | 201 + .../SKILL.md | 370 ++ .../references/api-reference.md | 59 + .../scripts/agent.py | 136 + .../LICENSE | 201 + .../SKILL.md | 209 + .../assets/template.md | 31 + .../references/api-reference.md | 54 + .../references/standards.md | 16 + .../references/workflows.md | 19 + .../scripts/agent.py | 141 + .../scripts/process.py | 195 + .../LICENSE | 201 + .../SKILL.md | 60 + .../references/api-reference.md | 68 + .../scripts/agent.py | 192 + .../LICENSE | 201 + .../SKILL.md | 261 ++ .../assets/template.md | 24 + .../references/api-reference.md | 53 + .../references/standards.md | 27 + .../references/workflows.md | 30 + .../scripts/agent.py | 146 + .../scripts/process.py | 140 + .../LICENSE | 201 + .../SKILL.md | 596 +++ .../references/api-reference.md | 112 + .../scripts/agent.py | 347 ++ .../LICENSE | 201 + .../SKILL.md | 445 ++ .../references/api-reference.md | 56 + .../scripts/agent.py | 128 + .../LICENSE | 201 + .../SKILL.md | 58 + .../references/api-reference.md | 73 + .../scripts/agent.py | 232 ++ .../LICENSE | 201 + .../SKILL.md | 416 ++ .../references/api-reference.md | 63 + .../scripts/agent.py | 186 + .../LICENSE | 201 + .../SKILL.md | 452 +++ .../assets/template.md | 104 + .../references/api-reference.md | 53 + .../references/standards.md | 36 + .../references/workflows.md | 71 + .../scripts/agent.py | 175 + .../scripts/process.py | 302 ++ .../LICENSE | 201 + .../SKILL.md | 385 ++ .../references/api-reference.md | 68 + .../scripts/agent.py | 171 + .../LICENSE | 201 + .../SKILL.md | 177 + .../assets/template.md | 39 + .../references/api-reference.md | 61 + .../references/standards.md | 24 + .../references/workflows.md | 31 + .../scripts/agent.py | 133 + .../scripts/process.py | 125 + .../LICENSE | 201 + .../SKILL.md | 88 + .../assets/template.md | 34 + .../references/api-reference.md | 55 + .../references/standards.md | 36 + .../references/workflows.md | 66 + .../scripts/agent.py | 152 + .../scripts/process.py | 319 ++ .../LICENSE | 201 + .../SKILL.md | 220 + .../assets/template.md | 45 + .../references/api-reference.md | 61 + .../references/standards.md | 30 + .../references/workflows.md | 79 + .../scripts/agent.py | 158 + .../scripts/process.py | 176 + .../LICENSE | 201 + .../SKILL.md | 125 + .../assets/template.md | 76 + .../references/api-reference.md | 51 + .../references/standards.md | 62 + .../references/workflows.md | 124 + .../scripts/agent.py | 134 + .../scripts/process.py | 597 +++ .../LICENSE | 201 + .../SKILL.md | 368 ++ .../references/api-reference.md | 49 + .../scripts/agent.py | 141 + .../LICENSE | 201 + .../SKILL.md | 383 ++ .../references/api-reference.md | 192 + .../scripts/agent.py | 813 ++++ .../LICENSE | 201 + .../SKILL.md | 106 + .../assets/template.md | 34 + .../references/api-reference.md | 58 + .../references/standards.md | 34 + .../references/workflows.md | 69 + .../scripts/agent.py | 140 + .../scripts/process.py | 241 ++ .../LICENSE | 201 + .../SKILL.md | 82 + .../assets/template.md | 41 + .../references/api-reference.md | 48 + .../references/standards.md | 40 + .../references/workflows.md | 87 + .../scripts/agent.py | 143 + .../scripts/process.py | 358 ++ .../LICENSE | 201 + .../SKILL.md | 66 + .../references/api-reference.md | 72 + .../scripts/agent.py | 192 + .../LICENSE | 201 + .../SKILL.md | 168 + .../assets/template.md | 20 + .../references/api-reference.md | 55 + .../references/standards.md | 6 + .../references/workflows.md | 8 + .../scripts/agent.py | 165 + .../scripts/process.py | 47 + .../LICENSE | 201 + .../SKILL.md | 95 + .../assets/template.md | 90 + .../references/api-reference.md | 49 + .../references/standards.md | 53 + .../references/workflows.md | 76 + .../scripts/agent.py | 169 + .../scripts/process.py | 345 ++ .../LICENSE | 201 + .../SKILL.md | 218 + .../assets/template.md | 41 + .../references/api-reference.md | 54 + .../references/standards.md | 59 + .../references/workflows.md | 32 + .../scripts/agent.py | 147 + .../scripts/process.py | 215 + .../LICENSE | 201 + .../SKILL.md | 59 + .../references/api-reference.md | 103 + .../scripts/agent.py | 238 ++ .../LICENSE | 201 + .../SKILL.md | 293 ++ .../assets/template.md | 36 + .../references/api-reference.md | 53 + .../references/standards.md | 36 + .../references/workflows.md | 64 + .../scripts/agent.py | 185 + .../scripts/process.py | 175 + .../LICENSE | 201 + .../SKILL.md | 283 ++ .../assets/template.md | 25 + .../references/api-reference.md | 58 + .../references/standards.md | 15 + .../references/workflows.md | 22 + .../scripts/agent.py | 189 + .../scripts/process.py | 186 + .../LICENSE | 201 + .../SKILL.md | 272 ++ .../assets/template.md | 35 + .../references/api-reference.md | 73 + .../references/standards.md | 27 + .../references/workflows.md | 46 + .../scripts/agent.py | 191 + .../scripts/process.py | 239 ++ .../LICENSE | 201 + .../SKILL.md | 313 ++ .../references/api-reference.md | 57 + .../scripts/agent.py | 190 + .../LICENSE | 201 + .../SKILL.md | 191 + .../assets/template.md | 140 + .../references/api-reference.md | 61 + .../references/standards.md | 108 + .../references/workflows.md | 288 ++ .../scripts/agent.py | 234 ++ .../scripts/process.py | 535 +++ .../LICENSE | 201 + .../SKILL.md | 297 ++ .../references/api-reference.md | 314 ++ .../scripts/agent.py | 1503 +++++++ .../LICENSE | 201 + .../SKILL.md | 209 + .../assets/template.md | 60 + .../references/api-reference.md | 68 + .../references/standards.md | 40 + .../references/workflows.md | 104 + .../scripts/agent.py | 211 + .../scripts/process.py | 192 + .../LICENSE | 201 + .../SKILL.md | 481 +++ .../references/api-reference.md | 58 + .../scripts/agent.py | 206 + .../LICENSE | 201 + .../SKILL.md | 101 + .../assets/template.md | 26 + .../references/api-reference.md | 55 + .../references/standards.md | 31 + .../references/workflows.md | 60 + .../scripts/agent.py | 194 + .../scripts/process.py | 224 + .../LICENSE | 201 + .../SKILL.md | 180 + .../assets/template.md | 43 + .../references/api-reference.md | 60 + .../references/standards.md | 44 + .../references/workflows.md | 113 + .../scripts/agent.py | 162 + .../scripts/process.py | 234 ++ .../LICENSE | 201 + .../SKILL.md | 223 + .../references/api-reference.md | 195 + .../scripts/agent.py | 1009 +++++ .../LICENSE | 201 + .../SKILL.md | 590 +++ .../references/api-reference.md | 69 + .../scripts/agent.py | 231 ++ .../LICENSE | 201 + .../SKILL.md | 368 ++ .../assets/template.md | 33 + .../references/api-reference.md | 67 + .../references/standards.md | 21 + .../references/workflows.md | 70 + .../scripts/agent.py | 232 ++ .../scripts/process.py | 293 ++ .../LICENSE | 201 + .../SKILL.md | 74 + .../references/api-reference.md | 59 + .../scripts/agent.py | 205 + .../LICENSE | 201 + .../SKILL.md | 351 ++ .../references/api-reference.md | 55 + .../scripts/agent.py | 213 + .../LICENSE | 201 + .../SKILL.md | 65 + .../references/api-reference.md | 55 + .../scripts/agent.py | 201 + .../LICENSE | 201 + .../SKILL.md | 208 + .../assets/template.md | 104 + .../references/api-reference.md | 57 + .../references/standards.md | 83 + .../references/workflows.md | 173 + .../scripts/agent.py | 222 + .../scripts/process.py | 392 ++ .../LICENSE | 201 + .../SKILL.md | 527 +++ .../references/api-reference.md | 47 + .../scripts/agent.py | 212 + .../LICENSE | 201 + .../SKILL.md | 326 ++ .../assets/template.md | 45 + .../references/api-reference.md | 75 + .../references/standards.md | 36 + .../references/workflows.md | 41 + .../scripts/agent.py | 211 + .../scripts/process.py | 189 + .../LICENSE | 201 + .../SKILL.md | 103 + .../references/api-reference.md | 71 + .../scripts/agent.py | 455 +++ .../LICENSE | 201 + .../SKILL.md | 260 ++ .../assets/template.md | 94 + .../references/api-reference.md | 56 + .../references/standards.md | 46 + .../references/workflows.md | 76 + .../scripts/agent.py | 183 + .../scripts/process.py | 245 ++ .../LICENSE | 201 + .../SKILL.md | 162 + .../assets/template.md | 301 ++ .../references/api-reference.md | 50 + .../references/standards.md | 181 + .../references/workflows.md | 313 ++ .../scripts/agent.py | 201 + .../scripts/process.py | 711 ++++ .../LICENSE | 201 + .../SKILL.md | 126 + .../assets/template.md | 46 + .../references/api-reference.md | 55 + .../references/standards.md | 27 + .../references/workflows.md | 55 + .../scripts/agent.py | 221 + .../scripts/process.py | 373 ++ .../LICENSE | 201 + .../SKILL.md | 87 + .../assets/template.md | 32 + .../references/api-reference.md | 52 + .../references/standards.md | 38 + .../references/workflows.md | 60 + .../scripts/agent.py | 186 + .../scripts/process.py | 283 ++ .../LICENSE | 201 + .../SKILL.md | 327 ++ .../assets/template.md | 69 + .../references/api-reference.md | 64 + .../references/standards.md | 57 + .../references/workflows.md | 195 + .../scripts/agent.py | 210 + .../scripts/process.py | 243 ++ .../LICENSE | 201 + .../SKILL.md | 262 ++ .../assets/template.md | 55 + .../references/api-reference.md | 52 + .../references/standards.md | 71 + .../references/workflows.md | 110 + .../scripts/agent.py | 185 + .../scripts/process.py | 308 ++ .../LICENSE | 201 + .../SKILL.md | 227 ++ .../references/api-reference.md | 201 + .../scripts/agent.py | 527 +++ .../LICENSE | 201 + .../SKILL.md | 61 + .../references/api-reference.md | 111 + .../scripts/agent.py | 248 ++ .../LICENSE | 201 + .../SKILL.md | 64 + .../references/api-reference.md | 56 + .../scripts/agent.py | 221 + .../LICENSE | 201 + .../SKILL.md | 101 + .../assets/template.md | 21 + .../references/api-reference.md | 63 + .../references/standards.md | 5 + .../references/workflows.md | 10 + .../scripts/agent.py | 158 + .../scripts/process.py | 31 + .../LICENSE | 201 + .../SKILL.md | 344 ++ .../references/api-reference.md | 59 + .../scripts/agent.py | 152 + .../LICENSE | 201 + .../SKILL.md | 124 + .../assets/template.md | 35 + .../references/api-reference.md | 50 + .../references/standards.md | 41 + .../references/workflows.md | 95 + .../scripts/agent.py | 151 + .../scripts/process.py | 348 ++ .../LICENSE | 201 + .../SKILL.md | 290 ++ .../assets/template.md | 44 + .../references/api-reference.md | 50 + .../references/standards.md | 40 + .../references/workflows.md | 51 + .../scripts/agent.py | 176 + .../scripts/process.py | 198 + .../LICENSE | 201 + .../SKILL.md | 175 + .../assets/template.md | 18 + .../references/api-reference.md | 41 + .../references/standards.md | 19 + .../references/workflows.md | 14 + .../scripts/agent.py | 146 + .../scripts/process.py | 119 + .../LICENSE | 201 + .../SKILL.md | 75 + .../references/api-reference.md | 62 + .../scripts/agent.py | 184 + .../LICENSE | 201 + .../SKILL.md | 427 ++ .../references/api-reference.md | 39 + .../scripts/agent.py | 146 + .../LICENSE | 201 + .../SKILL.md | 341 ++ .../references/api-reference.md | 51 + .../scripts/agent.py | 122 + .../LICENSE | 201 + .../SKILL.md | 436 ++ .../references/api-reference.md | 58 + .../scripts/agent.py | 214 + .../LICENSE | 201 + .../SKILL.md | 86 + .../references/api-reference.md | 128 + .../scripts/agent.py | 211 + .../LICENSE | 201 + .../SKILL.md | 370 ++ .../references/api-reference.md | 107 + .../scripts/agent.py | 154 + .../LICENSE | 201 + .../SKILL.md | 233 ++ .../assets/template.md | 16 + .../references/api-reference.md | 61 + .../references/standards.md | 18 + .../references/workflows.md | 20 + .../scripts/agent.py | 156 + .../scripts/process.py | 119 + .../LICENSE | 201 + .../SKILL.md | 489 +++ .../references/api-reference.md | 39 + .../scripts/agent.py | 146 + .../LICENSE | 201 + .../SKILL.md | 340 ++ .../references/api-reference.md | 80 + .../scripts/agent.py | 106 + .../LICENSE | 201 + .../SKILL.md | 64 + .../references/api-reference.md | 75 + .../scripts/agent.py | 214 + .../LICENSE | 201 + .../SKILL.md | 57 + .../references/api-reference.md | 75 + .../scripts/agent.py | 166 + .../LICENSE | 201 + .../SKILL.md | 274 ++ .../assets/template.md | 60 + .../references/api-reference.md | 42 + .../references/standards.md | 28 + .../references/workflows.md | 64 + .../scripts/agent.py | 113 + .../scripts/process.py | 249 ++ .../LICENSE | 201 + .../SKILL.md | 387 ++ .../assets/template.md | 48 + .../references/api-reference.md | 46 + .../references/standards.md | 51 + .../references/workflows.md | 92 + .../scripts/agent.py | 108 + .../scripts/process.py | 228 ++ .../LICENSE | 201 + .../SKILL.md | 385 ++ .../references/api-reference.md | 59 + .../scripts/agent.py | 127 + .../LICENSE | 201 + .../SKILL.md | 272 ++ .../references/api-reference.md | 72 + .../scripts/agent.py | 92 + .../LICENSE | 201 + .../SKILL.md | 65 + .../references/api-reference.md | 76 + .../scripts/agent.py | 91 + .../LICENSE | 201 + .../SKILL.md | 544 +++ .../references/api-reference.md | 57 + .../scripts/agent.py | 206 + .../LICENSE | 201 + .../SKILL.md | 74 + .../references/api-reference.md | 100 + .../references/standards.md | 20 + .../references/workflows.md | 36 + .../scripts/agent.py | 91 + .../LICENSE | 201 + .../SKILL.md | 344 ++ .../references/api-reference.md | 75 + .../scripts/agent.py | 82 + .../LICENSE | 201 + .../SKILL.md | 316 ++ .../assets/template.md | 30 + .../references/api-reference.md | 161 + .../references/standards.md | 28 + .../references/workflows.md | 49 + .../scripts/agent.py | 284 ++ .../scripts/process.py | 341 ++ .../LICENSE | 201 + .../SKILL.md | 131 + .../assets/template.md | 140 + .../references/api-reference.md | 208 + .../references/standards.md | 25 + .../references/workflows.md | 94 + .../scripts/agent.py | 324 ++ .../LICENSE | 201 + .../SKILL.md | 320 ++ .../assets/template.md | 42 + .../references/api-reference.md | 169 + .../references/standards.md | 28 + .../references/workflows.md | 38 + .../scripts/agent.py | 305 ++ .../scripts/process.py | 153 + .../LICENSE | 201 + .../SKILL.md | 323 ++ .../assets/template.md | 75 + .../references/api-reference.md | 183 + .../references/standards.md | 26 + .../references/workflows.md | 55 + .../scripts/agent.py | 273 ++ .../scripts/process.py | 167 + .../LICENSE | 201 + .../SKILL.md | 144 + .../assets/template.md | 58 + .../references/api-reference.md | 179 + .../references/standards.md | 31 + .../references/workflows.md | 74 + .../scripts/agent.py | 252 ++ .../scripts/process.py | 391 ++ .../LICENSE | 201 + .../SKILL.md | 59 + .../references/api-reference.md | 52 + .../scripts/agent.py | 169 + .../LICENSE | 201 + .../SKILL.md | 334 ++ .../references/api-reference.md | 63 + .../scripts/agent.py | 233 ++ .../LICENSE | 201 + .../SKILL.md | 126 + .../assets/template.md | 45 + .../references/api-reference.md | 175 + .../references/standards.md | 37 + .../references/workflows.md | 107 + .../scripts/agent.py | 210 + .../scripts/process.py | 335 ++ .../LICENSE | 201 + .../SKILL.md | 443 ++ .../references/api-reference.md | 183 + .../scripts/agent.py | 213 + .../LICENSE | 201 + .../SKILL.md | 291 ++ .../assets/template.md | 122 + .../references/api-reference.md | 210 + .../references/standards.md | 58 + .../references/workflows.md | 175 + .../scripts/agent.py | 295 ++ .../scripts/process.py | 582 +++ .../LICENSE | 201 + .../SKILL.md | 219 + .../references/api-reference.md | 132 + .../scripts/agent.py | 322 ++ .../LICENSE | 201 + .../SKILL.md | 360 ++ .../assets/template.md | 45 + .../references/api-reference.md | 190 + .../references/standards.md | 29 + .../references/workflows.md | 80 + .../scripts/agent.py | 245 ++ .../scripts/process.py | 327 ++ .../LICENSE | 201 + .../SKILL.md | 261 ++ .../assets/template.md | 30 + .../references/api-reference.md | 194 + .../references/standards.md | 18 + .../references/workflows.md | 17 + .../scripts/agent.py | 253 ++ .../scripts/process.py | 193 + .../LICENSE | 201 + .../SKILL.md | 94 + .../assets/template.md | 56 + .../references/api-reference.md | 222 + .../references/standards.md | 41 + .../references/workflows.md | 81 + .../scripts/agent.py | 226 ++ .../scripts/process.py | 348 ++ .../LICENSE | 201 + .../SKILL.md | 73 + .../references/api-reference.md | 99 + .../scripts/agent.py | 161 + .../LICENSE | 201 + .../SKILL.md | 295 ++ .../assets/template.md | 110 + .../references/api-reference.md | 45 + .../references/standards.md | 42 + .../references/workflows.md | 70 + .../scripts/agent.py | 105 + .../scripts/process.py | 372 ++ .../implementing-saml-sso-with-okta/LICENSE | 201 + .../implementing-saml-sso-with-okta/SKILL.md | 138 + .../assets/template.md | 128 + .../references/api-reference.md | 44 + .../references/standards.md | 41 + .../references/workflows.md | 92 + .../scripts/agent.py | 197 + .../scripts/process.py | 499 +++ .../LICENSE | 201 + .../SKILL.md | 221 + .../assets/template.md | 75 + .../references/api-reference.md | 178 + .../references/standards.md | 60 + .../references/workflows.md | 96 + .../scripts/agent.py | 254 ++ .../scripts/process.py | 470 +++ .../LICENSE | 201 + .../SKILL.md | 337 ++ .../assets/template.md | 175 + .../references/api-reference.md | 180 + .../references/standards.md | 54 + .../references/workflows.md | 92 + .../scripts/agent.py | 215 + .../scripts/process.py | 299 ++ .../LICENSE | 201 + .../SKILL.md | 340 ++ .../references/api-reference.md | 176 + .../scripts/agent.py | 272 ++ .../LICENSE | 201 + .../SKILL.md | 87 + .../references/api-reference.md | 97 + .../scripts/agent.py | 173 + .../LICENSE | 201 + .../SKILL.md | 88 + .../references/api-reference.md | 52 + .../scripts/agent.py | 222 + .../LICENSE | 201 + .../SKILL.md | 408 ++ .../references/api-reference.md | 77 + .../scripts/agent.py | 201 + .../LICENSE | 201 + .../SKILL.md | 416 ++ .../references/api-reference.md | 100 + .../scripts/agent.py | 160 + .../LICENSE | 201 + .../SKILL.md | 327 ++ .../references/api-reference.md | 196 + .../references/standards.md | 27 + .../scripts/agent.py | 218 + .../LICENSE | 201 + .../SKILL.md | 84 + .../references/api-reference.md | 61 + .../scripts/agent.py | 258 ++ .../implementing-siem-use-case-tuning/LICENSE | 201 + .../SKILL.md | 58 + .../references/api-reference.md | 82 + .../scripts/agent.py | 187 + .../LICENSE | 201 + .../SKILL.md | 319 ++ .../references/api-reference.md | 55 + .../scripts/agent.py | 227 ++ .../LICENSE | 201 + .../SKILL.md | 167 + .../references/api-reference.md | 116 + .../scripts/agent.py | 473 +++ .../LICENSE | 201 + .../SKILL.md | 417 ++ .../references/api-reference.md | 74 + .../scripts/agent.py | 245 ++ .../LICENSE | 201 + .../SKILL.md | 89 + .../references/api-reference.md | 122 + .../scripts/agent.py | 245 ++ .../LICENSE | 201 + .../SKILL.md | 341 ++ .../assets/template.md | 62 + .../references/api-reference.md | 178 + .../references/standards.md | 58 + .../references/workflows.md | 95 + .../scripts/agent.py | 197 + .../scripts/process.py | 256 ++ .../LICENSE | 201 + .../SKILL.md | 310 ++ .../assets/template.md | 53 + .../references/api-reference.md | 169 + .../references/standards.md | 70 + .../references/workflows.md | 57 + .../scripts/agent.py | 322 ++ .../scripts/process.py | 306 ++ .../LICENSE | 201 + .../SKILL.md | 298 ++ .../assets/template.md | 56 + .../references/api-reference.md | 180 + .../references/standards.md | 51 + .../references/workflows.md | 77 + .../scripts/agent.py | 253 ++ .../scripts/process.py | 254 ++ .../LICENSE | 201 + .../SKILL.md | 74 + .../references/api-reference.md | 65 + .../scripts/agent.py | 246 ++ .../LICENSE | 201 + .../SKILL.md | 370 ++ .../references/api-reference.md | 184 + .../scripts/agent.py | 255 ++ .../LICENSE | 201 + .../SKILL.md | 385 ++ .../references/api-reference.md | 197 + .../scripts/agent.py | 265 ++ .../LICENSE | 201 + .../SKILL.md | 390 ++ .../references/api-reference.md | 59 + .../scripts/agent.py | 209 + .../LICENSE | 201 + .../SKILL.md | 423 ++ .../references/api-reference.md | 75 + .../scripts/agent.py | 236 ++ .../LICENSE | 201 + .../SKILL.md | 149 + .../assets/template.md | 24 + .../references/api-reference.md | 188 + .../references/standards.md | 12 + .../references/workflows.md | 17 + .../scripts/agent.py | 233 ++ .../scripts/process.py | 88 + .../LICENSE | 201 + .../SKILL.md | 301 ++ .../assets/template.md | 54 + .../references/api-reference.md | 167 + .../references/standards.md | 47 + .../references/workflows.md | 150 + .../scripts/agent.py | 277 ++ .../scripts/process.py | 327 ++ .../LICENSE | 201 + .../SKILL.md | 61 + .../references/api-reference.md | 81 + .../scripts/agent.py | 152 + .../LICENSE | 201 + .../SKILL.md | 186 + .../assets/template.md | 34 + .../references/api-reference.md | 159 + .../references/standards.md | 24 + .../references/workflows.md | 51 + .../scripts/agent.py | 232 ++ .../scripts/process.py | 273 ++ .../LICENSE | 201 + .../SKILL.md | 300 ++ .../assets/template.md | 87 + .../references/api-reference.md | 201 + .../references/standards.md | 58 + .../references/workflows.md | 72 + .../scripts/agent.py | 264 ++ .../scripts/process.py | 302 ++ .../LICENSE | 201 + .../SKILL.md | 73 + .../references/api-reference.md | 65 + .../scripts/agent.py | 241 ++ .../LICENSE | 201 + .../SKILL.md | 83 + .../references/api-reference.md | 50 + .../references/standards.md | 29 + .../references/workflows.md | 46 + .../scripts/agent.py | 124 + .../LICENSE | 201 + .../SKILL.md | 243 ++ .../assets/template.md | 40 + .../references/api-reference.md | 47 + .../references/standards.md | 51 + .../references/workflows.md | 99 + .../scripts/agent.py | 161 + .../scripts/process.py | 191 + .../LICENSE | 201 + .../SKILL.md | 360 ++ .../assets/template.md | 42 + .../references/api-reference.md | 47 + .../references/standards.md | 48 + .../references/workflows.md | 74 + .../scripts/agent.py | 145 + .../scripts/process.py | 281 ++ .../LICENSE | 201 + .../SKILL.md | 320 ++ .../references/api-reference.md | 47 + .../scripts/agent.py | 141 + .../implementing-zero-trust-in-cloud/LICENSE | 201 + .../implementing-zero-trust-in-cloud/SKILL.md | 319 ++ .../references/api-reference.md | 72 + .../scripts/agent.py | 227 ++ .../LICENSE | 201 + .../SKILL.md | 174 + .../assets/template.md | 135 + .../references/api-reference.md | 51 + .../references/standards.md | 83 + .../references/workflows.md | 207 + .../scripts/agent.py | 133 + .../scripts/process.py | 459 +++ .../LICENSE | 201 + .../SKILL.md | 359 ++ .../references/api-reference.md | 59 + .../scripts/agent.py | 168 + .../LICENSE | 201 + .../SKILL.md | 64 + .../references/api-reference.md | 92 + .../scripts/agent.py | 163 + .../LICENSE | 201 + .../SKILL.md | 497 +++ .../assets/template.md | 42 + .../references/api-reference.md | 51 + .../references/standards.md | 59 + .../references/workflows.md | 103 + .../scripts/agent.py | 157 + .../scripts/process.py | 358 ++ .../LICENSE | 201 + .../SKILL.md | 256 ++ .../assets/template.md | 98 + .../references/api-reference.md | 57 + .../references/standards.md | 38 + .../references/workflows.md | 83 + .../scripts/agent.py | 136 + .../scripts/process.py | 245 ++ .../LICENSE | 201 + .../SKILL.md | 329 ++ .../assets/template.md | 216 + .../references/api-reference.md | 60 + .../references/standards.md | 65 + .../references/workflows.md | 137 + .../scripts/agent.py | 152 + .../scripts/process.py | 380 ++ .../LICENSE | 201 + .../SKILL.md | 195 + .../assets/template.md | 53 + .../references/api-reference.md | 54 + .../references/standards.md | 40 + .../references/workflows.md | 70 + .../scripts/agent.py | 158 + .../scripts/process.py | 279 ++ .../LICENSE | 201 + .../SKILL.md | 305 ++ .../references/api-reference.md | 56 + .../scripts/agent.py | 234 ++ .../LICENSE | 201 + .../SKILL.md | 300 ++ .../references/api-reference.md | 62 + .../scripts/agent.py | 217 + .../LICENSE | 201 + .../SKILL.md | 384 ++ .../references/api-reference.md | 57 + .../scripts/agent.py | 206 + .../managing-cloud-identity-with-okta/LICENSE | 201 + .../SKILL.md | 272 ++ .../references/api-reference.md | 63 + .../scripts/agent.py | 227 ++ .../managing-intelligence-lifecycle/LICENSE | 201 + .../managing-intelligence-lifecycle/SKILL.md | 125 + .../references/api-reference.md | 56 + .../scripts/agent.py | 175 + .../mapping-mitre-attack-techniques/LICENSE | 201 + .../mapping-mitre-attack-techniques/SKILL.md | 157 + .../references/api-reference.md | 66 + .../scripts/agent.py | 205 + .../skills/monitoring-darkweb-sources/LICENSE | 201 + .../monitoring-darkweb-sources/SKILL.md | 144 + .../references/api-reference.md | 62 + .../scripts/agent.py | 195 + .../LICENSE | 201 + .../SKILL.md | 341 ++ .../references/api-reference.md | 119 + .../scripts/agent.py | 628 +++ .../LICENSE | 201 + .../SKILL.md | 254 ++ .../assets/template.md | 49 + .../references/api-reference.md | 53 + .../references/standards.md | 53 + .../references/workflows.md | 110 + .../scripts/agent.py | 143 + .../scripts/process.py | 230 ++ .../LICENSE | 201 + .../SKILL.md | 140 + .../assets/template.md | 46 + .../references/api-reference.md | 40 + .../references/standards.md | 23 + .../references/workflows.md | 56 + .../scripts/agent.py | 131 + .../scripts/process.py | 312 ++ .../LICENSE | 201 + .../SKILL.md | 241 ++ .../assets/template.md | 125 + .../references/api-reference.md | 59 + .../references/standards.md | 56 + .../references/workflows.md | 141 + .../scripts/agent.py | 186 + .../scripts/process.py | 402 ++ .../LICENSE | 201 + .../SKILL.md | 209 + .../assets/template.md | 147 + .../references/api-reference.md | 81 + .../references/standards.md | 54 + .../references/workflows.md | 151 + .../scripts/agent.py | 224 + .../scripts/process.py | 558 +++ .../LICENSE | 201 + .../SKILL.md | 61 + .../references/api-reference.md | 63 + .../scripts/agent.py | 231 ++ .../LICENSE | 201 + .../SKILL.md | 281 ++ .../assets/template.md | 42 + .../references/api-reference.md | 84 + .../references/standards.md | 23 + .../references/workflows.md | 36 + .../scripts/agent.py | 173 + .../scripts/process.py | 181 + .../LICENSE | 201 + .../SKILL.md | 242 ++ .../assets/template.md | 63 + .../references/api-reference.md | 88 + .../references/standards.md | 41 + .../references/workflows.md | 45 + .../scripts/agent.py | 168 + .../scripts/process.py | 211 + .../LICENSE | 201 + .../SKILL.md | 121 + .../assets/template.md | 35 + .../references/api-reference.md | 90 + .../references/standards.md | 32 + .../references/workflows.md | 71 + .../scripts/agent.py | 170 + .../scripts/process.py | 289 ++ .../LICENSE | 201 + .../SKILL.md | 416 ++ .../assets/template.md | 30 + .../references/api-reference.md | 106 + .../references/standards.md | 23 + .../references/workflows.md | 52 + .../scripts/agent.py | 165 + .../scripts/process.py | 219 + .../LICENSE | 201 + .../SKILL.md | 395 ++ .../references/api-reference.md | 168 + .../scripts/agent.py | 438 ++ .../LICENSE | 201 + .../SKILL.md | 249 ++ .../assets/template.md | 69 + .../references/api-reference.md | 112 + .../references/standards.md | 40 + .../references/workflows.md | 55 + .../scripts/agent.py | 175 + .../scripts/process.py | 314 ++ .../LICENSE | 201 + .../SKILL.md | 170 + .../assets/template.md | 126 + .../references/api-reference.md | 199 + .../references/standards.md | 44 + .../references/workflows.md | 70 + .../scripts/agent.py | 261 ++ .../scripts/process.py | 234 ++ .../LICENSE | 201 + .../SKILL.md | 373 ++ .../references/api-reference.md | 56 + .../scripts/agent.py | 191 + .../LICENSE | 201 + .../SKILL.md | 436 ++ .../references/api-reference.md | 64 + .../scripts/agent.py | 232 ++ .../LICENSE | 201 + .../SKILL.md | 413 ++ .../references/api-reference.md | 57 + .../scripts/agent.py | 262 ++ .../LICENSE | 201 + .../SKILL.md | 394 ++ .../references/api-reference.md | 56 + .../scripts/agent.py | 231 ++ .../LICENSE | 201 + .../SKILL.md | 273 ++ .../references/api-reference.md | 60 + .../scripts/agent.py | 196 + .../LICENSE | 201 + .../SKILL.md | 165 + .../assets/template.md | 15 + .../references/api-reference.md | 62 + .../references/standards.md | 13 + .../references/workflows.md | 18 + .../scripts/agent.py | 201 + .../scripts/process.py | 132 + .../LICENSE | 201 + .../SKILL.md | 266 ++ .../assets/template.md | 58 + .../references/api-reference.md | 64 + .../references/standards.md | 60 + .../references/workflows.md | 86 + .../scripts/agent.py | 222 + .../scripts/process.py | 265 ++ .../LICENSE | 201 + .../SKILL.md | 233 ++ .../assets/template.md | 38 + .../references/api-reference.md | 62 + .../references/standards.md | 39 + .../references/workflows.md | 61 + .../scripts/agent.py | 180 + .../scripts/process.py | 390 ++ .../LICENSE | 201 + .../SKILL.md | 173 + .../assets/template.md | 25 + .../references/api-reference.md | 204 + .../references/standards.md | 9 + .../references/workflows.md | 11 + .../scripts/agent.py | 288 ++ .../LICENSE | 201 + .../SKILL.md | 258 ++ .../assets/template.md | 78 + .../references/api-reference.md | 177 + .../references/standards.md | 58 + .../references/workflows.md | 75 + .../scripts/agent.py | 252 ++ .../scripts/process.py | 223 + .../LICENSE | 201 + .../SKILL.md | 285 ++ .../references/api-reference.md | 62 + .../scripts/agent.py | 204 + .../LICENSE | 201 + .../SKILL.md | 327 ++ .../references/api-reference.md | 61 + .../scripts/agent.py | 156 + .../LICENSE | 201 + .../SKILL.md | 453 +++ .../references/api-reference.md | 74 + .../scripts/agent.py | 200 + .../LICENSE | 201 + .../SKILL.md | 247 ++ .../references/api-reference.md | 177 + .../scripts/agent.py | 252 ++ .../LICENSE | 201 + .../SKILL.md | 85 + .../references/api-reference.md | 85 + .../scripts/agent.py | 186 + .../LICENSE | 201 + .../SKILL.md | 251 ++ .../references/api-reference.md | 158 + .../scripts/agent.py | 185 + .../LICENSE | 201 + .../SKILL.md | 435 ++ .../references/api-reference.md | 58 + .../scripts/agent.py | 234 ++ .../LICENSE | 201 + .../SKILL.md | 249 ++ .../assets/template.md | 26 + .../references/api-reference.md | 187 + .../references/standards.md | 17 + .../references/workflows.md | 22 + .../scripts/agent.py | 239 ++ .../scripts/process.py | 173 + .../LICENSE | 201 + .../SKILL.md | 341 ++ .../references/api-reference.md | 63 + .../scripts/agent.py | 223 + .../LICENSE | 201 + .../SKILL.md | 87 + .../references/api-reference.md | 113 + .../scripts/agent.py | 211 + .../LICENSE | 201 + .../SKILL.md | 310 ++ .../assets/template.md | 51 + .../references/api-reference.md | 174 + .../references/standards.md | 30 + .../references/workflows.md | 116 + .../scripts/agent.py | 327 ++ .../scripts/process.py | 280 ++ .../LICENSE | 19 + .../SKILL.md | 498 +++ .../references/athena-forensics-reference.md | 136 + .../scripts/agent.py | 807 ++++ .../LICENSE | 201 + .../SKILL.md | 77 + .../references/api-reference.md | 59 + .../scripts/agent.py | 195 + .../SKILL.md | 166 + .../assets/template.md | 34 + .../references/standards.md | 19 + .../references/workflows.md | 31 + .../scripts/process.py | 217 + .../LICENSE | 201 + .../SKILL.md | 299 ++ .../references/api-reference.md | 65 + .../scripts/agent.py | 230 ++ .../LICENSE | 201 + .../SKILL.md | 353 ++ .../assets/template.md | 22 + .../references/api-reference.md | 197 + .../references/standards.md | 18 + .../references/workflows.md | 33 + .../scripts/agent.py | 300 ++ .../scripts/process.py | 130 + .../LICENSE | 201 + .../SKILL.md | 74 + .../references/api-reference.md | 48 + .../scripts/agent.py | 197 + .../LICENSE | 201 + .../SKILL.md | 256 ++ .../assets/template.md | 51 + .../references/api-reference.md | 191 + .../references/standards.md | 30 + .../references/workflows.md | 56 + .../scripts/agent.py | 311 ++ .../scripts/process.py | 176 + .../LICENSE | 201 + .../SKILL.md | 68 + .../references/api-reference.md | 97 + .../scripts/agent.py | 159 + .../LICENSE | 201 + .../SKILL.md | 241 ++ .../references/api-reference.md | 211 + .../scripts/agent.py | 312 ++ .../LICENSE | 201 + .../SKILL.md | 223 + .../assets/template.md | 36 + .../references/api-reference.md | 208 + .../references/standards.md | 29 + .../references/workflows.md | 53 + .../scripts/agent.py | 275 ++ .../scripts/process.py | 197 + .../LICENSE | 201 + .../SKILL.md | 83 + .../assets/template.md | 65 + .../references/api-reference.md | 206 + .../references/standards.md | 40 + .../references/workflows.md | 62 + .../scripts/agent.py | 325 ++ .../scripts/process.py | 342 ++ .../performing-csrf-attack-simulation/LICENSE | 201 + .../SKILL.md | 359 ++ .../references/api-reference.md | 57 + .../scripts/agent.py | 253 ++ .../LICENSE | 201 + .../SKILL.md | 265 ++ .../assets/template.md | 31 + .../references/api-reference.md | 175 + .../references/standards.md | 27 + .../references/workflows.md | 52 + .../scripts/agent.py | 239 ++ .../scripts/process.py | 245 ++ .../LICENSE | 201 + .../SKILL.md | 264 ++ .../assets/template.md | 32 + .../references/api-reference.md | 213 + .../references/standards.md | 33 + .../references/workflows.md | 33 + .../scripts/agent.py | 249 ++ .../scripts/process.py | 228 ++ .../LICENSE | 201 + .../SKILL.md | 298 ++ .../references/api-reference.md | 59 + .../scripts/agent.py | 212 + .../LICENSE | 201 + .../SKILL.md | 322 ++ .../references/api-reference.md | 57 + .../scripts/agent.py | 261 ++ .../LICENSE | 201 + .../SKILL.md | 270 ++ .../references/api-reference.md | 62 + .../scripts/agent.py | 270 ++ .../LICENSE | 201 + .../SKILL.md | 130 + .../assets/template.md | 37 + .../references/api-reference.md | 30 + .../references/standards.md | 34 + .../references/workflows.md | 111 + .../scripts/agent.py | 145 + .../scripts/process.py | 338 ++ .../LICENSE | 201 + .../SKILL.md | 268 ++ .../references/api-reference.md | 63 + .../scripts/agent.py | 251 ++ .../LICENSE | 201 + .../SKILL.md | 82 + .../references/api-reference.md | 61 + .../scripts/agent.py | 178 + .../LICENSE | 201 + .../SKILL.md | 144 + .../assets/template.md | 22 + .../references/api-reference.md | 37 + .../references/standards.md | 17 + .../references/workflows.md | 18 + .../scripts/agent.py | 142 + .../scripts/process.py | 56 + .../LICENSE | 201 + .../SKILL.md | 245 ++ .../assets/template.md | 46 + .../references/api-reference.md | 41 + .../references/standards.md | 43 + .../references/workflows.md | 56 + .../scripts/agent.py | 153 + .../scripts/process.py | 248 ++ .../LICENSE | 201 + .../SKILL.md | 275 ++ .../references/api-reference.md | 75 + .../scripts/agent.py | 317 ++ .../LICENSE | 201 + .../SKILL.md | 243 ++ .../assets/template.md | 50 + .../references/api-reference.md | 211 + .../references/standards.md | 32 + .../references/workflows.md | 66 + .../scripts/agent.py | 165 + .../scripts/process.py | 209 + .../LICENSE | 201 + .../SKILL.md | 235 ++ .../assets/template.md | 74 + .../references/api-reference.md | 141 + .../references/standards.md | 52 + .../references/workflows.md | 127 + .../scripts/agent.py | 122 + .../scripts/process.py | 231 ++ .../LICENSE | 201 + .../SKILL.md | 459 +++ .../references/api-reference.md | 43 + .../scripts/agent.py | 173 + .../LICENSE | 201 + .../SKILL.md | 351 ++ .../assets/template.md | 146 + .../references/api-reference.md | 42 + .../references/standards.md | 49 + .../references/workflows.md | 95 + .../scripts/agent.py | 146 + .../scripts/process.py | 382 ++ .../LICENSE | 201 + .../SKILL.md | 232 ++ .../assets/template.md | 28 + .../references/api-reference.md | 36 + .../references/standards.md | 28 + .../references/workflows.md | 16 + .../scripts/agent.py | 133 + .../scripts/process.py | 155 + .../LICENSE | 201 + .../SKILL.md | 327 ++ .../references/api-reference.md | 48 + .../scripts/agent.py | 180 + .../LICENSE | 201 + .../SKILL.md | 260 ++ .../references/api-reference.md | 148 + .../scripts/agent.py | 448 ++ .../LICENSE | 201 + .../SKILL.md | 310 ++ .../references/api-reference.md | 45 + .../scripts/agent.py | 215 + .../LICENSE | 201 + .../SKILL.md | 82 + .../references/api-reference.md | 64 + .../scripts/agent.py | 187 + .../LICENSE | 201 + .../SKILL.md | 68 + .../references/api-reference.md | 82 + .../scripts/agent.py | 217 + .../LICENSE | 201 + .../SKILL.md | 294 ++ .../references/api-reference.md | 47 + .../scripts/agent.py | 191 + .../LICENSE | 201 + .../SKILL.md | 420 ++ .../references/api-reference.md | 41 + .../scripts/agent.py | 171 + .../LICENSE | 201 + .../SKILL.md | 521 +++ .../references/api-reference.md | 180 + .../scripts/agent.py | 115 + .../LICENSE | 201 + .../SKILL.md | 334 ++ .../references/api-reference.md | 50 + .../scripts/agent.py | 196 + .../LICENSE | 201 + .../SKILL.md | 67 + .../references/api-reference.md | 70 + .../scripts/agent.py | 222 + .../LICENSE | 201 + .../SKILL.md | 90 + .../assets/template.md | 49 + .../references/api-reference.md | 40 + .../references/standards.md | 30 + .../references/workflows.md | 62 + .../scripts/agent.py | 171 + .../scripts/process.py | 265 ++ .../LICENSE | 201 + .../SKILL.md | 202 + .../references/api-reference.md | 42 + .../scripts/agent.py | 155 + .../LICENSE | 201 + .../SKILL.md | 516 +++ .../references/api-reference.md | 45 + .../scripts/agent.py | 173 + .../LICENSE | 201 + .../SKILL.md | 140 + .../assets/template.md | 39 + .../references/api-reference.md | 45 + .../references/standards.md | 24 + .../references/workflows.md | 31 + .../scripts/agent.py | 168 + .../scripts/process.py | 130 + .../LICENSE | 201 + .../SKILL.md | 182 + .../assets/template.md | 55 + .../references/api-reference.md | 46 + .../references/standards.md | 32 + .../references/workflows.md | 60 + .../scripts/agent.py | 166 + .../scripts/process.py | 180 + .../LICENSE | 201 + .../SKILL.md | 256 ++ .../references/api-reference.md | 43 + .../scripts/agent.py | 234 ++ .../LICENSE | 201 + .../SKILL.md | 426 ++ .../references/api-reference.md | 54 + .../scripts/agent.py | 243 ++ .../LICENSE | 201 + .../SKILL.md | 280 ++ .../references/api-reference.md | 78 + .../scripts/agent.py | 616 +++ .../LICENSE | 201 + .../SKILL.md | 194 + .../references/api-reference.md | 50 + .../scripts/agent.py | 241 ++ .../LICENSE | 201 + .../SKILL.md | 298 ++ .../references/api-reference.md | 37 + .../scripts/agent.py | 123 + .../LICENSE | 201 + .../SKILL.md | 339 ++ .../references/api-reference.md | 47 + .../scripts/agent.py | 164 + .../performing-kerberoasting-attack/LICENSE | 201 + .../performing-kerberoasting-attack/SKILL.md | 109 + .../assets/template.md | 57 + .../references/api-reference.md | 43 + .../references/standards.md | 74 + .../references/workflows.md | 131 + .../scripts/agent.py | 175 + .../scripts/process.py | 371 ++ .../LICENSE | 201 + .../SKILL.md | 231 ++ .../assets/template.md | 30 + .../references/api-reference.md | 43 + .../references/standards.md | 41 + .../references/workflows.md | 78 + .../scripts/agent.py | 171 + .../scripts/process.py | 180 + .../LICENSE | 201 + .../SKILL.md | 218 + .../assets/template.md | 26 + .../references/api-reference.md | 46 + .../references/standards.md | 22 + .../references/workflows.md | 19 + .../scripts/agent.py | 186 + .../scripts/process.py | 195 + .../LICENSE | 201 + .../SKILL.md | 275 ++ .../assets/template.md | 58 + .../references/api-reference.md | 40 + .../references/standards.md | 54 + .../references/workflows.md | 92 + .../scripts/agent.py | 156 + .../scripts/process.py | 414 ++ .../LICENSE | 201 + .../SKILL.md | 309 ++ .../references/api-reference.md | 47 + .../scripts/agent.py | 220 + .../LICENSE | 201 + .../SKILL.md | 206 + .../assets/template.md | 20 + .../references/api-reference.md | 42 + .../references/standards.md | 24 + .../references/workflows.md | 21 + .../scripts/agent.py | 162 + .../scripts/process.py | 107 + .../LICENSE | 201 + .../SKILL.md | 294 ++ .../assets/template.md | 17 + .../references/api-reference.md | 44 + .../references/standards.md | 15 + .../references/workflows.md | 27 + .../scripts/agent.py | 179 + .../scripts/process.py | 42 + .../LICENSE | 201 + .../SKILL.md | 320 ++ .../references/api-reference.md | 53 + .../scripts/agent.py | 220 + .../LICENSE | 201 + .../SKILL.md | 292 ++ .../assets/template.md | 46 + .../references/api-reference.md | 41 + .../references/standards.md | 31 + .../references/workflows.md | 60 + .../scripts/agent.py | 168 + .../scripts/process.py | 197 + .../LICENSE | 201 + .../SKILL.md | 342 ++ .../references/api-reference.md | 32 + .../scripts/agent.py | 141 + .../performing-malware-ioc-extraction/LICENSE | 201 + .../SKILL.md | 369 ++ .../assets/template.md | 99 + .../references/api-reference.md | 48 + .../references/standards.md | 81 + .../references/workflows.md | 70 + .../scripts/agent.py | 149 + .../scripts/process.py | 452 +++ .../LICENSE | 201 + .../SKILL.md | 476 +++ .../references/api-reference.md | 49 + .../scripts/agent.py | 248 ++ .../LICENSE | 201 + .../SKILL.md | 373 ++ .../references/api-reference.md | 54 + .../scripts/agent.py | 182 + .../LICENSE | 201 + .../SKILL.md | 236 ++ .../assets/template.md | 28 + .../references/api-reference.md | 49 + .../references/standards.md | 29 + .../references/workflows.md | 20 + .../scripts/agent.py | 168 + .../scripts/process.py | 71 + .../LICENSE | 201 + .../SKILL.es.md | 49 + .../SKILL.md | 296 ++ .../references/api-reference.md | 47 + .../scripts/agent.py | 160 + .../LICENSE | 201 + .../SKILL.md | 235 ++ .../assets/template.md | 21 + .../references/api-reference.md | 43 + .../references/standards.md | 20 + .../references/workflows.md | 30 + .../scripts/agent.py | 165 + .../scripts/process.py | 149 + .../LICENSE | 201 + .../SKILL.md | 344 ++ .../references/api-reference.md | 52 + .../scripts/agent.py | 220 + .../LICENSE | 201 + .../SKILL.md | 313 ++ .../references/api-reference.md | 52 + .../scripts/agent.py | 229 ++ .../LICENSE | 201 + .../SKILL.md | 261 ++ .../assets/template.md | 19 + .../references/api-reference.md | 45 + .../references/standards.md | 13 + .../references/workflows.md | 21 + .../scripts/agent.py | 154 + .../scripts/process.py | 37 + .../LICENSE | 201 + .../SKILL.md | 59 + .../references/api-reference.md | 86 + .../scripts/agent.py | 228 ++ .../LICENSE | 201 + .../SKILL.md | 417 ++ .../references/api-reference.md | 49 + .../scripts/agent.py | 161 + .../LICENSE | 201 + .../SKILL.md | 131 + .../assets/template.md | 106 + .../references/api-reference.md | 47 + .../references/standards.md | 87 + .../references/workflows.md | 184 + .../scripts/agent.py | 188 + .../scripts/process.py | 355 ++ .../LICENSE | 201 + .../SKILL.md | 617 +++ .../references/api-reference.md | 51 + .../scripts/agent.py | 203 + .../LICENSE | 201 + .../SKILL.md | 369 ++ .../references/api-reference.md | 45 + .../scripts/agent.py | 156 + .../LICENSE | 201 + .../SKILL.md | 135 + .../assets/template.md | 189 + .../references/api-reference.md | 43 + .../references/standards.md | 67 + .../references/workflows.md | 253 ++ .../scripts/agent.py | 176 + .../scripts/process.py | 604 +++ .../performing-osint-with-spiderfoot/LICENSE | 201 + .../SKILL.es.md | 38 + .../performing-osint-with-spiderfoot/SKILL.md | 57 + .../references/api-reference.md | 79 + .../scripts/agent.py | 176 + .../LICENSE | 201 + .../SKILL.md | 594 +++ .../assets/template.md | 169 + .../references/api-reference.md | 42 + .../references/standards.md | 87 + .../references/workflows.md | 119 + .../scripts/agent.py | 156 + .../scripts/process.py | 417 ++ .../LICENSE | 201 + .../SKILL.md | 314 ++ .../references/api-reference.md | 38 + .../scripts/agent.py | 135 + .../LICENSE | 201 + .../SKILL.md | 291 ++ .../references/api-reference.md | 44 + .../scripts/agent.py | 169 + .../LICENSE | 201 + .../SKILL.md | 327 ++ .../references/api-reference.md | 61 + .../scripts/agent.py | 168 + .../LICENSE | 201 + .../SKILL.md | 341 ++ .../references/api-reference.md | 39 + .../scripts/agent.py | 176 + .../LICENSE | 201 + .../SKILL.md | 114 + .../assets/template.md | 77 + .../references/api-reference.md | 46 + .../references/standards.md | 58 + .../references/workflows.md | 135 + .../scripts/agent.py | 181 + .../scripts/process.py | 515 +++ .../LICENSE | 201 + .../SKILL.md | 217 + .../assets/template.md | 59 + .../references/api-reference.md | 39 + .../references/standards.md | 31 + .../references/workflows.md | 79 + .../scripts/agent.py | 150 + .../scripts/process.py | 202 + .../LICENSE | 201 + .../SKILL.md | 497 +++ .../references/api-reference.md | 46 + .../scripts/agent.py | 184 + .../LICENSE | 201 + .../SKILL.md | 338 ++ .../references/api-reference.md | 255 ++ .../scripts/agent.py | 1568 +++++++ .../LICENSE | 201 + .../SKILL.md | 309 ++ .../references/api-reference.md | 40 + .../scripts/agent.py | 172 + .../LICENSE | 201 + .../SKILL.md | 282 ++ .../references/api-reference.md | 252 ++ .../scripts/agent.py | 1413 +++++++ .../LICENSE | 201 + .../SKILL.md | 212 + .../references/api-reference.md | 49 + .../scripts/agent.py | 248 ++ .../LICENSE | 201 + .../SKILL.md | 113 + .../references/api-reference.md | 53 + .../references/standards.md | 27 + .../references/workflows.md | 161 + .../scripts/agent.py | 208 + .../LICENSE | 201 + .../SKILL.md | 172 + .../assets/template.md | 66 + .../references/api-reference.md | 62 + .../references/standards.md | 50 + .../references/workflows.md | 100 + .../scripts/agent.py | 136 + .../scripts/process.py | 311 ++ .../LICENSE | 201 + .../SKILL.md | 64 + .../references/api-reference.md | 58 + .../scripts/agent.py | 136 + .../LICENSE | 201 + .../SKILL.md | 1006 +++++ .../references/api-reference.md | 148 + .../scripts/agent.py | 898 ++++ .../performing-purple-team-exercise/LICENSE | 201 + .../performing-purple-team-exercise/SKILL.md | 310 ++ .../references/api-reference.md | 50 + .../scripts/agent.py | 197 + .../performing-ransomware-response/LICENSE | 201 + .../performing-ransomware-response/SKILL.md | 232 ++ .../references/api-reference.md | 47 + .../scripts/agent.py | 225 ++ .../LICENSE | 201 + .../SKILL.md | 236 ++ .../assets/template.md | 112 + .../references/api-reference.md | 84 + .../references/standards.md | 48 + .../references/workflows.md | 139 + .../scripts/agent.py | 151 + .../scripts/process.py | 500 +++ .../LICENSE | 201 + .../SKILL.md | 66 + .../references/api-reference.md | 68 + .../scripts/agent.py | 198 + .../performing-red-team-with-covenant/LICENSE | 201 + .../SKILL.md | 62 + .../references/api-reference.md | 66 + .../scripts/agent.py | 193 + .../LICENSE | 201 + .../SKILL.md | 432 ++ .../references/api-reference.md | 80 + .../scripts/agent.py | 178 + .../LICENSE | 201 + .../SKILL.md | 261 ++ .../assets/template.md | 131 + .../references/api-reference.md | 80 + .../references/standards.md | 63 + .../references/workflows.md | 130 + .../scripts/agent.py | 160 + .../scripts/process.py | 266 ++ .../LICENSE | 201 + .../SKILL.md | 314 ++ .../references/api-reference.md | 86 + .../scripts/agent.py | 186 + .../LICENSE | 201 + .../SKILL.md | 228 ++ .../references/api-reference.md | 87 + .../scripts/agent.py | 163 + .../performing-security-headers-audit/LICENSE | 201 + .../SKILL.md | 320 ++ .../references/api-reference.md | 44 + .../scripts/agent.py | 276 ++ .../LICENSE | 201 + .../SKILL.md | 299 ++ .../references/api-reference.md | 72 + .../scripts/agent.py | 153 + .../performing-service-account-audit/LICENSE | 201 + .../performing-service-account-audit/SKILL.md | 125 + .../assets/template.md | 25 + .../references/api-reference.md | 55 + .../references/standards.md | 18 + .../references/workflows.md | 22 + .../scripts/agent.py | 173 + .../scripts/process.py | 267 ++ .../LICENSE | 201 + .../SKILL.md | 270 ++ .../assets/template.md | 32 + .../references/api-reference.md | 58 + .../references/standards.md | 47 + .../references/workflows.md | 93 + .../scripts/agent.py | 163 + .../scripts/process.py | 262 ++ .../LICENSE | 201 + .../SKILL.md | 453 +++ .../references/api-reference.md | 55 + .../scripts/agent.py | 182 + .../performing-soc-tabletop-exercise/LICENSE | 201 + .../performing-soc-tabletop-exercise/SKILL.md | 406 ++ .../references/api-reference.md | 63 + .../scripts/agent.py | 193 + .../LICENSE | 201 + .../SKILL.md | 301 ++ .../assets/template.md | 232 ++ .../references/api-reference.md | 259 ++ .../references/standards.md | 116 + .../references/workflows.md | 258 ++ .../scripts/agent.py | 879 ++++ .../scripts/process.py | 566 +++ .../LICENSE | 201 + .../SKILL.md | 364 ++ .../assets/template.md | 31 + .../references/api-reference.md | 56 + .../references/standards.md | 20 + .../references/workflows.md | 39 + .../scripts/agent.py | 204 + .../scripts/process.py | 192 + .../LICENSE | 201 + .../SKILL.md | 89 + .../assets/template.md | 54 + .../references/api-reference.md | 57 + .../references/standards.md | 42 + .../references/workflows.md | 81 + .../scripts/agent.py | 163 + .../scripts/process.py | 349 ++ .../performing-ssl-stripping-attack/LICENSE | 201 + .../performing-ssl-stripping-attack/SKILL.md | 262 ++ .../references/api-reference.md | 60 + .../scripts/agent.py | 149 + .../LICENSE | 201 + .../SKILL.md | 285 ++ .../references/api-reference.md | 60 + .../scripts/agent.py | 158 + .../LICENSE | 201 + .../SKILL.md | 60 + .../references/api-reference.md | 62 + .../scripts/agent.py | 187 + .../LICENSE | 201 + .../SKILL.md | 61 + .../references/api-reference.md | 40 + .../scripts/agent.py | 174 + .../LICENSE | 201 + .../SKILL.md | 307 ++ .../references/api-reference.md | 60 + .../scripts/agent.py | 217 + .../LICENSE | 201 + .../SKILL.md | 345 ++ .../references/api-reference.md | 66 + .../scripts/agent.py | 195 + .../LICENSE | 201 + .../SKILL.md | 193 + .../assets/template.md | 45 + .../references/api-reference.md | 69 + .../references/standards.md | 26 + .../references/workflows.md | 39 + .../scripts/agent.py | 168 + .../scripts/process.py | 164 + .../LICENSE | 201 + .../SKILL.md | 59 + .../references/api-reference.md | 123 + .../scripts/agent.py | 266 ++ .../LICENSE | 201 + .../SKILL.md | 265 ++ .../assets/template.md | 24 + .../references/api-reference.md | 58 + .../references/standards.md | 18 + .../references/workflows.md | 26 + .../scripts/agent.py | 209 + .../scripts/process.py | 190 + .../LICENSE | 201 + .../SKILL.md | 89 + .../references/api-reference.md | 67 + .../scripts/agent.py | 197 + .../LICENSE | 201 + .../SKILL.md | 285 ++ .../references/api-reference.md | 66 + .../scripts/agent.py | 225 ++ .../LICENSE | 201 + .../SKILL.md | 426 ++ .../references/api-reference.md | 89 + .../scripts/agent.py | 173 + .../LICENSE | 201 + .../SKILL.md | 62 + .../references/api-reference.md | 97 + .../scripts/agent.py | 218 + .../LICENSE | 201 + .../SKILL.md | 294 ++ .../references/api-reference.md | 56 + .../scripts/agent.py | 168 + .../LICENSE | 201 + .../SKILL.md | 209 + .../assets/template.md | 48 + .../references/api-reference.md | 64 + .../references/standards.md | 44 + .../references/workflows.md | 75 + .../scripts/agent.py | 204 + .../scripts/process.py | 191 + .../LICENSE | 201 + .../SKILL.md | 325 ++ .../references/api-reference.md | 68 + .../scripts/agent.py | 172 + .../LICENSE | 201 + .../SKILL.md | 253 ++ .../references/api-reference.md | 55 + .../scripts/agent.py | 219 + .../performing-vlan-hopping-attack/LICENSE | 201 + .../performing-vlan-hopping-attack/SKILL.md | 326 ++ .../references/api-reference.md | 64 + .../scripts/agent.py | 185 + .../LICENSE | 201 + .../SKILL.md | 169 + .../references/api-reference.md | 75 + .../scripts/agent.py | 197 + .../LICENSE | 201 + .../SKILL.md | 255 ++ .../references/api-reference.md | 53 + .../scripts/agent.py | 145 + .../LICENSE | 201 + .../SKILL.md | 201 + .../references/api-reference.md | 64 + .../scripts/agent.py | 244 ++ .../LICENSE | 201 + .../SKILL.md | 239 ++ .../assets/template.md | 37 + .../references/api-reference.md | 57 + .../references/standards.md | 41 + .../references/workflows.md | 43 + .../scripts/agent.py | 140 + .../scripts/process.py | 297 ++ .../LICENSE | 201 + .../SKILL.md | 243 ++ .../references/api-reference.md | 59 + .../references/standards.md | 33 + .../references/workflows.md | 26 + .../scripts/agent.py | 116 + .../LICENSE | 201 + .../SKILL.md | 218 + .../references/api-reference.md | 58 + .../scripts/agent.py | 137 + .../LICENSE | 201 + .../SKILL.md | 321 ++ .../references/api-reference.md | 61 + .../scripts/agent.py | 221 + .../LICENSE | 201 + .../SKILL.md | 276 ++ .../references/api-reference.md | 73 + .../scripts/agent.py | 210 + .../LICENSE | 201 + .../SKILL.md | 288 ++ .../assets/template.md | 65 + .../references/api-reference.md | 51 + .../references/standards.md | 59 + .../references/workflows.md | 98 + .../scripts/agent.py | 161 + .../scripts/process.py | 295 ++ .../LICENSE | 201 + .../SKILL.md | 258 ++ .../assets/template.md | 33 + .../references/api-reference.md | 47 + .../references/standards.md | 26 + .../references/workflows.md | 31 + .../scripts/agent.py | 145 + .../scripts/process.py | 200 + .../LICENSE | 201 + .../SKILL.md | 390 ++ .../references/api-reference.md | 55 + .../scripts/agent.py | 147 + .../LICENSE | 201 + .../SKILL.md | 337 ++ .../assets/template.md | 31 + .../references/api-reference.md | 66 + .../references/standards.md | 35 + .../references/workflows.md | 34 + .../scripts/agent.py | 161 + .../scripts/process.py | 266 ++ .../LICENSE | 201 + .../SKILL.md | 188 + .../assets/template.md | 36 + .../references/api-reference.md | 65 + .../references/standards.md | 38 + .../references/workflows.md | 53 + .../scripts/agent.py | 175 + .../scripts/process.py | 401 ++ .../processing-stix-taxii-feeds/LICENSE | 201 + .../processing-stix-taxii-feeds/SKILL.md | 161 + .../references/api-reference.md | 60 + .../scripts/agent.py | 212 + .../profiling-threat-actor-groups/LICENSE | 201 + .../profiling-threat-actor-groups/SKILL.md | 135 + .../references/api-reference.md | 67 + .../scripts/agent.py | 180 + .../LICENSE | 201 + .../SKILL.md | 245 ++ .../references/api-reference.md | 59 + .../scripts/agent.py | 202 + .../recovering-from-ransomware-attack/LICENSE | 201 + .../SKILL.md | 310 ++ .../references/api-reference.md | 55 + .../references/standards.md | 20 + .../references/workflows.md | 65 + .../scripts/agent.py | 160 + .../scripts/process.py | 290 ++ .../LICENSE | 201 + .../SKILL.md | 312 ++ .../references/api-reference.md | 81 + .../scripts/agent.py | 208 + .../LICENSE | 201 + .../SKILL.md | 353 ++ .../references/api-reference.md | 67 + .../scripts/agent.py | 196 + .../LICENSE | 201 + .../SKILL.md | 348 ++ .../references/api-reference.md | 68 + .../scripts/agent.py | 218 + .../LICENSE | 201 + .../SKILL.md | 251 ++ .../assets/template.md | 38 + .../references/api-reference.md | 48 + .../references/standards.md | 27 + .../references/workflows.md | 36 + .../scripts/agent.py | 160 + .../scripts/process.py | 119 + .../LICENSE | 201 + .../SKILL.md | 289 ++ .../references/api-reference.md | 75 + .../scripts/agent.py | 232 ++ .../LICENSE | 201 + .../SKILL.md | 288 ++ .../assets/template.md | 30 + .../references/api-reference.md | 52 + .../references/standards.md | 30 + .../references/workflows.md | 37 + .../scripts/agent.py | 183 + .../scripts/process.py | 179 + .../reverse-engineering-rust-malware/LICENSE | 201 + .../reverse-engineering-rust-malware/SKILL.md | 179 + .../assets/template.md | 25 + .../references/api-reference.md | 45 + .../references/standards.md | 9 + .../references/workflows.md | 11 + .../scripts/agent.py | 173 + .../LICENSE | 201 + .../SKILL.md | 229 ++ .../assets/template.md | 85 + .../references/api-reference.md | 50 + .../references/standards.md | 58 + .../references/workflows.md | 162 + .../scripts/agent.py | 145 + .../scripts/process.py | 183 + .../LICENSE | 201 + .../SKILL.md | 323 ++ .../assets/template.md | 195 + .../references/api-reference.md | 57 + .../references/standards.md | 62 + .../references/workflows.md | 131 + .../scripts/agent.py | 170 + .../scripts/process.py | 326 ++ .../scanning-docker-images-with-trivy/LICENSE | 201 + .../SKILL.md | 261 ++ .../assets/template.md | 75 + .../references/api-reference.md | 48 + .../references/standards.md | 66 + .../references/workflows.md | 136 + .../scripts/agent.py | 164 + .../scripts/process.py | 334 ++ .../LICENSE | 201 + .../SKILL.md | 174 + .../assets/template.md | 115 + .../references/api-reference.md | 57 + .../references/standards.md | 36 + .../references/workflows.md | 83 + .../scripts/agent.py | 183 + .../scripts/process.py | 489 +++ .../LICENSE | 201 + .../SKILL.md | 300 ++ .../assets/template.md | 51 + .../references/api-reference.md | 53 + .../references/standards.md | 44 + .../references/workflows.md | 59 + .../scripts/agent.py | 155 + .../scripts/process.py | 241 ++ .../LICENSE | 201 + .../SKILL.md | 206 + .../references/api-reference.md | 57 + .../scripts/agent.py | 158 + .../securing-api-gateway-with-aws-waf/LICENSE | 201 + .../SKILL.md | 450 +++ .../references/api-reference.md | 56 + .../scripts/agent.py | 168 + .../securing-aws-iam-permissions/LICENSE | 201 + .../securing-aws-iam-permissions/SKILL.md | 315 ++ .../references/api-reference.md | 54 + .../scripts/agent.py | 177 + .../LICENSE | 201 + .../SKILL.md | 373 ++ .../references/api-reference.md | 59 + .../scripts/agent.py | 175 + .../LICENSE | 201 + .../SKILL.md | 269 ++ .../references/api-reference.md | 61 + .../scripts/agent.py | 178 + .../LICENSE | 201 + .../SKILL.md | 303 ++ .../references/api-reference.md | 56 + .../scripts/agent.py | 196 + .../LICENSE | 201 + .../SKILL.md | 238 ++ .../assets/template.md | 19 + .../references/api-reference.md | 49 + .../references/standards.md | 28 + .../references/workflows.md | 26 + .../scripts/agent.py | 183 + .../scripts/process.py | 180 + .../securing-github-actions-workflows/LICENSE | 201 + .../SKILL.md | 255 ++ .../assets/template.md | 52 + .../references/api-reference.md | 55 + .../references/standards.md | 30 + .../references/workflows.md | 40 + .../scripts/agent.py | 218 + .../scripts/process.py | 210 + .../securing-helm-chart-deployments/LICENSE | 201 + .../securing-helm-chart-deployments/SKILL.md | 288 ++ .../assets/template.md | 40 + .../references/api-reference.md | 56 + .../references/standards.md | 35 + .../references/workflows.md | 29 + .../scripts/agent.py | 173 + .../scripts/process.py | 199 + .../LICENSE | 201 + .../SKILL.md | 383 ++ .../references/api-reference.md | 47 + .../scripts/agent.py | 215 + .../securing-kubernetes-on-cloud/LICENSE | 201 + .../securing-kubernetes-on-cloud/SKILL.md | 378 ++ .../references/api-reference.md | 68 + .../scripts/agent.py | 211 + .../LICENSE | 201 + .../SKILL.md | 384 ++ .../references/api-reference.md | 54 + .../scripts/agent.py | 193 + .../securing-serverless-functions/LICENSE | 201 + .../securing-serverless-functions/SKILL.md | 322 ++ .../references/api-reference.md | 56 + .../scripts/agent.py | 171 + .../LICENSE | 201 + .../SKILL.md | 186 + .../assets/template.md | 30 + .../references/api-reference.md | 50 + .../references/standards.md | 22 + .../references/workflows.md | 17 + .../scripts/agent.py | 178 + .../scripts/process.py | 197 + .../LICENSE | 201 + .../SKILL.md | 448 ++ .../references/api-reference.md | 57 + .../scripts/agent.py | 220 + .../LICENSE | 201 + .../SKILL.md | 386 ++ .../references/api-reference.md | 54 + .../scripts/agent.py | 198 + .../LICENSE | 201 + .../SKILL.md | 353 ++ .../references/api-reference.md | 52 + .../scripts/agent.py | 198 + .../LICENSE | 201 + .../SKILL.md | 348 ++ .../references/api-reference.md | 54 + .../scripts/agent.py | 223 + .../testing-cors-misconfiguration/LICENSE | 201 + .../testing-cors-misconfiguration/SKILL.md | 342 ++ .../references/api-reference.md | 50 + .../scripts/agent.py | 206 + .../testing-for-broken-access-control/LICENSE | 201 + .../SKILL.md | 360 ++ .../references/api-reference.md | 60 + .../scripts/agent.py | 195 + .../LICENSE | 201 + .../SKILL.md | 354 ++ .../references/api-reference.md | 56 + .../scripts/agent.py | 209 + .../LICENSE | 201 + .../SKILL.md | 225 ++ .../references/api-reference.md | 55 + .../scripts/agent.py | 188 + .../testing-for-host-header-injection/LICENSE | 201 + .../SKILL.md | 229 ++ .../references/api-reference.md | 44 + .../scripts/agent.py | 188 + .../LICENSE | 201 + .../SKILL.md | 237 ++ .../references/api-reference.md | 59 + .../scripts/agent.py | 216 + .../LICENSE | 201 + .../SKILL.md | 204 + .../references/api-reference.md | 45 + .../scripts/agent.py | 166 + .../LICENSE | 201 + .../SKILL.md | 371 ++ .../references/api-reference.md | 50 + .../scripts/agent.py | 236 ++ .../LICENSE | 201 + .../SKILL.md | 220 + .../references/api-reference.md | 45 + .../scripts/agent.py | 205 + .../LICENSE | 201 + .../SKILL.md | 318 ++ .../references/api-reference.md | 62 + .../scripts/agent.py | 198 + .../testing-for-xss-vulnerabilities/LICENSE | 201 + .../testing-for-xss-vulnerabilities/SKILL.md | 197 + .../references/api-reference.md | 53 + .../scripts/agent.py | 206 + .../LICENSE | 201 + .../SKILL.md | 351 ++ .../references/api-reference.md | 68 + .../scripts/agent.py | 233 ++ .../skills/testing-jwt-token-security/LICENSE | 201 + .../testing-jwt-token-security/SKILL.md | 347 ++ .../references/api-reference.md | 68 + .../scripts/agent.py | 237 ++ .../testing-mobile-api-authentication/LICENSE | 201 + .../SKILL.md | 195 + .../assets/template.md | 32 + .../references/api-reference.md | 48 + .../references/standards.md | 35 + .../references/workflows.md | 28 + .../scripts/agent.py | 197 + .../scripts/process.py | 243 ++ .../LICENSE | 201 + .../SKILL.md | 401 ++ .../references/api-reference.md | 50 + .../scripts/agent.py | 197 + .../LICENSE | 201 + .../SKILL.md | 173 + .../references/api-reference.md | 132 + .../scripts/agent.py | 316 ++ .../testing-websocket-api-security/LICENSE | 201 + .../testing-websocket-api-security/SKILL.md | 443 ++ .../references/api-reference.md | 49 + .../scripts/agent.py | 215 + .../LICENSE | 201 + .../SKILL.md | 299 ++ .../assets/template.md | 47 + .../references/api-reference.md | 48 + .../references/standards.md | 46 + .../references/workflows.md | 50 + .../scripts/agent.py | 194 + .../scripts/process.py | 284 ++ .../LICENSE | 201 + .../SKILL.md | 218 + .../references/api-reference.md | 69 + .../scripts/agent.py | 213 + .../LICENSE | 201 + .../SKILL.md | 229 ++ .../assets/template.md | 114 + .../references/api-reference.md | 49 + .../references/standards.md | 42 + .../references/workflows.md | 121 + .../scripts/agent.py | 206 + .../scripts/process.py | 366 ++ .../skills/triaging-security-incident/LICENSE | 201 + .../triaging-security-incident/SKILL.md | 229 ++ .../references/api-reference.md | 63 + .../scripts/agent.py | 262 ++ .../LICENSE | 201 + .../SKILL.md | 209 + .../references/api-reference.md | 56 + .../references/standards.md | 64 + .../references/workflows.md | 115 + .../scripts/agent.py | 219 + .../scripts/process.py | 346 ++ .../LICENSE | 201 + .../SKILL.md | 180 + .../references/api-reference.md | 160 + .../scripts/agent.py | 323 ++ 4189 files changed, 693908 insertions(+), 85 deletions(-) create mode 100644 personas/_shared/anthropic-cybersecurity-skills/.claude-plugin/marketplace.json create mode 100644 personas/_shared/anthropic-cybersecurity-skills/.claude-plugin/plugin.json create mode 100644 personas/_shared/anthropic-cybersecurity-skills/.github/FUNDING.yml create mode 100644 personas/_shared/anthropic-cybersecurity-skills/.github/ISSUE_TEMPLATE/bug-report.yml create mode 100644 personas/_shared/anthropic-cybersecurity-skills/.github/ISSUE_TEMPLATE/config.yml create mode 100644 personas/_shared/anthropic-cybersecurity-skills/.github/ISSUE_TEMPLATE/improve-skill.md create mode 100644 personas/_shared/anthropic-cybersecurity-skills/.github/ISSUE_TEMPLATE/new-skill-request.yml create mode 100644 personas/_shared/anthropic-cybersecurity-skills/.github/ISSUE_TEMPLATE/new-skill.md create mode 100644 personas/_shared/anthropic-cybersecurity-skills/.github/ISSUE_TEMPLATE/skill-improvement.yml create mode 100644 personas/_shared/anthropic-cybersecurity-skills/.github/workflows/sync-marketplace-version.yml create mode 100644 personas/_shared/anthropic-cybersecurity-skills/.github/workflows/update-index.yml create mode 100644 personas/_shared/anthropic-cybersecurity-skills/.github/workflows/validate-skills.yml create mode 100644 personas/_shared/anthropic-cybersecurity-skills/ATTACK_COVERAGE.md create mode 100644 personas/_shared/anthropic-cybersecurity-skills/CITATION.cff create mode 100644 personas/_shared/anthropic-cybersecurity-skills/CODE_OF_CONDUCT.md create mode 100644 personas/_shared/anthropic-cybersecurity-skills/CONTRIBUTING.md create mode 100644 personas/_shared/anthropic-cybersecurity-skills/LICENSE create mode 100644 personas/_shared/anthropic-cybersecurity-skills/README.md create mode 100644 personas/_shared/anthropic-cybersecurity-skills/SECURITY.md create mode 100644 personas/_shared/anthropic-cybersecurity-skills/assets/.gitkeep create mode 100644 personas/_shared/anthropic-cybersecurity-skills/assets/banner.png create mode 100644 personas/_shared/anthropic-cybersecurity-skills/index.json create mode 100644 personas/_shared/anthropic-cybersecurity-skills/mappings/README.md create mode 100644 personas/_shared/anthropic-cybersecurity-skills/mappings/attack-navigator-layer.json create mode 100644 personas/_shared/anthropic-cybersecurity-skills/mappings/mitre-attack/README.md create mode 100644 personas/_shared/anthropic-cybersecurity-skills/mappings/mitre-attack/coverage-summary.md create mode 100644 personas/_shared/anthropic-cybersecurity-skills/mappings/nist-csf/README.md create mode 100644 personas/_shared/anthropic-cybersecurity-skills/mappings/nist-csf/csf-alignment.md create mode 100644 personas/_shared/anthropic-cybersecurity-skills/mappings/owasp/README.md create mode 100644 personas/_shared/community-skills/olla/SKILL.md create mode 100644 personas/_shared/community-skills/olla/references/olla-docs-links.md create mode 100644 personas/_shared/skills/acquiring-disk-image-with-dd-and-dcfldd/LICENSE create mode 100644 personas/_shared/skills/acquiring-disk-image-with-dd-and-dcfldd/SKILL.md create mode 100644 personas/_shared/skills/acquiring-disk-image-with-dd-and-dcfldd/references/api-reference.md create mode 100644 personas/_shared/skills/acquiring-disk-image-with-dd-and-dcfldd/scripts/agent.py create mode 100644 personas/_shared/skills/analyzing-active-directory-acl-abuse/LICENSE create mode 100644 personas/_shared/skills/analyzing-active-directory-acl-abuse/SKILL.md create mode 100644 personas/_shared/skills/analyzing-active-directory-acl-abuse/references/api-reference.md create mode 100644 personas/_shared/skills/analyzing-active-directory-acl-abuse/scripts/agent.py create mode 100644 personas/_shared/skills/analyzing-android-malware-with-apktool/LICENSE create mode 100644 personas/_shared/skills/analyzing-android-malware-with-apktool/SKILL.md create mode 100644 personas/_shared/skills/analyzing-android-malware-with-apktool/references/api-reference.md create mode 100644 personas/_shared/skills/analyzing-android-malware-with-apktool/scripts/agent.py create mode 100644 personas/_shared/skills/analyzing-api-gateway-access-logs/LICENSE create mode 100644 personas/_shared/skills/analyzing-api-gateway-access-logs/SKILL.md create mode 100644 personas/_shared/skills/analyzing-api-gateway-access-logs/references/api-reference.md create mode 100644 personas/_shared/skills/analyzing-api-gateway-access-logs/scripts/agent.py create mode 100644 personas/_shared/skills/analyzing-apt-group-with-mitre-navigator/LICENSE create mode 100644 personas/_shared/skills/analyzing-apt-group-with-mitre-navigator/SKILL.md create mode 100644 personas/_shared/skills/analyzing-apt-group-with-mitre-navigator/references/api-reference.md create mode 100644 personas/_shared/skills/analyzing-apt-group-with-mitre-navigator/scripts/agent.py create mode 100644 personas/_shared/skills/analyzing-azure-activity-logs-for-threats/LICENSE create mode 100644 personas/_shared/skills/analyzing-azure-activity-logs-for-threats/SKILL.md create mode 100644 personas/_shared/skills/analyzing-azure-activity-logs-for-threats/references/api-reference.md create mode 100644 personas/_shared/skills/analyzing-azure-activity-logs-for-threats/scripts/agent.py create mode 100644 personas/_shared/skills/analyzing-bootkit-and-rootkit-samples/LICENSE create mode 100644 personas/_shared/skills/analyzing-bootkit-and-rootkit-samples/SKILL.md create mode 100644 personas/_shared/skills/analyzing-bootkit-and-rootkit-samples/references/api-reference.md create mode 100644 personas/_shared/skills/analyzing-bootkit-and-rootkit-samples/scripts/agent.py create mode 100644 personas/_shared/skills/analyzing-browser-forensics-with-hindsight/LICENSE create mode 100644 personas/_shared/skills/analyzing-browser-forensics-with-hindsight/SKILL.md create mode 100644 personas/_shared/skills/analyzing-browser-forensics-with-hindsight/assets/template.md create mode 100644 personas/_shared/skills/analyzing-browser-forensics-with-hindsight/references/api-reference.md create mode 100644 personas/_shared/skills/analyzing-browser-forensics-with-hindsight/references/standards.md create mode 100644 personas/_shared/skills/analyzing-browser-forensics-with-hindsight/references/workflows.md create mode 100644 personas/_shared/skills/analyzing-browser-forensics-with-hindsight/scripts/agent.py create mode 100644 personas/_shared/skills/analyzing-browser-forensics-with-hindsight/scripts/process.py create mode 100644 personas/_shared/skills/analyzing-campaign-attribution-evidence/LICENSE create mode 100644 personas/_shared/skills/analyzing-campaign-attribution-evidence/SKILL.md create mode 100644 personas/_shared/skills/analyzing-campaign-attribution-evidence/assets/template.md create mode 100644 personas/_shared/skills/analyzing-campaign-attribution-evidence/references/api-reference.md create mode 100644 personas/_shared/skills/analyzing-campaign-attribution-evidence/references/standards.md create mode 100644 personas/_shared/skills/analyzing-campaign-attribution-evidence/references/workflows.md create mode 100644 personas/_shared/skills/analyzing-campaign-attribution-evidence/scripts/agent.py create mode 100644 personas/_shared/skills/analyzing-campaign-attribution-evidence/scripts/process.py create mode 100644 personas/_shared/skills/analyzing-certificate-transparency-for-phishing/LICENSE create mode 100644 personas/_shared/skills/analyzing-certificate-transparency-for-phishing/SKILL.md create mode 100644 personas/_shared/skills/analyzing-certificate-transparency-for-phishing/references/api-reference.md create mode 100644 personas/_shared/skills/analyzing-certificate-transparency-for-phishing/scripts/agent.py create mode 100644 personas/_shared/skills/analyzing-cloud-storage-access-patterns/LICENSE create mode 100644 personas/_shared/skills/analyzing-cloud-storage-access-patterns/SKILL.md create mode 100644 personas/_shared/skills/analyzing-cloud-storage-access-patterns/references/api-reference.md create mode 100644 personas/_shared/skills/analyzing-cloud-storage-access-patterns/scripts/agent.py create mode 100644 personas/_shared/skills/analyzing-cobalt-strike-beacon-configuration/LICENSE create mode 100644 personas/_shared/skills/analyzing-cobalt-strike-beacon-configuration/SKILL.md create mode 100644 personas/_shared/skills/analyzing-cobalt-strike-beacon-configuration/assets/template.md create mode 100644 personas/_shared/skills/analyzing-cobalt-strike-beacon-configuration/references/api-reference.md create mode 100644 personas/_shared/skills/analyzing-cobalt-strike-beacon-configuration/references/standards.md create mode 100644 personas/_shared/skills/analyzing-cobalt-strike-beacon-configuration/references/workflows.md create mode 100644 personas/_shared/skills/analyzing-cobalt-strike-beacon-configuration/scripts/agent.py create mode 100644 personas/_shared/skills/analyzing-cobalt-strike-beacon-configuration/scripts/process.py create mode 100644 personas/_shared/skills/analyzing-cobaltstrike-malleable-c2-profiles/LICENSE create mode 100644 personas/_shared/skills/analyzing-cobaltstrike-malleable-c2-profiles/SKILL.md create mode 100644 personas/_shared/skills/analyzing-cobaltstrike-malleable-c2-profiles/references/api-reference.md create mode 100644 personas/_shared/skills/analyzing-cobaltstrike-malleable-c2-profiles/scripts/agent.py create mode 100644 personas/_shared/skills/analyzing-command-and-control-communication/LICENSE create mode 100644 personas/_shared/skills/analyzing-command-and-control-communication/SKILL.md create mode 100644 personas/_shared/skills/analyzing-command-and-control-communication/references/api-reference.md create mode 100644 personas/_shared/skills/analyzing-command-and-control-communication/scripts/agent.py create mode 100644 personas/_shared/skills/analyzing-cyber-kill-chain/LICENSE create mode 100644 personas/_shared/skills/analyzing-cyber-kill-chain/SKILL.md create mode 100644 personas/_shared/skills/analyzing-cyber-kill-chain/references/api-reference.md create mode 100644 personas/_shared/skills/analyzing-cyber-kill-chain/scripts/agent.py create mode 100644 personas/_shared/skills/analyzing-disk-image-with-autopsy/LICENSE create mode 100644 personas/_shared/skills/analyzing-disk-image-with-autopsy/SKILL.md create mode 100644 personas/_shared/skills/analyzing-disk-image-with-autopsy/references/api-reference.md create mode 100644 personas/_shared/skills/analyzing-disk-image-with-autopsy/scripts/agent.py create mode 100644 personas/_shared/skills/analyzing-dns-logs-for-exfiltration/LICENSE create mode 100644 personas/_shared/skills/analyzing-dns-logs-for-exfiltration/SKILL.md create mode 100644 personas/_shared/skills/analyzing-dns-logs-for-exfiltration/references/api-reference.md create mode 100644 personas/_shared/skills/analyzing-dns-logs-for-exfiltration/scripts/agent.py create mode 100644 personas/_shared/skills/analyzing-docker-container-forensics/LICENSE create mode 100644 personas/_shared/skills/analyzing-docker-container-forensics/SKILL.md create mode 100644 personas/_shared/skills/analyzing-docker-container-forensics/references/api-reference.md create mode 100644 personas/_shared/skills/analyzing-docker-container-forensics/scripts/agent.py create mode 100644 personas/_shared/skills/analyzing-email-headers-for-phishing-investigation/LICENSE create mode 100644 personas/_shared/skills/analyzing-email-headers-for-phishing-investigation/SKILL.md create mode 100644 personas/_shared/skills/analyzing-email-headers-for-phishing-investigation/references/api-reference.md create mode 100644 personas/_shared/skills/analyzing-email-headers-for-phishing-investigation/scripts/agent.py create mode 100644 personas/_shared/skills/analyzing-ethereum-smart-contract-vulnerabilities/LICENSE create mode 100644 personas/_shared/skills/analyzing-ethereum-smart-contract-vulnerabilities/SKILL.md create mode 100644 personas/_shared/skills/analyzing-ethereum-smart-contract-vulnerabilities/references/api-reference.md create mode 100644 personas/_shared/skills/analyzing-ethereum-smart-contract-vulnerabilities/scripts/agent.py create mode 100644 personas/_shared/skills/analyzing-golang-malware-with-ghidra/LICENSE create mode 100644 personas/_shared/skills/analyzing-golang-malware-with-ghidra/SKILL.md create mode 100644 personas/_shared/skills/analyzing-golang-malware-with-ghidra/assets/template.md create mode 100644 personas/_shared/skills/analyzing-golang-malware-with-ghidra/references/api-reference.md create mode 100644 personas/_shared/skills/analyzing-golang-malware-with-ghidra/references/standards.md create mode 100644 personas/_shared/skills/analyzing-golang-malware-with-ghidra/references/workflows.md create mode 100644 personas/_shared/skills/analyzing-golang-malware-with-ghidra/scripts/agent.py create mode 100644 personas/_shared/skills/analyzing-golang-malware-with-ghidra/scripts/process.py create mode 100644 personas/_shared/skills/analyzing-heap-spray-exploitation/LICENSE create mode 100644 personas/_shared/skills/analyzing-heap-spray-exploitation/SKILL.md create mode 100644 personas/_shared/skills/analyzing-heap-spray-exploitation/references/api-reference.md create mode 100644 personas/_shared/skills/analyzing-heap-spray-exploitation/scripts/agent.py create mode 100644 personas/_shared/skills/analyzing-indicators-of-compromise/LICENSE create mode 100644 personas/_shared/skills/analyzing-indicators-of-compromise/SKILL.md create mode 100644 personas/_shared/skills/analyzing-indicators-of-compromise/references/api-reference.md create mode 100644 personas/_shared/skills/analyzing-indicators-of-compromise/scripts/agent.py create mode 100644 personas/_shared/skills/analyzing-ios-app-security-with-objection/LICENSE create mode 100644 personas/_shared/skills/analyzing-ios-app-security-with-objection/SKILL.md create mode 100644 personas/_shared/skills/analyzing-ios-app-security-with-objection/assets/template.md create mode 100644 personas/_shared/skills/analyzing-ios-app-security-with-objection/references/api-reference.md create mode 100644 personas/_shared/skills/analyzing-ios-app-security-with-objection/references/standards.md create mode 100644 personas/_shared/skills/analyzing-ios-app-security-with-objection/references/workflows.md create mode 100644 personas/_shared/skills/analyzing-ios-app-security-with-objection/scripts/agent.py create mode 100644 personas/_shared/skills/analyzing-ios-app-security-with-objection/scripts/process.py create mode 100644 personas/_shared/skills/analyzing-kubernetes-audit-logs/LICENSE create mode 100644 personas/_shared/skills/analyzing-kubernetes-audit-logs/SKILL.md create mode 100644 personas/_shared/skills/analyzing-kubernetes-audit-logs/references/api-reference.md create mode 100644 personas/_shared/skills/analyzing-kubernetes-audit-logs/scripts/agent.py create mode 100644 personas/_shared/skills/analyzing-linux-audit-logs-for-intrusion/LICENSE create mode 100644 personas/_shared/skills/analyzing-linux-audit-logs-for-intrusion/SKILL.md create mode 100644 personas/_shared/skills/analyzing-linux-audit-logs-for-intrusion/references/api-reference.md create mode 100644 personas/_shared/skills/analyzing-linux-audit-logs-for-intrusion/scripts/agent.py create mode 100644 personas/_shared/skills/analyzing-linux-elf-malware/LICENSE create mode 100644 personas/_shared/skills/analyzing-linux-elf-malware/SKILL.md create mode 100644 personas/_shared/skills/analyzing-linux-elf-malware/references/api-reference.md create mode 100644 personas/_shared/skills/analyzing-linux-elf-malware/scripts/agent.py create mode 100644 personas/_shared/skills/analyzing-linux-kernel-rootkits/LICENSE create mode 100644 personas/_shared/skills/analyzing-linux-kernel-rootkits/SKILL.md create mode 100644 personas/_shared/skills/analyzing-linux-kernel-rootkits/references/api-reference.md create mode 100644 personas/_shared/skills/analyzing-linux-kernel-rootkits/scripts/agent.py create mode 100644 personas/_shared/skills/analyzing-linux-system-artifacts/LICENSE create mode 100644 personas/_shared/skills/analyzing-linux-system-artifacts/SKILL.md create mode 100644 personas/_shared/skills/analyzing-linux-system-artifacts/references/api-reference.md create mode 100644 personas/_shared/skills/analyzing-linux-system-artifacts/scripts/agent.py create mode 100644 personas/_shared/skills/analyzing-lnk-file-and-jump-list-artifacts/LICENSE create mode 100644 personas/_shared/skills/analyzing-lnk-file-and-jump-list-artifacts/SKILL.md create mode 100644 personas/_shared/skills/analyzing-lnk-file-and-jump-list-artifacts/assets/template.md create mode 100644 personas/_shared/skills/analyzing-lnk-file-and-jump-list-artifacts/references/api-reference.md create mode 100644 personas/_shared/skills/analyzing-lnk-file-and-jump-list-artifacts/references/standards.md create mode 100644 personas/_shared/skills/analyzing-lnk-file-and-jump-list-artifacts/references/workflows.md create mode 100644 personas/_shared/skills/analyzing-lnk-file-and-jump-list-artifacts/scripts/agent.py create mode 100644 personas/_shared/skills/analyzing-lnk-file-and-jump-list-artifacts/scripts/process.py create mode 100644 personas/_shared/skills/analyzing-macro-malware-in-office-documents/LICENSE create mode 100644 personas/_shared/skills/analyzing-macro-malware-in-office-documents/SKILL.md create mode 100644 personas/_shared/skills/analyzing-macro-malware-in-office-documents/references/api-reference.md create mode 100644 personas/_shared/skills/analyzing-macro-malware-in-office-documents/scripts/agent.py create mode 100644 personas/_shared/skills/analyzing-malicious-pdf-with-peepdf/LICENSE create mode 100644 personas/_shared/skills/analyzing-malicious-pdf-with-peepdf/SKILL.md create mode 100644 personas/_shared/skills/analyzing-malicious-pdf-with-peepdf/references/api-reference.md create mode 100644 personas/_shared/skills/analyzing-malicious-pdf-with-peepdf/scripts/agent.py create mode 100644 personas/_shared/skills/analyzing-malicious-url-with-urlscan/LICENSE create mode 100644 personas/_shared/skills/analyzing-malicious-url-with-urlscan/SKILL.md create mode 100644 personas/_shared/skills/analyzing-malicious-url-with-urlscan/assets/template.md create mode 100644 personas/_shared/skills/analyzing-malicious-url-with-urlscan/references/api-reference.md create mode 100644 personas/_shared/skills/analyzing-malicious-url-with-urlscan/references/standards.md create mode 100644 personas/_shared/skills/analyzing-malicious-url-with-urlscan/references/workflows.md create mode 100644 personas/_shared/skills/analyzing-malicious-url-with-urlscan/scripts/agent.py create mode 100644 personas/_shared/skills/analyzing-malicious-url-with-urlscan/scripts/process.py create mode 100644 personas/_shared/skills/analyzing-malware-behavior-with-cuckoo-sandbox/LICENSE create mode 100644 personas/_shared/skills/analyzing-malware-behavior-with-cuckoo-sandbox/SKILL.md create mode 100644 personas/_shared/skills/analyzing-malware-behavior-with-cuckoo-sandbox/references/api-reference.md create mode 100644 personas/_shared/skills/analyzing-malware-behavior-with-cuckoo-sandbox/scripts/agent.py create mode 100644 personas/_shared/skills/analyzing-malware-family-relationships-with-malpedia/LICENSE create mode 100644 personas/_shared/skills/analyzing-malware-family-relationships-with-malpedia/SKILL.md create mode 100644 personas/_shared/skills/analyzing-malware-family-relationships-with-malpedia/references/api-reference.md create mode 100644 personas/_shared/skills/analyzing-malware-family-relationships-with-malpedia/scripts/agent.py create mode 100644 personas/_shared/skills/analyzing-malware-persistence-with-autoruns/LICENSE create mode 100644 personas/_shared/skills/analyzing-malware-persistence-with-autoruns/SKILL.md create mode 100644 personas/_shared/skills/analyzing-malware-persistence-with-autoruns/assets/template.md create mode 100644 personas/_shared/skills/analyzing-malware-persistence-with-autoruns/references/api-reference.md create mode 100644 personas/_shared/skills/analyzing-malware-persistence-with-autoruns/references/standards.md create mode 100644 personas/_shared/skills/analyzing-malware-persistence-with-autoruns/references/workflows.md create mode 100644 personas/_shared/skills/analyzing-malware-persistence-with-autoruns/scripts/agent.py create mode 100644 personas/_shared/skills/analyzing-malware-sandbox-evasion-techniques/LICENSE create mode 100644 personas/_shared/skills/analyzing-malware-sandbox-evasion-techniques/SKILL.md create mode 100644 personas/_shared/skills/analyzing-malware-sandbox-evasion-techniques/references/api-reference.md create mode 100644 personas/_shared/skills/analyzing-malware-sandbox-evasion-techniques/scripts/agent.py create mode 100644 personas/_shared/skills/analyzing-memory-dumps-with-volatility/LICENSE create mode 100644 personas/_shared/skills/analyzing-memory-dumps-with-volatility/SKILL.md create mode 100644 personas/_shared/skills/analyzing-memory-dumps-with-volatility/references/api-reference.md create mode 100644 personas/_shared/skills/analyzing-memory-dumps-with-volatility/scripts/agent.py create mode 100644 personas/_shared/skills/analyzing-memory-forensics-with-lime-and-volatility/LICENSE create mode 100644 personas/_shared/skills/analyzing-memory-forensics-with-lime-and-volatility/SKILL.md create mode 100644 personas/_shared/skills/analyzing-memory-forensics-with-lime-and-volatility/references/api-reference.md create mode 100644 personas/_shared/skills/analyzing-memory-forensics-with-lime-and-volatility/scripts/agent.py create mode 100644 personas/_shared/skills/analyzing-mft-for-deleted-file-recovery/LICENSE create mode 100644 personas/_shared/skills/analyzing-mft-for-deleted-file-recovery/SKILL.md create mode 100644 personas/_shared/skills/analyzing-mft-for-deleted-file-recovery/assets/template.md create mode 100644 personas/_shared/skills/analyzing-mft-for-deleted-file-recovery/references/api-reference.md create mode 100644 personas/_shared/skills/analyzing-mft-for-deleted-file-recovery/references/standards.md create mode 100644 personas/_shared/skills/analyzing-mft-for-deleted-file-recovery/references/workflows.md create mode 100644 personas/_shared/skills/analyzing-mft-for-deleted-file-recovery/scripts/agent.py create mode 100644 personas/_shared/skills/analyzing-mft-for-deleted-file-recovery/scripts/process.py create mode 100644 personas/_shared/skills/analyzing-network-covert-channels-in-malware/LICENSE create mode 100644 personas/_shared/skills/analyzing-network-covert-channels-in-malware/SKILL.md create mode 100644 personas/_shared/skills/analyzing-network-covert-channels-in-malware/assets/template.md create mode 100644 personas/_shared/skills/analyzing-network-covert-channels-in-malware/references/api-reference.md create mode 100644 personas/_shared/skills/analyzing-network-covert-channels-in-malware/references/standards.md create mode 100644 personas/_shared/skills/analyzing-network-covert-channels-in-malware/references/workflows.md create mode 100644 personas/_shared/skills/analyzing-network-covert-channels-in-malware/scripts/agent.py create mode 100644 personas/_shared/skills/analyzing-network-flow-data-with-netflow/LICENSE create mode 100644 personas/_shared/skills/analyzing-network-flow-data-with-netflow/SKILL.md create mode 100644 personas/_shared/skills/analyzing-network-flow-data-with-netflow/references/api-reference.md create mode 100644 personas/_shared/skills/analyzing-network-flow-data-with-netflow/scripts/agent.py create mode 100644 personas/_shared/skills/analyzing-network-packets-with-scapy/LICENSE create mode 100644 personas/_shared/skills/analyzing-network-packets-with-scapy/SKILL.md create mode 100644 personas/_shared/skills/analyzing-network-packets-with-scapy/references/api-reference.md create mode 100644 personas/_shared/skills/analyzing-network-packets-with-scapy/scripts/agent.py create mode 100644 personas/_shared/skills/analyzing-network-traffic-for-incidents/LICENSE create mode 100644 personas/_shared/skills/analyzing-network-traffic-for-incidents/SKILL.md create mode 100644 personas/_shared/skills/analyzing-network-traffic-for-incidents/references/api-reference.md create mode 100644 personas/_shared/skills/analyzing-network-traffic-for-incidents/scripts/agent.py create mode 100644 personas/_shared/skills/analyzing-network-traffic-of-malware/LICENSE create mode 100644 personas/_shared/skills/analyzing-network-traffic-of-malware/SKILL.md create mode 100644 personas/_shared/skills/analyzing-network-traffic-of-malware/references/api-reference.md create mode 100644 personas/_shared/skills/analyzing-network-traffic-of-malware/scripts/agent.py create mode 100644 personas/_shared/skills/analyzing-network-traffic-with-wireshark/LICENSE create mode 100644 personas/_shared/skills/analyzing-network-traffic-with-wireshark/SKILL.md create mode 100644 personas/_shared/skills/analyzing-network-traffic-with-wireshark/references/api-reference.md create mode 100644 personas/_shared/skills/analyzing-network-traffic-with-wireshark/scripts/agent.py create mode 100644 personas/_shared/skills/analyzing-office365-audit-logs-for-compromise/LICENSE create mode 100644 personas/_shared/skills/analyzing-office365-audit-logs-for-compromise/SKILL.md create mode 100644 personas/_shared/skills/analyzing-office365-audit-logs-for-compromise/references/api-reference.md create mode 100644 personas/_shared/skills/analyzing-office365-audit-logs-for-compromise/scripts/agent.py create mode 100644 personas/_shared/skills/analyzing-outlook-pst-for-email-forensics/LICENSE create mode 100644 personas/_shared/skills/analyzing-outlook-pst-for-email-forensics/SKILL.md create mode 100644 personas/_shared/skills/analyzing-outlook-pst-for-email-forensics/references/api-reference.md create mode 100644 personas/_shared/skills/analyzing-outlook-pst-for-email-forensics/references/standards.md create mode 100644 personas/_shared/skills/analyzing-outlook-pst-for-email-forensics/references/workflows.md create mode 100644 personas/_shared/skills/analyzing-outlook-pst-for-email-forensics/scripts/agent.py create mode 100644 personas/_shared/skills/analyzing-packed-malware-with-upx-unpacker/LICENSE create mode 100644 personas/_shared/skills/analyzing-packed-malware-with-upx-unpacker/SKILL.md create mode 100644 personas/_shared/skills/analyzing-packed-malware-with-upx-unpacker/references/api-reference.md create mode 100644 personas/_shared/skills/analyzing-packed-malware-with-upx-unpacker/scripts/agent.py create mode 100644 personas/_shared/skills/analyzing-pdf-malware-with-pdfid/LICENSE create mode 100644 personas/_shared/skills/analyzing-pdf-malware-with-pdfid/SKILL.md create mode 100644 personas/_shared/skills/analyzing-pdf-malware-with-pdfid/references/api-reference.md create mode 100644 personas/_shared/skills/analyzing-pdf-malware-with-pdfid/scripts/agent.py create mode 100644 personas/_shared/skills/analyzing-persistence-mechanisms-in-linux/LICENSE create mode 100644 personas/_shared/skills/analyzing-persistence-mechanisms-in-linux/SKILL.md create mode 100644 personas/_shared/skills/analyzing-persistence-mechanisms-in-linux/references/api-reference.md create mode 100644 personas/_shared/skills/analyzing-persistence-mechanisms-in-linux/scripts/agent.py create mode 100644 personas/_shared/skills/analyzing-powershell-empire-artifacts/LICENSE create mode 100644 personas/_shared/skills/analyzing-powershell-empire-artifacts/SKILL.md create mode 100644 personas/_shared/skills/analyzing-powershell-empire-artifacts/references/api-reference.md create mode 100644 personas/_shared/skills/analyzing-powershell-empire-artifacts/scripts/agent.py create mode 100644 personas/_shared/skills/analyzing-powershell-script-block-logging/LICENSE create mode 100644 personas/_shared/skills/analyzing-powershell-script-block-logging/SKILL.md create mode 100644 personas/_shared/skills/analyzing-powershell-script-block-logging/references/api-reference.md create mode 100644 personas/_shared/skills/analyzing-powershell-script-block-logging/scripts/agent.py create mode 100644 personas/_shared/skills/analyzing-prefetch-files-for-execution-history/LICENSE create mode 100644 personas/_shared/skills/analyzing-prefetch-files-for-execution-history/SKILL.md create mode 100644 personas/_shared/skills/analyzing-prefetch-files-for-execution-history/references/api-reference.md create mode 100644 personas/_shared/skills/analyzing-prefetch-files-for-execution-history/scripts/agent.py create mode 100644 personas/_shared/skills/analyzing-ransomware-encryption-mechanisms/LICENSE create mode 100644 personas/_shared/skills/analyzing-ransomware-encryption-mechanisms/SKILL.md create mode 100644 personas/_shared/skills/analyzing-ransomware-encryption-mechanisms/references/api-reference.md create mode 100644 personas/_shared/skills/analyzing-ransomware-encryption-mechanisms/scripts/agent.py create mode 100644 personas/_shared/skills/analyzing-ransomware-leak-site-intelligence/LICENSE create mode 100644 personas/_shared/skills/analyzing-ransomware-leak-site-intelligence/SKILL.md create mode 100644 personas/_shared/skills/analyzing-ransomware-leak-site-intelligence/references/api-reference.md create mode 100644 personas/_shared/skills/analyzing-ransomware-leak-site-intelligence/scripts/agent.py create mode 100644 personas/_shared/skills/analyzing-ransomware-network-indicators/LICENSE create mode 100644 personas/_shared/skills/analyzing-ransomware-network-indicators/SKILL.md create mode 100644 personas/_shared/skills/analyzing-ransomware-network-indicators/references/api-reference.md create mode 100644 personas/_shared/skills/analyzing-ransomware-network-indicators/scripts/agent.py create mode 100644 personas/_shared/skills/analyzing-ransomware-payment-wallets/LICENSE create mode 100644 personas/_shared/skills/analyzing-ransomware-payment-wallets/SKILL.md create mode 100644 personas/_shared/skills/analyzing-ransomware-payment-wallets/references/api-reference.md create mode 100644 personas/_shared/skills/analyzing-ransomware-payment-wallets/scripts/agent.py create mode 100644 personas/_shared/skills/analyzing-sbom-for-supply-chain-vulnerabilities/LICENSE create mode 100644 personas/_shared/skills/analyzing-sbom-for-supply-chain-vulnerabilities/SKILL.md create mode 100644 personas/_shared/skills/analyzing-sbom-for-supply-chain-vulnerabilities/references/api-reference.md create mode 100644 personas/_shared/skills/analyzing-sbom-for-supply-chain-vulnerabilities/scripts/agent.py create mode 100644 personas/_shared/skills/analyzing-security-logs-with-splunk/LICENSE create mode 100644 personas/_shared/skills/analyzing-security-logs-with-splunk/SKILL.md create mode 100644 personas/_shared/skills/analyzing-security-logs-with-splunk/references/api-reference.md create mode 100644 personas/_shared/skills/analyzing-security-logs-with-splunk/scripts/agent.py create mode 100644 personas/_shared/skills/analyzing-slack-space-and-file-system-artifacts/LICENSE create mode 100644 personas/_shared/skills/analyzing-slack-space-and-file-system-artifacts/SKILL.md create mode 100644 personas/_shared/skills/analyzing-slack-space-and-file-system-artifacts/references/api-reference.md create mode 100644 personas/_shared/skills/analyzing-slack-space-and-file-system-artifacts/scripts/agent.py create mode 100644 personas/_shared/skills/analyzing-supply-chain-malware-artifacts/LICENSE create mode 100644 personas/_shared/skills/analyzing-supply-chain-malware-artifacts/SKILL.md create mode 100644 personas/_shared/skills/analyzing-supply-chain-malware-artifacts/assets/template.md create mode 100644 personas/_shared/skills/analyzing-supply-chain-malware-artifacts/references/api-reference.md create mode 100644 personas/_shared/skills/analyzing-supply-chain-malware-artifacts/references/standards.md create mode 100644 personas/_shared/skills/analyzing-supply-chain-malware-artifacts/references/workflows.md create mode 100644 personas/_shared/skills/analyzing-supply-chain-malware-artifacts/scripts/agent.py create mode 100644 personas/_shared/skills/analyzing-threat-actor-ttps-with-mitre-attack/LICENSE create mode 100644 personas/_shared/skills/analyzing-threat-actor-ttps-with-mitre-attack/SKILL.md create mode 100644 personas/_shared/skills/analyzing-threat-actor-ttps-with-mitre-attack/assets/template.md create mode 100644 personas/_shared/skills/analyzing-threat-actor-ttps-with-mitre-attack/references/api-reference.md create mode 100644 personas/_shared/skills/analyzing-threat-actor-ttps-with-mitre-attack/references/standards.md create mode 100644 personas/_shared/skills/analyzing-threat-actor-ttps-with-mitre-attack/references/workflows.md create mode 100644 personas/_shared/skills/analyzing-threat-actor-ttps-with-mitre-attack/scripts/agent.py create mode 100644 personas/_shared/skills/analyzing-threat-actor-ttps-with-mitre-attack/scripts/process.py create mode 100644 personas/_shared/skills/analyzing-threat-actor-ttps-with-mitre-navigator/LICENSE create mode 100644 personas/_shared/skills/analyzing-threat-actor-ttps-with-mitre-navigator/SKILL.md create mode 100644 personas/_shared/skills/analyzing-threat-actor-ttps-with-mitre-navigator/references/api-reference.md create mode 100644 personas/_shared/skills/analyzing-threat-actor-ttps-with-mitre-navigator/scripts/agent.py create mode 100644 personas/_shared/skills/analyzing-threat-intelligence-feeds/LICENSE create mode 100644 personas/_shared/skills/analyzing-threat-intelligence-feeds/SKILL.md create mode 100644 personas/_shared/skills/analyzing-threat-intelligence-feeds/references/api-reference.md create mode 100644 personas/_shared/skills/analyzing-threat-intelligence-feeds/scripts/agent.py create mode 100644 personas/_shared/skills/analyzing-threat-landscape-with-misp/LICENSE create mode 100644 personas/_shared/skills/analyzing-threat-landscape-with-misp/SKILL.md create mode 100644 personas/_shared/skills/analyzing-threat-landscape-with-misp/references/api-reference.md create mode 100644 personas/_shared/skills/analyzing-threat-landscape-with-misp/scripts/agent.py create mode 100644 personas/_shared/skills/analyzing-tls-certificate-transparency-logs/LICENSE create mode 100644 personas/_shared/skills/analyzing-tls-certificate-transparency-logs/SKILL.md create mode 100644 personas/_shared/skills/analyzing-tls-certificate-transparency-logs/references/api-reference.md create mode 100644 personas/_shared/skills/analyzing-tls-certificate-transparency-logs/scripts/agent.py create mode 100644 personas/_shared/skills/analyzing-typosquatting-domains-with-dnstwist/LICENSE create mode 100644 personas/_shared/skills/analyzing-typosquatting-domains-with-dnstwist/SKILL.md create mode 100644 personas/_shared/skills/analyzing-typosquatting-domains-with-dnstwist/references/api-reference.md create mode 100644 personas/_shared/skills/analyzing-typosquatting-domains-with-dnstwist/scripts/agent.py create mode 100644 personas/_shared/skills/analyzing-uefi-bootkit-persistence/LICENSE create mode 100644 personas/_shared/skills/analyzing-uefi-bootkit-persistence/SKILL.md create mode 100644 personas/_shared/skills/analyzing-uefi-bootkit-persistence/references/api-reference.md create mode 100644 personas/_shared/skills/analyzing-uefi-bootkit-persistence/scripts/agent.py create mode 100644 personas/_shared/skills/analyzing-usb-device-connection-history/LICENSE create mode 100644 personas/_shared/skills/analyzing-usb-device-connection-history/SKILL.md create mode 100644 personas/_shared/skills/analyzing-usb-device-connection-history/references/api-reference.md create mode 100644 personas/_shared/skills/analyzing-usb-device-connection-history/scripts/agent.py create mode 100644 personas/_shared/skills/analyzing-web-server-logs-for-intrusion/LICENSE create mode 100644 personas/_shared/skills/analyzing-web-server-logs-for-intrusion/SKILL.md create mode 100644 personas/_shared/skills/analyzing-web-server-logs-for-intrusion/references/api-reference.md create mode 100644 personas/_shared/skills/analyzing-web-server-logs-for-intrusion/scripts/agent.py create mode 100644 personas/_shared/skills/analyzing-windows-amcache-artifacts/LICENSE create mode 100644 personas/_shared/skills/analyzing-windows-amcache-artifacts/SKILL.md create mode 100644 personas/_shared/skills/analyzing-windows-amcache-artifacts/references/api-reference.md create mode 100644 personas/_shared/skills/analyzing-windows-amcache-artifacts/scripts/agent.py create mode 100644 personas/_shared/skills/analyzing-windows-event-logs-in-splunk/LICENSE create mode 100644 personas/_shared/skills/analyzing-windows-event-logs-in-splunk/SKILL.md create mode 100644 personas/_shared/skills/analyzing-windows-event-logs-in-splunk/references/api-reference.md create mode 100644 personas/_shared/skills/analyzing-windows-event-logs-in-splunk/scripts/agent.py create mode 100644 personas/_shared/skills/analyzing-windows-lnk-files-for-artifacts/LICENSE create mode 100644 personas/_shared/skills/analyzing-windows-lnk-files-for-artifacts/SKILL.md create mode 100644 personas/_shared/skills/analyzing-windows-lnk-files-for-artifacts/references/api-reference.md create mode 100644 personas/_shared/skills/analyzing-windows-lnk-files-for-artifacts/scripts/agent.py create mode 100644 personas/_shared/skills/analyzing-windows-prefetch-with-python/LICENSE create mode 100644 personas/_shared/skills/analyzing-windows-prefetch-with-python/SKILL.md create mode 100644 personas/_shared/skills/analyzing-windows-prefetch-with-python/references/api-reference.md create mode 100644 personas/_shared/skills/analyzing-windows-prefetch-with-python/scripts/agent.py create mode 100644 personas/_shared/skills/analyzing-windows-registry-for-artifacts/LICENSE create mode 100644 personas/_shared/skills/analyzing-windows-registry-for-artifacts/SKILL.md create mode 100644 personas/_shared/skills/analyzing-windows-registry-for-artifacts/references/api-reference.md create mode 100644 personas/_shared/skills/analyzing-windows-registry-for-artifacts/scripts/agent.py create mode 100644 personas/_shared/skills/analyzing-windows-shellbag-artifacts/LICENSE create mode 100644 personas/_shared/skills/analyzing-windows-shellbag-artifacts/SKILL.md create mode 100644 personas/_shared/skills/analyzing-windows-shellbag-artifacts/assets/template.md create mode 100644 personas/_shared/skills/analyzing-windows-shellbag-artifacts/references/api-reference.md create mode 100644 personas/_shared/skills/analyzing-windows-shellbag-artifacts/references/standards.md create mode 100644 personas/_shared/skills/analyzing-windows-shellbag-artifacts/references/workflows.md create mode 100644 personas/_shared/skills/analyzing-windows-shellbag-artifacts/scripts/agent.py create mode 100644 personas/_shared/skills/analyzing-windows-shellbag-artifacts/scripts/process.py create mode 100644 personas/_shared/skills/auditing-aws-s3-bucket-permissions/LICENSE create mode 100644 personas/_shared/skills/auditing-aws-s3-bucket-permissions/SKILL.md create mode 100644 personas/_shared/skills/auditing-aws-s3-bucket-permissions/references/api-reference.md create mode 100644 personas/_shared/skills/auditing-aws-s3-bucket-permissions/scripts/agent.py create mode 100644 personas/_shared/skills/auditing-azure-active-directory-configuration/LICENSE create mode 100644 personas/_shared/skills/auditing-azure-active-directory-configuration/SKILL.md create mode 100644 personas/_shared/skills/auditing-azure-active-directory-configuration/references/api-reference.md create mode 100644 personas/_shared/skills/auditing-azure-active-directory-configuration/scripts/agent.py create mode 100644 personas/_shared/skills/auditing-cloud-with-cis-benchmarks/LICENSE create mode 100644 personas/_shared/skills/auditing-cloud-with-cis-benchmarks/SKILL.md create mode 100644 personas/_shared/skills/auditing-cloud-with-cis-benchmarks/references/api-reference.md create mode 100644 personas/_shared/skills/auditing-cloud-with-cis-benchmarks/scripts/agent.py create mode 100644 personas/_shared/skills/auditing-gcp-iam-permissions/LICENSE create mode 100644 personas/_shared/skills/auditing-gcp-iam-permissions/SKILL.md create mode 100644 personas/_shared/skills/auditing-gcp-iam-permissions/references/api-reference.md create mode 100644 personas/_shared/skills/auditing-gcp-iam-permissions/scripts/agent.py create mode 100644 personas/_shared/skills/auditing-kubernetes-cluster-rbac/LICENSE create mode 100644 personas/_shared/skills/auditing-kubernetes-cluster-rbac/SKILL.md create mode 100644 personas/_shared/skills/auditing-kubernetes-cluster-rbac/references/api-reference.md create mode 100644 personas/_shared/skills/auditing-kubernetes-cluster-rbac/scripts/agent.py create mode 100644 personas/_shared/skills/auditing-terraform-infrastructure-for-security/LICENSE create mode 100644 personas/_shared/skills/auditing-terraform-infrastructure-for-security/SKILL.md create mode 100644 personas/_shared/skills/auditing-terraform-infrastructure-for-security/references/api-reference.md create mode 100644 personas/_shared/skills/auditing-terraform-infrastructure-for-security/scripts/agent.py create mode 100644 personas/_shared/skills/auditing-tls-certificate-transparency-logs/LICENSE create mode 100644 personas/_shared/skills/auditing-tls-certificate-transparency-logs/SKILL.md create mode 100644 personas/_shared/skills/auditing-tls-certificate-transparency-logs/references/api-reference.md create mode 100644 personas/_shared/skills/auditing-tls-certificate-transparency-logs/scripts/agent.py create mode 100644 personas/_shared/skills/automating-ioc-enrichment/LICENSE create mode 100644 personas/_shared/skills/automating-ioc-enrichment/SKILL.md create mode 100644 personas/_shared/skills/automating-ioc-enrichment/references/api-reference.md create mode 100644 personas/_shared/skills/automating-ioc-enrichment/scripts/agent.py create mode 100644 personas/_shared/skills/building-adversary-infrastructure-tracking-system/LICENSE create mode 100644 personas/_shared/skills/building-adversary-infrastructure-tracking-system/SKILL.md create mode 100644 personas/_shared/skills/building-adversary-infrastructure-tracking-system/references/api-reference.md create mode 100644 personas/_shared/skills/building-adversary-infrastructure-tracking-system/scripts/agent.py create mode 100644 personas/_shared/skills/building-attack-pattern-library-from-cti-reports/LICENSE create mode 100644 personas/_shared/skills/building-attack-pattern-library-from-cti-reports/SKILL.md create mode 100644 personas/_shared/skills/building-attack-pattern-library-from-cti-reports/references/api-reference.md create mode 100644 personas/_shared/skills/building-attack-pattern-library-from-cti-reports/scripts/agent.py create mode 100644 personas/_shared/skills/building-automated-malware-submission-pipeline/LICENSE create mode 100644 personas/_shared/skills/building-automated-malware-submission-pipeline/SKILL.md create mode 100644 personas/_shared/skills/building-automated-malware-submission-pipeline/references/api-reference.md create mode 100644 personas/_shared/skills/building-automated-malware-submission-pipeline/scripts/agent.py create mode 100644 personas/_shared/skills/building-c2-infrastructure-with-sliver-framework/LICENSE create mode 100644 personas/_shared/skills/building-c2-infrastructure-with-sliver-framework/SKILL.md create mode 100644 personas/_shared/skills/building-c2-infrastructure-with-sliver-framework/assets/template.md create mode 100644 personas/_shared/skills/building-c2-infrastructure-with-sliver-framework/references/api-reference.md create mode 100644 personas/_shared/skills/building-c2-infrastructure-with-sliver-framework/references/standards.md create mode 100644 personas/_shared/skills/building-c2-infrastructure-with-sliver-framework/references/workflows.md create mode 100644 personas/_shared/skills/building-c2-infrastructure-with-sliver-framework/scripts/agent.py create mode 100644 personas/_shared/skills/building-c2-infrastructure-with-sliver-framework/scripts/process.py create mode 100644 personas/_shared/skills/building-cloud-siem-with-sentinel/LICENSE create mode 100644 personas/_shared/skills/building-cloud-siem-with-sentinel/SKILL.md create mode 100644 personas/_shared/skills/building-cloud-siem-with-sentinel/references/api-reference.md create mode 100644 personas/_shared/skills/building-cloud-siem-with-sentinel/scripts/agent.py create mode 100644 personas/_shared/skills/building-detection-rule-with-splunk-spl/LICENSE create mode 100644 personas/_shared/skills/building-detection-rule-with-splunk-spl/SKILL.md create mode 100644 personas/_shared/skills/building-detection-rule-with-splunk-spl/assets/template.md create mode 100644 personas/_shared/skills/building-detection-rule-with-splunk-spl/references/api-reference.md create mode 100644 personas/_shared/skills/building-detection-rule-with-splunk-spl/references/standards.md create mode 100644 personas/_shared/skills/building-detection-rule-with-splunk-spl/references/workflows.md create mode 100644 personas/_shared/skills/building-detection-rule-with-splunk-spl/scripts/agent.py create mode 100644 personas/_shared/skills/building-detection-rule-with-splunk-spl/scripts/process.py create mode 100644 personas/_shared/skills/building-detection-rules-with-sigma/LICENSE create mode 100644 personas/_shared/skills/building-detection-rules-with-sigma/SKILL.md create mode 100644 personas/_shared/skills/building-detection-rules-with-sigma/references/api-reference.md create mode 100644 personas/_shared/skills/building-detection-rules-with-sigma/scripts/agent.py create mode 100644 personas/_shared/skills/building-devsecops-pipeline-with-gitlab-ci/LICENSE create mode 100644 personas/_shared/skills/building-devsecops-pipeline-with-gitlab-ci/SKILL.md create mode 100644 personas/_shared/skills/building-devsecops-pipeline-with-gitlab-ci/assets/template.md create mode 100644 personas/_shared/skills/building-devsecops-pipeline-with-gitlab-ci/references/api-reference.md create mode 100644 personas/_shared/skills/building-devsecops-pipeline-with-gitlab-ci/references/standards.md create mode 100644 personas/_shared/skills/building-devsecops-pipeline-with-gitlab-ci/references/workflows.md create mode 100644 personas/_shared/skills/building-devsecops-pipeline-with-gitlab-ci/scripts/agent.py create mode 100644 personas/_shared/skills/building-devsecops-pipeline-with-gitlab-ci/scripts/process.py create mode 100644 personas/_shared/skills/building-identity-federation-with-saml-azure-ad/LICENSE create mode 100644 personas/_shared/skills/building-identity-federation-with-saml-azure-ad/SKILL.md create mode 100644 personas/_shared/skills/building-identity-federation-with-saml-azure-ad/assets/template.md create mode 100644 personas/_shared/skills/building-identity-federation-with-saml-azure-ad/references/api-reference.md create mode 100644 personas/_shared/skills/building-identity-federation-with-saml-azure-ad/references/standards.md create mode 100644 personas/_shared/skills/building-identity-federation-with-saml-azure-ad/references/workflows.md create mode 100644 personas/_shared/skills/building-identity-federation-with-saml-azure-ad/scripts/agent.py create mode 100644 personas/_shared/skills/building-identity-federation-with-saml-azure-ad/scripts/process.py create mode 100644 personas/_shared/skills/building-identity-governance-lifecycle-process/LICENSE create mode 100644 personas/_shared/skills/building-identity-governance-lifecycle-process/SKILL.md create mode 100644 personas/_shared/skills/building-identity-governance-lifecycle-process/references/api-reference.md create mode 100644 personas/_shared/skills/building-identity-governance-lifecycle-process/scripts/agent.py create mode 100644 personas/_shared/skills/building-incident-response-dashboard/LICENSE create mode 100644 personas/_shared/skills/building-incident-response-dashboard/SKILL.es.md create mode 100644 personas/_shared/skills/building-incident-response-dashboard/SKILL.md create mode 100644 personas/_shared/skills/building-incident-response-dashboard/references/api-reference.md create mode 100644 personas/_shared/skills/building-incident-response-dashboard/scripts/agent.py create mode 100644 personas/_shared/skills/building-incident-response-playbook/LICENSE create mode 100644 personas/_shared/skills/building-incident-response-playbook/SKILL.md create mode 100644 personas/_shared/skills/building-incident-response-playbook/references/api-reference.md create mode 100644 personas/_shared/skills/building-incident-response-playbook/scripts/agent.py create mode 100644 personas/_shared/skills/building-incident-timeline-with-timesketch/LICENSE create mode 100644 personas/_shared/skills/building-incident-timeline-with-timesketch/SKILL.md create mode 100644 personas/_shared/skills/building-incident-timeline-with-timesketch/assets/template.md create mode 100644 personas/_shared/skills/building-incident-timeline-with-timesketch/references/api-reference.md create mode 100644 personas/_shared/skills/building-incident-timeline-with-timesketch/references/standards.md create mode 100644 personas/_shared/skills/building-incident-timeline-with-timesketch/references/workflows.md create mode 100644 personas/_shared/skills/building-incident-timeline-with-timesketch/scripts/agent.py create mode 100644 personas/_shared/skills/building-incident-timeline-with-timesketch/scripts/process.py create mode 100644 personas/_shared/skills/building-ioc-defanging-and-sharing-pipeline/LICENSE create mode 100644 personas/_shared/skills/building-ioc-defanging-and-sharing-pipeline/SKILL.md create mode 100644 personas/_shared/skills/building-ioc-defanging-and-sharing-pipeline/references/api-reference.md create mode 100644 personas/_shared/skills/building-ioc-defanging-and-sharing-pipeline/scripts/agent.py create mode 100644 personas/_shared/skills/building-ioc-enrichment-pipeline-with-opencti/LICENSE create mode 100644 personas/_shared/skills/building-ioc-enrichment-pipeline-with-opencti/SKILL.md create mode 100644 personas/_shared/skills/building-ioc-enrichment-pipeline-with-opencti/assets/template.md create mode 100644 personas/_shared/skills/building-ioc-enrichment-pipeline-with-opencti/references/api-reference.md create mode 100644 personas/_shared/skills/building-ioc-enrichment-pipeline-with-opencti/references/standards.md create mode 100644 personas/_shared/skills/building-ioc-enrichment-pipeline-with-opencti/references/workflows.md create mode 100644 personas/_shared/skills/building-ioc-enrichment-pipeline-with-opencti/scripts/agent.py create mode 100644 personas/_shared/skills/building-ioc-enrichment-pipeline-with-opencti/scripts/process.py create mode 100644 personas/_shared/skills/building-malware-incident-communication-template/LICENSE create mode 100644 personas/_shared/skills/building-malware-incident-communication-template/SKILL.md create mode 100644 personas/_shared/skills/building-malware-incident-communication-template/assets/template.md create mode 100644 personas/_shared/skills/building-malware-incident-communication-template/references/api-reference.md create mode 100644 personas/_shared/skills/building-malware-incident-communication-template/references/standards.md create mode 100644 personas/_shared/skills/building-malware-incident-communication-template/references/workflows.md create mode 100644 personas/_shared/skills/building-malware-incident-communication-template/scripts/agent.py create mode 100644 personas/_shared/skills/building-malware-incident-communication-template/scripts/process.py create mode 100644 personas/_shared/skills/building-patch-tuesday-response-process/LICENSE create mode 100644 personas/_shared/skills/building-patch-tuesday-response-process/SKILL.md create mode 100644 personas/_shared/skills/building-patch-tuesday-response-process/assets/template.md create mode 100644 personas/_shared/skills/building-patch-tuesday-response-process/references/api-reference.md create mode 100644 personas/_shared/skills/building-patch-tuesday-response-process/references/standards.md create mode 100644 personas/_shared/skills/building-patch-tuesday-response-process/references/workflows.md create mode 100644 personas/_shared/skills/building-patch-tuesday-response-process/scripts/agent.py create mode 100644 personas/_shared/skills/building-patch-tuesday-response-process/scripts/process.py create mode 100644 personas/_shared/skills/building-phishing-reporting-button-workflow/LICENSE create mode 100644 personas/_shared/skills/building-phishing-reporting-button-workflow/SKILL.md create mode 100644 personas/_shared/skills/building-phishing-reporting-button-workflow/assets/template.md create mode 100644 personas/_shared/skills/building-phishing-reporting-button-workflow/references/api-reference.md create mode 100644 personas/_shared/skills/building-phishing-reporting-button-workflow/references/standards.md create mode 100644 personas/_shared/skills/building-phishing-reporting-button-workflow/references/workflows.md create mode 100644 personas/_shared/skills/building-phishing-reporting-button-workflow/scripts/agent.py create mode 100644 personas/_shared/skills/building-phishing-reporting-button-workflow/scripts/process.py create mode 100644 personas/_shared/skills/building-ransomware-playbook-with-cisa-framework/LICENSE create mode 100644 personas/_shared/skills/building-ransomware-playbook-with-cisa-framework/SKILL.md create mode 100644 personas/_shared/skills/building-ransomware-playbook-with-cisa-framework/references/api-reference.md create mode 100644 personas/_shared/skills/building-ransomware-playbook-with-cisa-framework/scripts/agent.py create mode 100644 personas/_shared/skills/building-red-team-c2-infrastructure-with-havoc/LICENSE create mode 100644 personas/_shared/skills/building-red-team-c2-infrastructure-with-havoc/SKILL.md create mode 100644 personas/_shared/skills/building-red-team-c2-infrastructure-with-havoc/assets/template.md create mode 100644 personas/_shared/skills/building-red-team-c2-infrastructure-with-havoc/references/api-reference.md create mode 100644 personas/_shared/skills/building-red-team-c2-infrastructure-with-havoc/references/standards.md create mode 100644 personas/_shared/skills/building-red-team-c2-infrastructure-with-havoc/references/workflows.md create mode 100644 personas/_shared/skills/building-red-team-c2-infrastructure-with-havoc/scripts/agent.py create mode 100644 personas/_shared/skills/building-red-team-c2-infrastructure-with-havoc/scripts/process.py create mode 100644 personas/_shared/skills/building-role-mining-for-rbac-optimization/LICENSE create mode 100644 personas/_shared/skills/building-role-mining-for-rbac-optimization/SKILL.md create mode 100644 personas/_shared/skills/building-role-mining-for-rbac-optimization/assets/template.md create mode 100644 personas/_shared/skills/building-role-mining-for-rbac-optimization/references/api-reference.md create mode 100644 personas/_shared/skills/building-role-mining-for-rbac-optimization/references/standards.md create mode 100644 personas/_shared/skills/building-role-mining-for-rbac-optimization/references/workflows.md create mode 100644 personas/_shared/skills/building-role-mining-for-rbac-optimization/scripts/agent.py create mode 100644 personas/_shared/skills/building-role-mining-for-rbac-optimization/scripts/process.py create mode 100644 personas/_shared/skills/building-soc-escalation-matrix/LICENSE create mode 100644 personas/_shared/skills/building-soc-escalation-matrix/SKILL.md create mode 100644 personas/_shared/skills/building-soc-escalation-matrix/assets/template.md create mode 100644 personas/_shared/skills/building-soc-escalation-matrix/references/api-reference.md create mode 100644 personas/_shared/skills/building-soc-escalation-matrix/references/standards.md create mode 100644 personas/_shared/skills/building-soc-escalation-matrix/references/workflows.md create mode 100644 personas/_shared/skills/building-soc-escalation-matrix/scripts/agent.py create mode 100644 personas/_shared/skills/building-soc-escalation-matrix/scripts/process.py create mode 100644 personas/_shared/skills/building-soc-metrics-and-kpi-tracking/LICENSE create mode 100644 personas/_shared/skills/building-soc-metrics-and-kpi-tracking/SKILL.md create mode 100644 personas/_shared/skills/building-soc-metrics-and-kpi-tracking/references/api-reference.md create mode 100644 personas/_shared/skills/building-soc-metrics-and-kpi-tracking/scripts/agent.py create mode 100644 personas/_shared/skills/building-soc-playbook-for-ransomware/LICENSE create mode 100644 personas/_shared/skills/building-soc-playbook-for-ransomware/SKILL.md create mode 100644 personas/_shared/skills/building-soc-playbook-for-ransomware/references/api-reference.md create mode 100644 personas/_shared/skills/building-soc-playbook-for-ransomware/scripts/agent.py create mode 100644 personas/_shared/skills/building-threat-actor-profile-from-osint/LICENSE create mode 100644 personas/_shared/skills/building-threat-actor-profile-from-osint/SKILL.md create mode 100644 personas/_shared/skills/building-threat-actor-profile-from-osint/references/api-reference.md create mode 100644 personas/_shared/skills/building-threat-actor-profile-from-osint/scripts/agent.py create mode 100644 personas/_shared/skills/building-threat-feed-aggregation-with-misp/LICENSE create mode 100644 personas/_shared/skills/building-threat-feed-aggregation-with-misp/SKILL.md create mode 100644 personas/_shared/skills/building-threat-feed-aggregation-with-misp/references/api-reference.md create mode 100644 personas/_shared/skills/building-threat-feed-aggregation-with-misp/scripts/agent.py create mode 100644 personas/_shared/skills/building-threat-hunt-hypothesis-framework/LICENSE create mode 100644 personas/_shared/skills/building-threat-hunt-hypothesis-framework/SKILL.md create mode 100644 personas/_shared/skills/building-threat-hunt-hypothesis-framework/assets/template.md create mode 100644 personas/_shared/skills/building-threat-hunt-hypothesis-framework/references/api-reference.md create mode 100644 personas/_shared/skills/building-threat-hunt-hypothesis-framework/references/standards.md create mode 100644 personas/_shared/skills/building-threat-hunt-hypothesis-framework/references/workflows.md create mode 100644 personas/_shared/skills/building-threat-hunt-hypothesis-framework/scripts/agent.py create mode 100644 personas/_shared/skills/building-threat-hunt-hypothesis-framework/scripts/process.py create mode 100644 personas/_shared/skills/building-threat-intelligence-enrichment-in-splunk/LICENSE create mode 100644 personas/_shared/skills/building-threat-intelligence-enrichment-in-splunk/SKILL.md create mode 100644 personas/_shared/skills/building-threat-intelligence-enrichment-in-splunk/assets/template.md create mode 100644 personas/_shared/skills/building-threat-intelligence-enrichment-in-splunk/references/api-reference.md create mode 100644 personas/_shared/skills/building-threat-intelligence-enrichment-in-splunk/references/standards.md create mode 100644 personas/_shared/skills/building-threat-intelligence-enrichment-in-splunk/references/workflows.md create mode 100644 personas/_shared/skills/building-threat-intelligence-enrichment-in-splunk/scripts/agent.py create mode 100644 personas/_shared/skills/building-threat-intelligence-enrichment-in-splunk/scripts/process.py create mode 100644 personas/_shared/skills/building-threat-intelligence-feed-integration/LICENSE create mode 100644 personas/_shared/skills/building-threat-intelligence-feed-integration/SKILL.md create mode 100644 personas/_shared/skills/building-threat-intelligence-feed-integration/references/api-reference.md create mode 100644 personas/_shared/skills/building-threat-intelligence-feed-integration/scripts/agent.py create mode 100644 personas/_shared/skills/building-threat-intelligence-platform/LICENSE create mode 100644 personas/_shared/skills/building-threat-intelligence-platform/SKILL.md create mode 100644 personas/_shared/skills/building-threat-intelligence-platform/assets/template.md create mode 100644 personas/_shared/skills/building-threat-intelligence-platform/references/api-reference.md create mode 100644 personas/_shared/skills/building-threat-intelligence-platform/references/standards.md create mode 100644 personas/_shared/skills/building-threat-intelligence-platform/references/workflows.md create mode 100644 personas/_shared/skills/building-threat-intelligence-platform/scripts/agent.py create mode 100644 personas/_shared/skills/building-threat-intelligence-platform/scripts/process.py create mode 100644 personas/_shared/skills/building-vulnerability-aging-and-sla-tracking/LICENSE create mode 100644 personas/_shared/skills/building-vulnerability-aging-and-sla-tracking/SKILL.md create mode 100644 personas/_shared/skills/building-vulnerability-aging-and-sla-tracking/assets/template.md create mode 100644 personas/_shared/skills/building-vulnerability-aging-and-sla-tracking/references/api-reference.md create mode 100644 personas/_shared/skills/building-vulnerability-aging-and-sla-tracking/references/standards.md create mode 100644 personas/_shared/skills/building-vulnerability-aging-and-sla-tracking/references/workflows.md create mode 100644 personas/_shared/skills/building-vulnerability-aging-and-sla-tracking/scripts/agent.py create mode 100644 personas/_shared/skills/building-vulnerability-aging-and-sla-tracking/scripts/process.py create mode 100644 personas/_shared/skills/building-vulnerability-dashboard-with-defectdojo/LICENSE create mode 100644 personas/_shared/skills/building-vulnerability-dashboard-with-defectdojo/SKILL.md create mode 100644 personas/_shared/skills/building-vulnerability-dashboard-with-defectdojo/assets/template.md create mode 100644 personas/_shared/skills/building-vulnerability-dashboard-with-defectdojo/references/api-reference.md create mode 100644 personas/_shared/skills/building-vulnerability-dashboard-with-defectdojo/references/standards.md create mode 100644 personas/_shared/skills/building-vulnerability-dashboard-with-defectdojo/references/workflows.md create mode 100644 personas/_shared/skills/building-vulnerability-dashboard-with-defectdojo/scripts/agent.py create mode 100644 personas/_shared/skills/building-vulnerability-dashboard-with-defectdojo/scripts/process.py create mode 100644 personas/_shared/skills/building-vulnerability-exception-tracking-system/LICENSE create mode 100644 personas/_shared/skills/building-vulnerability-exception-tracking-system/SKILL.md create mode 100644 personas/_shared/skills/building-vulnerability-exception-tracking-system/assets/template.md create mode 100644 personas/_shared/skills/building-vulnerability-exception-tracking-system/references/api-reference.md create mode 100644 personas/_shared/skills/building-vulnerability-exception-tracking-system/references/standards.md create mode 100644 personas/_shared/skills/building-vulnerability-exception-tracking-system/references/workflows.md create mode 100644 personas/_shared/skills/building-vulnerability-exception-tracking-system/scripts/agent.py create mode 100644 personas/_shared/skills/building-vulnerability-exception-tracking-system/scripts/process.py create mode 100644 personas/_shared/skills/building-vulnerability-scanning-workflow/LICENSE create mode 100644 personas/_shared/skills/building-vulnerability-scanning-workflow/SKILL.md create mode 100644 personas/_shared/skills/building-vulnerability-scanning-workflow/references/api-reference.md create mode 100644 personas/_shared/skills/building-vulnerability-scanning-workflow/scripts/agent.py create mode 100644 personas/_shared/skills/bypassing-authentication-with-forced-browsing/LICENSE create mode 100644 personas/_shared/skills/bypassing-authentication-with-forced-browsing/SKILL.md create mode 100644 personas/_shared/skills/bypassing-authentication-with-forced-browsing/references/api-reference.md create mode 100644 personas/_shared/skills/bypassing-authentication-with-forced-browsing/scripts/agent.py create mode 100644 personas/_shared/skills/collecting-indicators-of-compromise/LICENSE create mode 100644 personas/_shared/skills/collecting-indicators-of-compromise/SKILL.md create mode 100644 personas/_shared/skills/collecting-indicators-of-compromise/references/api-reference.md create mode 100644 personas/_shared/skills/collecting-indicators-of-compromise/scripts/agent.py create mode 100644 personas/_shared/skills/collecting-open-source-intelligence/LICENSE create mode 100644 personas/_shared/skills/collecting-open-source-intelligence/SKILL.md create mode 100644 personas/_shared/skills/collecting-open-source-intelligence/references/api-reference.md create mode 100644 personas/_shared/skills/collecting-open-source-intelligence/scripts/agent.py create mode 100644 personas/_shared/skills/collecting-threat-intelligence-with-misp/LICENSE create mode 100644 personas/_shared/skills/collecting-threat-intelligence-with-misp/SKILL.md create mode 100644 personas/_shared/skills/collecting-threat-intelligence-with-misp/assets/template.md create mode 100644 personas/_shared/skills/collecting-threat-intelligence-with-misp/references/api-reference.md create mode 100644 personas/_shared/skills/collecting-threat-intelligence-with-misp/references/standards.md create mode 100644 personas/_shared/skills/collecting-threat-intelligence-with-misp/references/workflows.md create mode 100644 personas/_shared/skills/collecting-threat-intelligence-with-misp/scripts/agent.py create mode 100644 personas/_shared/skills/collecting-threat-intelligence-with-misp/scripts/process.py create mode 100644 personas/_shared/skills/collecting-volatile-evidence-from-compromised-host/LICENSE create mode 100644 personas/_shared/skills/collecting-volatile-evidence-from-compromised-host/SKILL.md create mode 100644 personas/_shared/skills/collecting-volatile-evidence-from-compromised-host/assets/template.md create mode 100644 personas/_shared/skills/collecting-volatile-evidence-from-compromised-host/references/api-reference.md create mode 100644 personas/_shared/skills/collecting-volatile-evidence-from-compromised-host/references/standards.md create mode 100644 personas/_shared/skills/collecting-volatile-evidence-from-compromised-host/references/workflows.md create mode 100644 personas/_shared/skills/collecting-volatile-evidence-from-compromised-host/scripts/agent.py create mode 100644 personas/_shared/skills/collecting-volatile-evidence-from-compromised-host/scripts/process.py create mode 100644 personas/_shared/skills/conducting-api-security-testing/LICENSE create mode 100644 personas/_shared/skills/conducting-api-security-testing/SKILL.md create mode 100644 personas/_shared/skills/conducting-api-security-testing/references/api-reference.md create mode 100644 personas/_shared/skills/conducting-api-security-testing/scripts/agent.py create mode 100644 personas/_shared/skills/conducting-cloud-incident-response/LICENSE create mode 100644 personas/_shared/skills/conducting-cloud-incident-response/SKILL.md create mode 100644 personas/_shared/skills/conducting-cloud-incident-response/references/api-reference.md create mode 100644 personas/_shared/skills/conducting-cloud-incident-response/scripts/agent.py create mode 100644 personas/_shared/skills/conducting-cloud-penetration-testing/LICENSE create mode 100644 personas/_shared/skills/conducting-cloud-penetration-testing/SKILL.md create mode 100644 personas/_shared/skills/conducting-cloud-penetration-testing/references/api-reference.md create mode 100644 personas/_shared/skills/conducting-cloud-penetration-testing/scripts/agent.py create mode 100644 personas/_shared/skills/conducting-domain-persistence-with-dcsync/LICENSE create mode 100644 personas/_shared/skills/conducting-domain-persistence-with-dcsync/SKILL.md create mode 100644 personas/_shared/skills/conducting-domain-persistence-with-dcsync/assets/template.md create mode 100644 personas/_shared/skills/conducting-domain-persistence-with-dcsync/references/api-reference.md create mode 100644 personas/_shared/skills/conducting-domain-persistence-with-dcsync/references/standards.md create mode 100644 personas/_shared/skills/conducting-domain-persistence-with-dcsync/references/workflows.md create mode 100644 personas/_shared/skills/conducting-domain-persistence-with-dcsync/scripts/agent.py create mode 100644 personas/_shared/skills/conducting-domain-persistence-with-dcsync/scripts/process.py create mode 100644 personas/_shared/skills/conducting-external-reconnaissance-with-osint/LICENSE create mode 100644 personas/_shared/skills/conducting-external-reconnaissance-with-osint/SKILL.md create mode 100644 personas/_shared/skills/conducting-external-reconnaissance-with-osint/references/api-reference.md create mode 100644 personas/_shared/skills/conducting-external-reconnaissance-with-osint/scripts/agent.py create mode 100644 personas/_shared/skills/conducting-full-scope-red-team-engagement/LICENSE create mode 100644 personas/_shared/skills/conducting-full-scope-red-team-engagement/SKILL.md create mode 100644 personas/_shared/skills/conducting-full-scope-red-team-engagement/assets/template.md create mode 100644 personas/_shared/skills/conducting-full-scope-red-team-engagement/references/api-reference.md create mode 100644 personas/_shared/skills/conducting-full-scope-red-team-engagement/references/standards.md create mode 100644 personas/_shared/skills/conducting-full-scope-red-team-engagement/references/workflows.md create mode 100644 personas/_shared/skills/conducting-full-scope-red-team-engagement/scripts/agent.py create mode 100644 personas/_shared/skills/conducting-full-scope-red-team-engagement/scripts/process.py create mode 100644 personas/_shared/skills/conducting-internal-network-penetration-test/LICENSE create mode 100644 personas/_shared/skills/conducting-internal-network-penetration-test/SKILL.md create mode 100644 personas/_shared/skills/conducting-internal-network-penetration-test/assets/template.md create mode 100644 personas/_shared/skills/conducting-internal-network-penetration-test/references/api-reference.md create mode 100644 personas/_shared/skills/conducting-internal-network-penetration-test/references/standards.md create mode 100644 personas/_shared/skills/conducting-internal-network-penetration-test/references/workflows.md create mode 100644 personas/_shared/skills/conducting-internal-network-penetration-test/scripts/agent.py create mode 100644 personas/_shared/skills/conducting-internal-network-penetration-test/scripts/process.py create mode 100644 personas/_shared/skills/conducting-internal-reconnaissance-with-bloodhound-ce/LICENSE create mode 100644 personas/_shared/skills/conducting-internal-reconnaissance-with-bloodhound-ce/SKILL.md create mode 100644 personas/_shared/skills/conducting-internal-reconnaissance-with-bloodhound-ce/assets/template.md create mode 100644 personas/_shared/skills/conducting-internal-reconnaissance-with-bloodhound-ce/references/api-reference.md create mode 100644 personas/_shared/skills/conducting-internal-reconnaissance-with-bloodhound-ce/references/standards.md create mode 100644 personas/_shared/skills/conducting-internal-reconnaissance-with-bloodhound-ce/references/workflows.md create mode 100644 personas/_shared/skills/conducting-internal-reconnaissance-with-bloodhound-ce/scripts/agent.py create mode 100644 personas/_shared/skills/conducting-internal-reconnaissance-with-bloodhound-ce/scripts/process.py create mode 100644 personas/_shared/skills/conducting-malware-incident-response/LICENSE create mode 100644 personas/_shared/skills/conducting-malware-incident-response/SKILL.md create mode 100644 personas/_shared/skills/conducting-malware-incident-response/references/api-reference.md create mode 100644 personas/_shared/skills/conducting-malware-incident-response/scripts/agent.py create mode 100644 personas/_shared/skills/conducting-man-in-the-middle-attack-simulation/LICENSE create mode 100644 personas/_shared/skills/conducting-man-in-the-middle-attack-simulation/SKILL.md create mode 100644 personas/_shared/skills/conducting-man-in-the-middle-attack-simulation/references/api-reference.md create mode 100644 personas/_shared/skills/conducting-man-in-the-middle-attack-simulation/scripts/agent.py create mode 100644 personas/_shared/skills/conducting-memory-forensics-with-volatility/LICENSE create mode 100644 personas/_shared/skills/conducting-memory-forensics-with-volatility/SKILL.md create mode 100644 personas/_shared/skills/conducting-memory-forensics-with-volatility/references/api-reference.md create mode 100644 personas/_shared/skills/conducting-memory-forensics-with-volatility/scripts/agent.py create mode 100644 personas/_shared/skills/conducting-mobile-app-penetration-test/LICENSE create mode 100644 personas/_shared/skills/conducting-mobile-app-penetration-test/SKILL.md create mode 100644 personas/_shared/skills/conducting-mobile-app-penetration-test/references/api-reference.md create mode 100644 personas/_shared/skills/conducting-mobile-app-penetration-test/scripts/agent.py create mode 100644 personas/_shared/skills/conducting-network-penetration-test/LICENSE create mode 100644 personas/_shared/skills/conducting-network-penetration-test/SKILL.md create mode 100644 personas/_shared/skills/conducting-network-penetration-test/references/api-reference.md create mode 100644 personas/_shared/skills/conducting-network-penetration-test/scripts/agent.py create mode 100644 personas/_shared/skills/conducting-pass-the-ticket-attack/LICENSE create mode 100644 personas/_shared/skills/conducting-pass-the-ticket-attack/SKILL.md create mode 100644 personas/_shared/skills/conducting-pass-the-ticket-attack/assets/template.md create mode 100644 personas/_shared/skills/conducting-pass-the-ticket-attack/references/api-reference.md create mode 100644 personas/_shared/skills/conducting-pass-the-ticket-attack/references/standards.md create mode 100644 personas/_shared/skills/conducting-pass-the-ticket-attack/references/workflows.md create mode 100644 personas/_shared/skills/conducting-pass-the-ticket-attack/scripts/agent.py create mode 100644 personas/_shared/skills/conducting-pass-the-ticket-attack/scripts/process.py create mode 100644 personas/_shared/skills/conducting-phishing-incident-response/LICENSE create mode 100644 personas/_shared/skills/conducting-phishing-incident-response/SKILL.md create mode 100644 personas/_shared/skills/conducting-phishing-incident-response/references/api-reference.md create mode 100644 personas/_shared/skills/conducting-phishing-incident-response/scripts/agent.py create mode 100644 personas/_shared/skills/conducting-post-incident-lessons-learned/LICENSE create mode 100644 personas/_shared/skills/conducting-post-incident-lessons-learned/SKILL.md create mode 100644 personas/_shared/skills/conducting-post-incident-lessons-learned/assets/template.md create mode 100644 personas/_shared/skills/conducting-post-incident-lessons-learned/references/api-reference.md create mode 100644 personas/_shared/skills/conducting-post-incident-lessons-learned/references/standards.md create mode 100644 personas/_shared/skills/conducting-post-incident-lessons-learned/references/workflows.md create mode 100644 personas/_shared/skills/conducting-post-incident-lessons-learned/scripts/agent.py create mode 100644 personas/_shared/skills/conducting-post-incident-lessons-learned/scripts/process.py create mode 100644 personas/_shared/skills/conducting-social-engineering-penetration-test/LICENSE create mode 100644 personas/_shared/skills/conducting-social-engineering-penetration-test/SKILL.md create mode 100644 personas/_shared/skills/conducting-social-engineering-penetration-test/assets/template.md create mode 100644 personas/_shared/skills/conducting-social-engineering-penetration-test/references/api-reference.md create mode 100644 personas/_shared/skills/conducting-social-engineering-penetration-test/references/standards.md create mode 100644 personas/_shared/skills/conducting-social-engineering-penetration-test/references/workflows.md create mode 100644 personas/_shared/skills/conducting-social-engineering-penetration-test/scripts/agent.py create mode 100644 personas/_shared/skills/conducting-social-engineering-penetration-test/scripts/process.py create mode 100644 personas/_shared/skills/conducting-social-engineering-pretext-call/LICENSE create mode 100644 personas/_shared/skills/conducting-social-engineering-pretext-call/SKILL.md create mode 100644 personas/_shared/skills/conducting-social-engineering-pretext-call/assets/template.md create mode 100644 personas/_shared/skills/conducting-social-engineering-pretext-call/references/api-reference.md create mode 100644 personas/_shared/skills/conducting-social-engineering-pretext-call/references/standards.md create mode 100644 personas/_shared/skills/conducting-social-engineering-pretext-call/references/workflows.md create mode 100644 personas/_shared/skills/conducting-social-engineering-pretext-call/scripts/agent.py create mode 100644 personas/_shared/skills/conducting-social-engineering-pretext-call/scripts/process.py create mode 100644 personas/_shared/skills/conducting-spearphishing-simulation-campaign/LICENSE create mode 100644 personas/_shared/skills/conducting-spearphishing-simulation-campaign/SKILL.md create mode 100644 personas/_shared/skills/conducting-spearphishing-simulation-campaign/assets/template.md create mode 100644 personas/_shared/skills/conducting-spearphishing-simulation-campaign/references/api-reference.md create mode 100644 personas/_shared/skills/conducting-spearphishing-simulation-campaign/references/standards.md create mode 100644 personas/_shared/skills/conducting-spearphishing-simulation-campaign/references/workflows.md create mode 100644 personas/_shared/skills/conducting-spearphishing-simulation-campaign/scripts/agent.py create mode 100644 personas/_shared/skills/conducting-spearphishing-simulation-campaign/scripts/process.py create mode 100644 personas/_shared/skills/conducting-wireless-network-penetration-test/LICENSE create mode 100644 personas/_shared/skills/conducting-wireless-network-penetration-test/SKILL.md create mode 100644 personas/_shared/skills/conducting-wireless-network-penetration-test/references/api-reference.md create mode 100644 personas/_shared/skills/conducting-wireless-network-penetration-test/scripts/agent.py create mode 100644 personas/_shared/skills/configuring-active-directory-tiered-model/LICENSE create mode 100644 personas/_shared/skills/configuring-active-directory-tiered-model/SKILL.md create mode 100644 personas/_shared/skills/configuring-active-directory-tiered-model/references/api-reference.md create mode 100644 personas/_shared/skills/configuring-active-directory-tiered-model/scripts/agent.py create mode 100644 personas/_shared/skills/configuring-aws-verified-access-for-ztna/LICENSE create mode 100644 personas/_shared/skills/configuring-aws-verified-access-for-ztna/SKILL.md create mode 100644 personas/_shared/skills/configuring-aws-verified-access-for-ztna/assets/template.md create mode 100644 personas/_shared/skills/configuring-aws-verified-access-for-ztna/references/api-reference.md create mode 100644 personas/_shared/skills/configuring-aws-verified-access-for-ztna/references/standards.md create mode 100644 personas/_shared/skills/configuring-aws-verified-access-for-ztna/references/workflows.md create mode 100644 personas/_shared/skills/configuring-aws-verified-access-for-ztna/scripts/agent.py create mode 100644 personas/_shared/skills/configuring-aws-verified-access-for-ztna/scripts/process.py create mode 100644 personas/_shared/skills/configuring-certificate-authority-with-openssl/LICENSE create mode 100644 personas/_shared/skills/configuring-certificate-authority-with-openssl/SKILL.md create mode 100644 personas/_shared/skills/configuring-certificate-authority-with-openssl/assets/template.md create mode 100644 personas/_shared/skills/configuring-certificate-authority-with-openssl/references/api-reference.md create mode 100644 personas/_shared/skills/configuring-certificate-authority-with-openssl/references/standards.md create mode 100644 personas/_shared/skills/configuring-certificate-authority-with-openssl/references/workflows.md create mode 100644 personas/_shared/skills/configuring-certificate-authority-with-openssl/scripts/agent.py create mode 100644 personas/_shared/skills/configuring-certificate-authority-with-openssl/scripts/process.py create mode 100644 personas/_shared/skills/configuring-host-based-intrusion-detection/LICENSE create mode 100644 personas/_shared/skills/configuring-host-based-intrusion-detection/SKILL.md create mode 100644 personas/_shared/skills/configuring-host-based-intrusion-detection/assets/template.md create mode 100644 personas/_shared/skills/configuring-host-based-intrusion-detection/references/api-reference.md create mode 100644 personas/_shared/skills/configuring-host-based-intrusion-detection/references/standards.md create mode 100644 personas/_shared/skills/configuring-host-based-intrusion-detection/references/workflows.md create mode 100644 personas/_shared/skills/configuring-host-based-intrusion-detection/scripts/agent.py create mode 100644 personas/_shared/skills/configuring-host-based-intrusion-detection/scripts/process.py create mode 100644 personas/_shared/skills/configuring-hsm-for-key-storage/LICENSE create mode 100644 personas/_shared/skills/configuring-hsm-for-key-storage/SKILL.md create mode 100644 personas/_shared/skills/configuring-hsm-for-key-storage/assets/template.md create mode 100644 personas/_shared/skills/configuring-hsm-for-key-storage/references/api-reference.md create mode 100644 personas/_shared/skills/configuring-hsm-for-key-storage/references/standards.md create mode 100644 personas/_shared/skills/configuring-hsm-for-key-storage/references/workflows.md create mode 100644 personas/_shared/skills/configuring-hsm-for-key-storage/scripts/agent.py create mode 100644 personas/_shared/skills/configuring-hsm-for-key-storage/scripts/process.py create mode 100644 personas/_shared/skills/configuring-identity-aware-proxy-with-google-iap/LICENSE create mode 100644 personas/_shared/skills/configuring-identity-aware-proxy-with-google-iap/SKILL.md create mode 100644 personas/_shared/skills/configuring-identity-aware-proxy-with-google-iap/assets/template.md create mode 100644 personas/_shared/skills/configuring-identity-aware-proxy-with-google-iap/references/api-reference.md create mode 100644 personas/_shared/skills/configuring-identity-aware-proxy-with-google-iap/references/standards.md create mode 100644 personas/_shared/skills/configuring-identity-aware-proxy-with-google-iap/references/workflows.md create mode 100644 personas/_shared/skills/configuring-identity-aware-proxy-with-google-iap/scripts/agent.py create mode 100644 personas/_shared/skills/configuring-identity-aware-proxy-with-google-iap/scripts/process.py create mode 100644 personas/_shared/skills/configuring-ldap-security-hardening/LICENSE create mode 100644 personas/_shared/skills/configuring-ldap-security-hardening/SKILL.md create mode 100644 personas/_shared/skills/configuring-ldap-security-hardening/references/api-reference.md create mode 100644 personas/_shared/skills/configuring-ldap-security-hardening/scripts/agent.py create mode 100644 personas/_shared/skills/configuring-microsegmentation-for-zero-trust/LICENSE create mode 100644 personas/_shared/skills/configuring-microsegmentation-for-zero-trust/SKILL.md create mode 100644 personas/_shared/skills/configuring-microsegmentation-for-zero-trust/assets/template.md create mode 100644 personas/_shared/skills/configuring-microsegmentation-for-zero-trust/references/api-reference.md create mode 100644 personas/_shared/skills/configuring-microsegmentation-for-zero-trust/references/standards.md create mode 100644 personas/_shared/skills/configuring-microsegmentation-for-zero-trust/references/workflows.md create mode 100644 personas/_shared/skills/configuring-microsegmentation-for-zero-trust/scripts/agent.py create mode 100644 personas/_shared/skills/configuring-microsegmentation-for-zero-trust/scripts/process.py create mode 100644 personas/_shared/skills/configuring-multi-factor-authentication-with-duo/LICENSE create mode 100644 personas/_shared/skills/configuring-multi-factor-authentication-with-duo/SKILL.md create mode 100644 personas/_shared/skills/configuring-multi-factor-authentication-with-duo/assets/template.md create mode 100644 personas/_shared/skills/configuring-multi-factor-authentication-with-duo/references/api-reference.md create mode 100644 personas/_shared/skills/configuring-multi-factor-authentication-with-duo/references/standards.md create mode 100644 personas/_shared/skills/configuring-multi-factor-authentication-with-duo/references/workflows.md create mode 100644 personas/_shared/skills/configuring-multi-factor-authentication-with-duo/scripts/agent.py create mode 100644 personas/_shared/skills/configuring-multi-factor-authentication-with-duo/scripts/process.py create mode 100644 personas/_shared/skills/configuring-network-segmentation-with-vlans/LICENSE create mode 100644 personas/_shared/skills/configuring-network-segmentation-with-vlans/SKILL.md create mode 100644 personas/_shared/skills/configuring-network-segmentation-with-vlans/references/api-reference.md create mode 100644 personas/_shared/skills/configuring-network-segmentation-with-vlans/scripts/agent.py create mode 100644 personas/_shared/skills/configuring-oauth2-authorization-flow/LICENSE create mode 100644 personas/_shared/skills/configuring-oauth2-authorization-flow/SKILL.md create mode 100644 personas/_shared/skills/configuring-oauth2-authorization-flow/assets/template.md create mode 100644 personas/_shared/skills/configuring-oauth2-authorization-flow/references/api-reference.md create mode 100644 personas/_shared/skills/configuring-oauth2-authorization-flow/references/standards.md create mode 100644 personas/_shared/skills/configuring-oauth2-authorization-flow/references/workflows.md create mode 100644 personas/_shared/skills/configuring-oauth2-authorization-flow/scripts/agent.py create mode 100644 personas/_shared/skills/configuring-oauth2-authorization-flow/scripts/process.py create mode 100644 personas/_shared/skills/configuring-pfsense-firewall-rules/LICENSE create mode 100644 personas/_shared/skills/configuring-pfsense-firewall-rules/SKILL.md create mode 100644 personas/_shared/skills/configuring-pfsense-firewall-rules/references/api-reference.md create mode 100644 personas/_shared/skills/configuring-pfsense-firewall-rules/scripts/agent.py create mode 100644 personas/_shared/skills/configuring-snort-ids-for-intrusion-detection/LICENSE create mode 100644 personas/_shared/skills/configuring-snort-ids-for-intrusion-detection/SKILL.md create mode 100644 personas/_shared/skills/configuring-snort-ids-for-intrusion-detection/references/api-reference.md create mode 100644 personas/_shared/skills/configuring-snort-ids-for-intrusion-detection/scripts/agent.py create mode 100644 personas/_shared/skills/configuring-suricata-for-network-monitoring/LICENSE create mode 100644 personas/_shared/skills/configuring-suricata-for-network-monitoring/SKILL.es.md create mode 100644 personas/_shared/skills/configuring-suricata-for-network-monitoring/SKILL.md create mode 100644 personas/_shared/skills/configuring-suricata-for-network-monitoring/references/api-reference.md create mode 100644 personas/_shared/skills/configuring-suricata-for-network-monitoring/scripts/agent.py create mode 100644 personas/_shared/skills/configuring-tls-1-3-for-secure-communications/LICENSE create mode 100644 personas/_shared/skills/configuring-tls-1-3-for-secure-communications/SKILL.md create mode 100644 personas/_shared/skills/configuring-tls-1-3-for-secure-communications/assets/template.md create mode 100644 personas/_shared/skills/configuring-tls-1-3-for-secure-communications/references/api-reference.md create mode 100644 personas/_shared/skills/configuring-tls-1-3-for-secure-communications/references/standards.md create mode 100644 personas/_shared/skills/configuring-tls-1-3-for-secure-communications/references/workflows.md create mode 100644 personas/_shared/skills/configuring-tls-1-3-for-secure-communications/scripts/agent.py create mode 100644 personas/_shared/skills/configuring-tls-1-3-for-secure-communications/scripts/process.py create mode 100644 personas/_shared/skills/configuring-windows-defender-advanced-settings/LICENSE create mode 100644 personas/_shared/skills/configuring-windows-defender-advanced-settings/SKILL.md create mode 100644 personas/_shared/skills/configuring-windows-defender-advanced-settings/assets/template.md create mode 100644 personas/_shared/skills/configuring-windows-defender-advanced-settings/references/api-reference.md create mode 100644 personas/_shared/skills/configuring-windows-defender-advanced-settings/references/standards.md create mode 100644 personas/_shared/skills/configuring-windows-defender-advanced-settings/references/workflows.md create mode 100644 personas/_shared/skills/configuring-windows-defender-advanced-settings/scripts/agent.py create mode 100644 personas/_shared/skills/configuring-windows-defender-advanced-settings/scripts/process.py create mode 100644 personas/_shared/skills/configuring-windows-event-logging-for-detection/LICENSE create mode 100644 personas/_shared/skills/configuring-windows-event-logging-for-detection/SKILL.md create mode 100644 personas/_shared/skills/configuring-windows-event-logging-for-detection/assets/template.md create mode 100644 personas/_shared/skills/configuring-windows-event-logging-for-detection/references/api-reference.md create mode 100644 personas/_shared/skills/configuring-windows-event-logging-for-detection/references/standards.md create mode 100644 personas/_shared/skills/configuring-windows-event-logging-for-detection/references/workflows.md create mode 100644 personas/_shared/skills/configuring-windows-event-logging-for-detection/scripts/agent.py create mode 100644 personas/_shared/skills/configuring-windows-event-logging-for-detection/scripts/process.py create mode 100644 personas/_shared/skills/configuring-zscaler-private-access-for-ztna/LICENSE create mode 100644 personas/_shared/skills/configuring-zscaler-private-access-for-ztna/SKILL.md create mode 100644 personas/_shared/skills/configuring-zscaler-private-access-for-ztna/assets/template.md create mode 100644 personas/_shared/skills/configuring-zscaler-private-access-for-ztna/references/api-reference.md create mode 100644 personas/_shared/skills/configuring-zscaler-private-access-for-ztna/references/standards.md create mode 100644 personas/_shared/skills/configuring-zscaler-private-access-for-ztna/references/workflows.md create mode 100644 personas/_shared/skills/configuring-zscaler-private-access-for-ztna/scripts/agent.py create mode 100644 personas/_shared/skills/configuring-zscaler-private-access-for-ztna/scripts/process.py create mode 100644 personas/_shared/skills/containing-active-breach/LICENSE create mode 100644 personas/_shared/skills/containing-active-breach/SKILL.md create mode 100644 personas/_shared/skills/containing-active-breach/references/api-reference.md create mode 100644 personas/_shared/skills/containing-active-breach/scripts/agent.py create mode 100644 personas/_shared/skills/correlating-security-events-in-qradar/LICENSE create mode 100644 personas/_shared/skills/correlating-security-events-in-qradar/SKILL.md create mode 100644 personas/_shared/skills/correlating-security-events-in-qradar/references/api-reference.md create mode 100644 personas/_shared/skills/correlating-security-events-in-qradar/scripts/agent.py create mode 100644 personas/_shared/skills/correlating-threat-campaigns/LICENSE create mode 100644 personas/_shared/skills/correlating-threat-campaigns/SKILL.md create mode 100644 personas/_shared/skills/correlating-threat-campaigns/references/api-reference.md create mode 100644 personas/_shared/skills/correlating-threat-campaigns/scripts/agent.py create mode 100644 personas/_shared/skills/deobfuscating-javascript-malware/LICENSE create mode 100644 personas/_shared/skills/deobfuscating-javascript-malware/SKILL.md create mode 100644 personas/_shared/skills/deobfuscating-javascript-malware/references/api-reference.md create mode 100644 personas/_shared/skills/deobfuscating-javascript-malware/scripts/agent.py create mode 100644 personas/_shared/skills/deobfuscating-powershell-obfuscated-malware/LICENSE create mode 100644 personas/_shared/skills/deobfuscating-powershell-obfuscated-malware/SKILL.md create mode 100644 personas/_shared/skills/deobfuscating-powershell-obfuscated-malware/assets/template.md create mode 100644 personas/_shared/skills/deobfuscating-powershell-obfuscated-malware/references/api-reference.md create mode 100644 personas/_shared/skills/deobfuscating-powershell-obfuscated-malware/references/standards.md create mode 100644 personas/_shared/skills/deobfuscating-powershell-obfuscated-malware/references/workflows.md create mode 100644 personas/_shared/skills/deobfuscating-powershell-obfuscated-malware/scripts/agent.py create mode 100644 personas/_shared/skills/deobfuscating-powershell-obfuscated-malware/scripts/process.py create mode 100644 personas/_shared/skills/deploying-active-directory-honeytokens/LICENSE create mode 100644 personas/_shared/skills/deploying-active-directory-honeytokens/SKILL.md create mode 100644 personas/_shared/skills/deploying-active-directory-honeytokens/references/api-reference.md create mode 100644 personas/_shared/skills/deploying-active-directory-honeytokens/scripts/Deploy-ADHoneytokens.ps1 create mode 100644 personas/_shared/skills/deploying-active-directory-honeytokens/scripts/agent.py create mode 100644 personas/_shared/skills/deploying-cloudflare-access-for-zero-trust/LICENSE create mode 100644 personas/_shared/skills/deploying-cloudflare-access-for-zero-trust/SKILL.md create mode 100644 personas/_shared/skills/deploying-cloudflare-access-for-zero-trust/assets/template.md create mode 100644 personas/_shared/skills/deploying-cloudflare-access-for-zero-trust/references/api-reference.md create mode 100644 personas/_shared/skills/deploying-cloudflare-access-for-zero-trust/references/standards.md create mode 100644 personas/_shared/skills/deploying-cloudflare-access-for-zero-trust/references/workflows.md create mode 100644 personas/_shared/skills/deploying-cloudflare-access-for-zero-trust/scripts/agent.py create mode 100644 personas/_shared/skills/deploying-cloudflare-access-for-zero-trust/scripts/process.py create mode 100644 personas/_shared/skills/deploying-decoy-files-for-ransomware-detection/LICENSE create mode 100644 personas/_shared/skills/deploying-decoy-files-for-ransomware-detection/SKILL.md create mode 100644 personas/_shared/skills/deploying-decoy-files-for-ransomware-detection/references/api-reference.md create mode 100644 personas/_shared/skills/deploying-decoy-files-for-ransomware-detection/scripts/agent.py create mode 100644 personas/_shared/skills/deploying-edr-agent-with-crowdstrike/LICENSE create mode 100644 personas/_shared/skills/deploying-edr-agent-with-crowdstrike/SKILL.md create mode 100644 personas/_shared/skills/deploying-edr-agent-with-crowdstrike/assets/template.md create mode 100644 personas/_shared/skills/deploying-edr-agent-with-crowdstrike/references/api-reference.md create mode 100644 personas/_shared/skills/deploying-edr-agent-with-crowdstrike/references/standards.md create mode 100644 personas/_shared/skills/deploying-edr-agent-with-crowdstrike/references/workflows.md create mode 100644 personas/_shared/skills/deploying-edr-agent-with-crowdstrike/scripts/agent.py create mode 100644 personas/_shared/skills/deploying-edr-agent-with-crowdstrike/scripts/process.py create mode 100644 personas/_shared/skills/deploying-osquery-for-endpoint-monitoring/LICENSE create mode 100644 personas/_shared/skills/deploying-osquery-for-endpoint-monitoring/SKILL.md create mode 100644 personas/_shared/skills/deploying-osquery-for-endpoint-monitoring/assets/template.md create mode 100644 personas/_shared/skills/deploying-osquery-for-endpoint-monitoring/references/api-reference.md create mode 100644 personas/_shared/skills/deploying-osquery-for-endpoint-monitoring/references/standards.md create mode 100644 personas/_shared/skills/deploying-osquery-for-endpoint-monitoring/references/workflows.md create mode 100644 personas/_shared/skills/deploying-osquery-for-endpoint-monitoring/scripts/agent.py create mode 100644 personas/_shared/skills/deploying-osquery-for-endpoint-monitoring/scripts/process.py create mode 100644 personas/_shared/skills/deploying-palo-alto-prisma-access-zero-trust/LICENSE create mode 100644 personas/_shared/skills/deploying-palo-alto-prisma-access-zero-trust/SKILL.md create mode 100644 personas/_shared/skills/deploying-palo-alto-prisma-access-zero-trust/references/api-reference.md create mode 100644 personas/_shared/skills/deploying-palo-alto-prisma-access-zero-trust/scripts/agent.py create mode 100644 personas/_shared/skills/deploying-ransomware-canary-files/LICENSE create mode 100644 personas/_shared/skills/deploying-ransomware-canary-files/SKILL.md create mode 100644 personas/_shared/skills/deploying-ransomware-canary-files/references/api-reference.md create mode 100644 personas/_shared/skills/deploying-ransomware-canary-files/scripts/agent.py create mode 100644 personas/_shared/skills/deploying-software-defined-perimeter/LICENSE create mode 100644 personas/_shared/skills/deploying-software-defined-perimeter/SKILL.md create mode 100644 personas/_shared/skills/deploying-software-defined-perimeter/assets/template.md create mode 100644 personas/_shared/skills/deploying-software-defined-perimeter/references/api-reference.md create mode 100644 personas/_shared/skills/deploying-software-defined-perimeter/references/standards.md create mode 100644 personas/_shared/skills/deploying-software-defined-perimeter/references/workflows.md create mode 100644 personas/_shared/skills/deploying-software-defined-perimeter/scripts/agent.py create mode 100644 personas/_shared/skills/deploying-software-defined-perimeter/scripts/process.py create mode 100644 personas/_shared/skills/deploying-tailscale-for-zero-trust-vpn/LICENSE create mode 100644 personas/_shared/skills/deploying-tailscale-for-zero-trust-vpn/SKILL.md create mode 100644 personas/_shared/skills/deploying-tailscale-for-zero-trust-vpn/assets/template.md create mode 100644 personas/_shared/skills/deploying-tailscale-for-zero-trust-vpn/references/api-reference.md create mode 100644 personas/_shared/skills/deploying-tailscale-for-zero-trust-vpn/references/standards.md create mode 100644 personas/_shared/skills/deploying-tailscale-for-zero-trust-vpn/references/workflows.md create mode 100644 personas/_shared/skills/deploying-tailscale-for-zero-trust-vpn/scripts/agent.py create mode 100644 personas/_shared/skills/deploying-tailscale-for-zero-trust-vpn/scripts/process.py create mode 100644 personas/_shared/skills/detecting-ai-model-prompt-injection-attacks/LICENSE create mode 100644 personas/_shared/skills/detecting-ai-model-prompt-injection-attacks/SKILL.md create mode 100644 personas/_shared/skills/detecting-ai-model-prompt-injection-attacks/references/api-reference.md create mode 100644 personas/_shared/skills/detecting-ai-model-prompt-injection-attacks/scripts/agent.py create mode 100644 personas/_shared/skills/detecting-anomalies-in-industrial-control-systems/LICENSE create mode 100644 personas/_shared/skills/detecting-anomalies-in-industrial-control-systems/SKILL.md create mode 100644 personas/_shared/skills/detecting-anomalies-in-industrial-control-systems/references/api-reference.md create mode 100644 personas/_shared/skills/detecting-anomalies-in-industrial-control-systems/scripts/agent.py create mode 100644 personas/_shared/skills/detecting-anomalous-authentication-patterns/LICENSE create mode 100644 personas/_shared/skills/detecting-anomalous-authentication-patterns/SKILL.md create mode 100644 personas/_shared/skills/detecting-anomalous-authentication-patterns/references/api-reference.md create mode 100644 personas/_shared/skills/detecting-anomalous-authentication-patterns/scripts/agent.py create mode 100644 personas/_shared/skills/detecting-api-enumeration-attacks/LICENSE create mode 100644 personas/_shared/skills/detecting-api-enumeration-attacks/SKILL.md create mode 100644 personas/_shared/skills/detecting-api-enumeration-attacks/references/api-reference.md create mode 100644 personas/_shared/skills/detecting-api-enumeration-attacks/scripts/agent.py create mode 100644 personas/_shared/skills/detecting-arp-poisoning-in-network-traffic/LICENSE create mode 100644 personas/_shared/skills/detecting-arp-poisoning-in-network-traffic/SKILL.md create mode 100644 personas/_shared/skills/detecting-arp-poisoning-in-network-traffic/references/api-reference.md create mode 100644 personas/_shared/skills/detecting-arp-poisoning-in-network-traffic/scripts/agent.py create mode 100644 personas/_shared/skills/detecting-attacks-on-historian-servers/LICENSE create mode 100644 personas/_shared/skills/detecting-attacks-on-historian-servers/SKILL.md create mode 100644 personas/_shared/skills/detecting-attacks-on-historian-servers/references/api-reference.md create mode 100644 personas/_shared/skills/detecting-attacks-on-historian-servers/scripts/agent.py create mode 100644 personas/_shared/skills/detecting-attacks-on-scada-systems/LICENSE create mode 100644 personas/_shared/skills/detecting-attacks-on-scada-systems/SKILL.md create mode 100644 personas/_shared/skills/detecting-attacks-on-scada-systems/references/api-reference.md create mode 100644 personas/_shared/skills/detecting-attacks-on-scada-systems/scripts/agent.py create mode 100644 personas/_shared/skills/detecting-aws-cloudtrail-anomalies/LICENSE create mode 100644 personas/_shared/skills/detecting-aws-cloudtrail-anomalies/SKILL.md create mode 100644 personas/_shared/skills/detecting-aws-cloudtrail-anomalies/references/api-reference.md create mode 100644 personas/_shared/skills/detecting-aws-cloudtrail-anomalies/scripts/agent.py create mode 100644 personas/_shared/skills/detecting-aws-credential-exposure-with-trufflehog/LICENSE create mode 100644 personas/_shared/skills/detecting-aws-credential-exposure-with-trufflehog/SKILL.md create mode 100644 personas/_shared/skills/detecting-aws-credential-exposure-with-trufflehog/references/api-reference.md create mode 100644 personas/_shared/skills/detecting-aws-credential-exposure-with-trufflehog/scripts/agent.py create mode 100644 personas/_shared/skills/detecting-aws-guardduty-findings-automation/LICENSE create mode 100644 personas/_shared/skills/detecting-aws-guardduty-findings-automation/SKILL.md create mode 100644 personas/_shared/skills/detecting-aws-guardduty-findings-automation/assets/template.md create mode 100644 personas/_shared/skills/detecting-aws-guardduty-findings-automation/references/api-reference.md create mode 100644 personas/_shared/skills/detecting-aws-guardduty-findings-automation/references/standards.md create mode 100644 personas/_shared/skills/detecting-aws-guardduty-findings-automation/references/workflows.md create mode 100644 personas/_shared/skills/detecting-aws-guardduty-findings-automation/scripts/agent.py create mode 100644 personas/_shared/skills/detecting-aws-guardduty-findings-automation/scripts/process.py create mode 100644 personas/_shared/skills/detecting-aws-iam-privilege-escalation/LICENSE create mode 100644 personas/_shared/skills/detecting-aws-iam-privilege-escalation/SKILL.md create mode 100644 personas/_shared/skills/detecting-aws-iam-privilege-escalation/references/api-reference.md create mode 100644 personas/_shared/skills/detecting-aws-iam-privilege-escalation/scripts/agent.py create mode 100644 personas/_shared/skills/detecting-azure-lateral-movement/LICENSE create mode 100644 personas/_shared/skills/detecting-azure-lateral-movement/SKILL.md create mode 100644 personas/_shared/skills/detecting-azure-lateral-movement/references/api-reference.md create mode 100644 personas/_shared/skills/detecting-azure-lateral-movement/scripts/agent.py create mode 100644 personas/_shared/skills/detecting-azure-service-principal-abuse/LICENSE create mode 100644 personas/_shared/skills/detecting-azure-service-principal-abuse/SKILL.md create mode 100644 personas/_shared/skills/detecting-azure-service-principal-abuse/assets/template.md create mode 100644 personas/_shared/skills/detecting-azure-service-principal-abuse/references/api-reference.md create mode 100644 personas/_shared/skills/detecting-azure-service-principal-abuse/references/standards.md create mode 100644 personas/_shared/skills/detecting-azure-service-principal-abuse/references/workflows.md create mode 100644 personas/_shared/skills/detecting-azure-service-principal-abuse/scripts/agent.py create mode 100644 personas/_shared/skills/detecting-azure-service-principal-abuse/scripts/process.py create mode 100644 personas/_shared/skills/detecting-azure-storage-account-misconfigurations/LICENSE create mode 100644 personas/_shared/skills/detecting-azure-storage-account-misconfigurations/SKILL.md create mode 100644 personas/_shared/skills/detecting-azure-storage-account-misconfigurations/references/api-reference.md create mode 100644 personas/_shared/skills/detecting-azure-storage-account-misconfigurations/scripts/agent.py create mode 100644 personas/_shared/skills/detecting-beaconing-patterns-with-zeek/LICENSE create mode 100644 personas/_shared/skills/detecting-beaconing-patterns-with-zeek/SKILL.md create mode 100644 personas/_shared/skills/detecting-beaconing-patterns-with-zeek/references/api-reference.md create mode 100644 personas/_shared/skills/detecting-beaconing-patterns-with-zeek/scripts/agent.py create mode 100644 personas/_shared/skills/detecting-bluetooth-low-energy-attacks/LICENSE create mode 100644 personas/_shared/skills/detecting-bluetooth-low-energy-attacks/SKILL.md create mode 100644 personas/_shared/skills/detecting-bluetooth-low-energy-attacks/references/api-reference.md create mode 100644 personas/_shared/skills/detecting-bluetooth-low-energy-attacks/scripts/agent.py create mode 100644 personas/_shared/skills/detecting-broken-object-property-level-authorization/LICENSE create mode 100644 personas/_shared/skills/detecting-broken-object-property-level-authorization/SKILL.md create mode 100644 personas/_shared/skills/detecting-broken-object-property-level-authorization/references/api-reference.md create mode 100644 personas/_shared/skills/detecting-broken-object-property-level-authorization/scripts/agent.py create mode 100644 personas/_shared/skills/detecting-business-email-compromise-with-ai/LICENSE create mode 100644 personas/_shared/skills/detecting-business-email-compromise-with-ai/SKILL.md create mode 100644 personas/_shared/skills/detecting-business-email-compromise-with-ai/assets/template.md create mode 100644 personas/_shared/skills/detecting-business-email-compromise-with-ai/references/api-reference.md create mode 100644 personas/_shared/skills/detecting-business-email-compromise-with-ai/references/standards.md create mode 100644 personas/_shared/skills/detecting-business-email-compromise-with-ai/references/workflows.md create mode 100644 personas/_shared/skills/detecting-business-email-compromise-with-ai/scripts/agent.py create mode 100644 personas/_shared/skills/detecting-business-email-compromise-with-ai/scripts/process.py create mode 100644 personas/_shared/skills/detecting-business-email-compromise/LICENSE create mode 100644 personas/_shared/skills/detecting-business-email-compromise/SKILL.md create mode 100644 personas/_shared/skills/detecting-business-email-compromise/assets/template.md create mode 100644 personas/_shared/skills/detecting-business-email-compromise/references/api-reference.md create mode 100644 personas/_shared/skills/detecting-business-email-compromise/references/standards.md create mode 100644 personas/_shared/skills/detecting-business-email-compromise/references/workflows.md create mode 100644 personas/_shared/skills/detecting-business-email-compromise/scripts/agent.py create mode 100644 personas/_shared/skills/detecting-business-email-compromise/scripts/process.py create mode 100644 personas/_shared/skills/detecting-cloud-threats-with-guardduty/LICENSE create mode 100644 personas/_shared/skills/detecting-cloud-threats-with-guardduty/SKILL.md create mode 100644 personas/_shared/skills/detecting-cloud-threats-with-guardduty/references/api-reference.md create mode 100644 personas/_shared/skills/detecting-cloud-threats-with-guardduty/scripts/agent.py create mode 100644 personas/_shared/skills/detecting-command-and-control-over-dns/LICENSE create mode 100644 personas/_shared/skills/detecting-command-and-control-over-dns/SKILL.md create mode 100644 personas/_shared/skills/detecting-command-and-control-over-dns/references/api-reference.md create mode 100644 personas/_shared/skills/detecting-command-and-control-over-dns/scripts/agent.py create mode 100644 personas/_shared/skills/detecting-compromised-cloud-credentials/LICENSE create mode 100644 personas/_shared/skills/detecting-compromised-cloud-credentials/SKILL.md create mode 100644 personas/_shared/skills/detecting-compromised-cloud-credentials/references/api-reference.md create mode 100644 personas/_shared/skills/detecting-compromised-cloud-credentials/scripts/agent.py create mode 100644 personas/_shared/skills/detecting-container-drift-at-runtime/LICENSE create mode 100644 personas/_shared/skills/detecting-container-drift-at-runtime/SKILL.md create mode 100644 personas/_shared/skills/detecting-container-drift-at-runtime/assets/template.md create mode 100644 personas/_shared/skills/detecting-container-drift-at-runtime/references/api-reference.md create mode 100644 personas/_shared/skills/detecting-container-drift-at-runtime/references/standards.md create mode 100644 personas/_shared/skills/detecting-container-drift-at-runtime/references/workflows.md create mode 100644 personas/_shared/skills/detecting-container-drift-at-runtime/scripts/agent.py create mode 100644 personas/_shared/skills/detecting-container-drift-at-runtime/scripts/process.py create mode 100644 personas/_shared/skills/detecting-container-escape-attempts/LICENSE create mode 100644 personas/_shared/skills/detecting-container-escape-attempts/SKILL.md create mode 100644 personas/_shared/skills/detecting-container-escape-attempts/assets/template.md create mode 100644 personas/_shared/skills/detecting-container-escape-attempts/references/api-reference.md create mode 100644 personas/_shared/skills/detecting-container-escape-attempts/references/standards.md create mode 100644 personas/_shared/skills/detecting-container-escape-attempts/references/workflows.md create mode 100644 personas/_shared/skills/detecting-container-escape-attempts/scripts/agent.py create mode 100644 personas/_shared/skills/detecting-container-escape-attempts/scripts/process.py create mode 100644 personas/_shared/skills/detecting-container-escape-with-falco-rules/LICENSE create mode 100644 personas/_shared/skills/detecting-container-escape-with-falco-rules/SKILL.md create mode 100644 personas/_shared/skills/detecting-container-escape-with-falco-rules/assets/template.md create mode 100644 personas/_shared/skills/detecting-container-escape-with-falco-rules/references/api-reference.md create mode 100644 personas/_shared/skills/detecting-container-escape-with-falco-rules/references/standards.md create mode 100644 personas/_shared/skills/detecting-container-escape-with-falco-rules/references/workflows.md create mode 100644 personas/_shared/skills/detecting-container-escape-with-falco-rules/scripts/agent.py create mode 100644 personas/_shared/skills/detecting-container-escape-with-falco-rules/scripts/process.py create mode 100644 personas/_shared/skills/detecting-credential-dumping-techniques/LICENSE create mode 100644 personas/_shared/skills/detecting-credential-dumping-techniques/SKILL.es.md create mode 100644 personas/_shared/skills/detecting-credential-dumping-techniques/SKILL.md create mode 100644 personas/_shared/skills/detecting-credential-dumping-techniques/references/api-reference.md create mode 100644 personas/_shared/skills/detecting-credential-dumping-techniques/scripts/agent.py create mode 100644 personas/_shared/skills/detecting-cryptomining-in-cloud/LICENSE create mode 100644 personas/_shared/skills/detecting-cryptomining-in-cloud/SKILL.md create mode 100644 personas/_shared/skills/detecting-cryptomining-in-cloud/references/api-reference.md create mode 100644 personas/_shared/skills/detecting-cryptomining-in-cloud/scripts/agent.py create mode 100644 personas/_shared/skills/detecting-dcsync-attack-in-active-directory/LICENSE create mode 100644 personas/_shared/skills/detecting-dcsync-attack-in-active-directory/SKILL.md create mode 100644 personas/_shared/skills/detecting-dcsync-attack-in-active-directory/assets/template.md create mode 100644 personas/_shared/skills/detecting-dcsync-attack-in-active-directory/references/api-reference.md create mode 100644 personas/_shared/skills/detecting-dcsync-attack-in-active-directory/references/standards.md create mode 100644 personas/_shared/skills/detecting-dcsync-attack-in-active-directory/references/workflows.md create mode 100644 personas/_shared/skills/detecting-dcsync-attack-in-active-directory/scripts/agent.py create mode 100644 personas/_shared/skills/detecting-dcsync-attack-in-active-directory/scripts/process.py create mode 100644 personas/_shared/skills/detecting-deepfake-audio-in-vishing-attacks/LICENSE create mode 100644 personas/_shared/skills/detecting-deepfake-audio-in-vishing-attacks/SKILL.md create mode 100644 personas/_shared/skills/detecting-deepfake-audio-in-vishing-attacks/references/api-reference.md create mode 100644 personas/_shared/skills/detecting-deepfake-audio-in-vishing-attacks/scripts/agent.py create mode 100644 personas/_shared/skills/detecting-dll-sideloading-attacks/LICENSE create mode 100644 personas/_shared/skills/detecting-dll-sideloading-attacks/SKILL.md create mode 100644 personas/_shared/skills/detecting-dll-sideloading-attacks/assets/template.md create mode 100644 personas/_shared/skills/detecting-dll-sideloading-attacks/references/api-reference.md create mode 100644 personas/_shared/skills/detecting-dll-sideloading-attacks/references/standards.md create mode 100644 personas/_shared/skills/detecting-dll-sideloading-attacks/references/workflows.md create mode 100644 personas/_shared/skills/detecting-dll-sideloading-attacks/scripts/agent.py create mode 100644 personas/_shared/skills/detecting-dll-sideloading-attacks/scripts/process.py create mode 100644 personas/_shared/skills/detecting-dnp3-protocol-anomalies/LICENSE create mode 100644 personas/_shared/skills/detecting-dnp3-protocol-anomalies/SKILL.md create mode 100644 personas/_shared/skills/detecting-dnp3-protocol-anomalies/references/api-reference.md create mode 100644 personas/_shared/skills/detecting-dnp3-protocol-anomalies/scripts/agent.py create mode 100644 personas/_shared/skills/detecting-dns-exfiltration-with-dns-query-analysis/LICENSE create mode 100644 personas/_shared/skills/detecting-dns-exfiltration-with-dns-query-analysis/SKILL.md create mode 100644 personas/_shared/skills/detecting-dns-exfiltration-with-dns-query-analysis/references/api-reference.md create mode 100644 personas/_shared/skills/detecting-dns-exfiltration-with-dns-query-analysis/scripts/agent.py create mode 100644 personas/_shared/skills/detecting-email-account-compromise/LICENSE create mode 100644 personas/_shared/skills/detecting-email-account-compromise/SKILL.md create mode 100644 personas/_shared/skills/detecting-email-account-compromise/references/api-reference.md create mode 100644 personas/_shared/skills/detecting-email-account-compromise/scripts/agent.py create mode 100644 personas/_shared/skills/detecting-email-forwarding-rules-attack/LICENSE create mode 100644 personas/_shared/skills/detecting-email-forwarding-rules-attack/SKILL.md create mode 100644 personas/_shared/skills/detecting-email-forwarding-rules-attack/assets/template.md create mode 100644 personas/_shared/skills/detecting-email-forwarding-rules-attack/references/api-reference.md create mode 100644 personas/_shared/skills/detecting-email-forwarding-rules-attack/references/standards.md create mode 100644 personas/_shared/skills/detecting-email-forwarding-rules-attack/references/workflows.md create mode 100644 personas/_shared/skills/detecting-email-forwarding-rules-attack/scripts/agent.py create mode 100644 personas/_shared/skills/detecting-email-forwarding-rules-attack/scripts/process.py create mode 100644 personas/_shared/skills/detecting-evasion-techniques-in-endpoint-logs/LICENSE create mode 100644 personas/_shared/skills/detecting-evasion-techniques-in-endpoint-logs/SKILL.md create mode 100644 personas/_shared/skills/detecting-evasion-techniques-in-endpoint-logs/assets/template.md create mode 100644 personas/_shared/skills/detecting-evasion-techniques-in-endpoint-logs/references/api-reference.md create mode 100644 personas/_shared/skills/detecting-evasion-techniques-in-endpoint-logs/references/standards.md create mode 100644 personas/_shared/skills/detecting-evasion-techniques-in-endpoint-logs/references/workflows.md create mode 100644 personas/_shared/skills/detecting-evasion-techniques-in-endpoint-logs/scripts/agent.py create mode 100644 personas/_shared/skills/detecting-evasion-techniques-in-endpoint-logs/scripts/process.py create mode 100644 personas/_shared/skills/detecting-exfiltration-over-dns-with-zeek/LICENSE create mode 100644 personas/_shared/skills/detecting-exfiltration-over-dns-with-zeek/SKILL.md create mode 100644 personas/_shared/skills/detecting-exfiltration-over-dns-with-zeek/references/api-reference.md create mode 100644 personas/_shared/skills/detecting-exfiltration-over-dns-with-zeek/scripts/agent.py create mode 100644 personas/_shared/skills/detecting-fileless-attacks-on-endpoints/LICENSE create mode 100644 personas/_shared/skills/detecting-fileless-attacks-on-endpoints/SKILL.md create mode 100644 personas/_shared/skills/detecting-fileless-attacks-on-endpoints/assets/template.md create mode 100644 personas/_shared/skills/detecting-fileless-attacks-on-endpoints/references/api-reference.md create mode 100644 personas/_shared/skills/detecting-fileless-attacks-on-endpoints/references/standards.md create mode 100644 personas/_shared/skills/detecting-fileless-attacks-on-endpoints/references/workflows.md create mode 100644 personas/_shared/skills/detecting-fileless-attacks-on-endpoints/scripts/agent.py create mode 100644 personas/_shared/skills/detecting-fileless-attacks-on-endpoints/scripts/process.py create mode 100644 personas/_shared/skills/detecting-fileless-malware-techniques/LICENSE create mode 100644 personas/_shared/skills/detecting-fileless-malware-techniques/SKILL.md create mode 100644 personas/_shared/skills/detecting-fileless-malware-techniques/references/api-reference.md create mode 100644 personas/_shared/skills/detecting-fileless-malware-techniques/scripts/agent.py create mode 100644 personas/_shared/skills/detecting-golden-ticket-attacks-in-kerberos-logs/LICENSE create mode 100644 personas/_shared/skills/detecting-golden-ticket-attacks-in-kerberos-logs/SKILL.md create mode 100644 personas/_shared/skills/detecting-golden-ticket-attacks-in-kerberos-logs/references/api-reference.md create mode 100644 personas/_shared/skills/detecting-golden-ticket-attacks-in-kerberos-logs/scripts/agent.py create mode 100644 personas/_shared/skills/detecting-golden-ticket-forgery/LICENSE create mode 100644 personas/_shared/skills/detecting-golden-ticket-forgery/SKILL.md create mode 100644 personas/_shared/skills/detecting-golden-ticket-forgery/references/api-reference.md create mode 100644 personas/_shared/skills/detecting-golden-ticket-forgery/scripts/agent.py create mode 100644 personas/_shared/skills/detecting-insider-data-exfiltration-via-dlp/LICENSE create mode 100644 personas/_shared/skills/detecting-insider-data-exfiltration-via-dlp/SKILL.md create mode 100644 personas/_shared/skills/detecting-insider-data-exfiltration-via-dlp/references/api-reference.md create mode 100644 personas/_shared/skills/detecting-insider-data-exfiltration-via-dlp/scripts/agent.py create mode 100644 personas/_shared/skills/detecting-insider-threat-behaviors/LICENSE create mode 100644 personas/_shared/skills/detecting-insider-threat-behaviors/SKILL.md create mode 100644 personas/_shared/skills/detecting-insider-threat-behaviors/assets/template.md create mode 100644 personas/_shared/skills/detecting-insider-threat-behaviors/references/api-reference.md create mode 100644 personas/_shared/skills/detecting-insider-threat-behaviors/references/standards.md create mode 100644 personas/_shared/skills/detecting-insider-threat-behaviors/references/workflows.md create mode 100644 personas/_shared/skills/detecting-insider-threat-behaviors/scripts/agent.py create mode 100644 personas/_shared/skills/detecting-insider-threat-behaviors/scripts/process.py create mode 100644 personas/_shared/skills/detecting-insider-threat-with-ueba/LICENSE create mode 100644 personas/_shared/skills/detecting-insider-threat-with-ueba/SKILL.md create mode 100644 personas/_shared/skills/detecting-insider-threat-with-ueba/references/api-reference.md create mode 100644 personas/_shared/skills/detecting-insider-threat-with-ueba/scripts/agent.py create mode 100644 personas/_shared/skills/detecting-kerberoasting-attacks/LICENSE create mode 100644 personas/_shared/skills/detecting-kerberoasting-attacks/SKILL.md create mode 100644 personas/_shared/skills/detecting-kerberoasting-attacks/assets/template.md create mode 100644 personas/_shared/skills/detecting-kerberoasting-attacks/references/api-reference.md create mode 100644 personas/_shared/skills/detecting-kerberoasting-attacks/references/standards.md create mode 100644 personas/_shared/skills/detecting-kerberoasting-attacks/references/workflows.md create mode 100644 personas/_shared/skills/detecting-kerberoasting-attacks/scripts/agent.py create mode 100644 personas/_shared/skills/detecting-kerberoasting-attacks/scripts/process.py create mode 100644 personas/_shared/skills/detecting-lateral-movement-in-network/LICENSE create mode 100644 personas/_shared/skills/detecting-lateral-movement-in-network/SKILL.md create mode 100644 personas/_shared/skills/detecting-lateral-movement-in-network/references/api-reference.md create mode 100644 personas/_shared/skills/detecting-lateral-movement-in-network/scripts/agent.py create mode 100644 personas/_shared/skills/detecting-lateral-movement-with-splunk/LICENSE create mode 100644 personas/_shared/skills/detecting-lateral-movement-with-splunk/SKILL.md create mode 100644 personas/_shared/skills/detecting-lateral-movement-with-splunk/assets/template.md create mode 100644 personas/_shared/skills/detecting-lateral-movement-with-splunk/references/api-reference.md create mode 100644 personas/_shared/skills/detecting-lateral-movement-with-splunk/references/standards.md create mode 100644 personas/_shared/skills/detecting-lateral-movement-with-splunk/references/workflows.md create mode 100644 personas/_shared/skills/detecting-lateral-movement-with-splunk/scripts/agent.py create mode 100644 personas/_shared/skills/detecting-lateral-movement-with-splunk/scripts/process.py create mode 100644 personas/_shared/skills/detecting-lateral-movement-with-zeek/LICENSE create mode 100644 personas/_shared/skills/detecting-lateral-movement-with-zeek/SKILL.md create mode 100644 personas/_shared/skills/detecting-lateral-movement-with-zeek/assets/template.md create mode 100644 personas/_shared/skills/detecting-lateral-movement-with-zeek/references/api-reference.md create mode 100644 personas/_shared/skills/detecting-lateral-movement-with-zeek/references/standards.md create mode 100644 personas/_shared/skills/detecting-lateral-movement-with-zeek/references/workflows.md create mode 100644 personas/_shared/skills/detecting-lateral-movement-with-zeek/scripts/agent.py create mode 100644 personas/_shared/skills/detecting-lateral-movement-with-zeek/scripts/process.py create mode 100644 personas/_shared/skills/detecting-living-off-the-land-attacks/LICENSE create mode 100644 personas/_shared/skills/detecting-living-off-the-land-attacks/SKILL.md create mode 100644 personas/_shared/skills/detecting-living-off-the-land-attacks/references/api-reference.md create mode 100644 personas/_shared/skills/detecting-living-off-the-land-attacks/scripts/agent.py create mode 100644 personas/_shared/skills/detecting-living-off-the-land-with-lolbas/LICENSE create mode 100644 personas/_shared/skills/detecting-living-off-the-land-with-lolbas/SKILL.md create mode 100644 personas/_shared/skills/detecting-living-off-the-land-with-lolbas/references/api-reference.md create mode 100644 personas/_shared/skills/detecting-living-off-the-land-with-lolbas/scripts/agent.py create mode 100644 personas/_shared/skills/detecting-malicious-scheduled-tasks-with-sysmon/LICENSE create mode 100644 personas/_shared/skills/detecting-malicious-scheduled-tasks-with-sysmon/SKILL.md create mode 100644 personas/_shared/skills/detecting-malicious-scheduled-tasks-with-sysmon/references/api-reference.md create mode 100644 personas/_shared/skills/detecting-malicious-scheduled-tasks-with-sysmon/scripts/agent.py create mode 100644 personas/_shared/skills/detecting-mimikatz-execution-patterns/LICENSE create mode 100644 personas/_shared/skills/detecting-mimikatz-execution-patterns/SKILL.md create mode 100644 personas/_shared/skills/detecting-mimikatz-execution-patterns/assets/template.md create mode 100644 personas/_shared/skills/detecting-mimikatz-execution-patterns/references/api-reference.md create mode 100644 personas/_shared/skills/detecting-mimikatz-execution-patterns/references/standards.md create mode 100644 personas/_shared/skills/detecting-mimikatz-execution-patterns/references/workflows.md create mode 100644 personas/_shared/skills/detecting-mimikatz-execution-patterns/scripts/agent.py create mode 100644 personas/_shared/skills/detecting-mimikatz-execution-patterns/scripts/process.py create mode 100644 personas/_shared/skills/detecting-misconfigured-azure-storage/LICENSE create mode 100644 personas/_shared/skills/detecting-misconfigured-azure-storage/SKILL.md create mode 100644 personas/_shared/skills/detecting-misconfigured-azure-storage/references/api-reference.md create mode 100644 personas/_shared/skills/detecting-misconfigured-azure-storage/scripts/agent.py create mode 100644 personas/_shared/skills/detecting-mobile-malware-behavior/LICENSE create mode 100644 personas/_shared/skills/detecting-mobile-malware-behavior/SKILL.md create mode 100644 personas/_shared/skills/detecting-mobile-malware-behavior/assets/template.md create mode 100644 personas/_shared/skills/detecting-mobile-malware-behavior/references/api-reference.md create mode 100644 personas/_shared/skills/detecting-mobile-malware-behavior/references/standards.md create mode 100644 personas/_shared/skills/detecting-mobile-malware-behavior/references/workflows.md create mode 100644 personas/_shared/skills/detecting-mobile-malware-behavior/scripts/agent.py create mode 100644 personas/_shared/skills/detecting-mobile-malware-behavior/scripts/process.py create mode 100644 personas/_shared/skills/detecting-modbus-command-injection-attacks/LICENSE create mode 100644 personas/_shared/skills/detecting-modbus-command-injection-attacks/SKILL.md create mode 100644 personas/_shared/skills/detecting-modbus-command-injection-attacks/references/api-reference.md create mode 100644 personas/_shared/skills/detecting-modbus-command-injection-attacks/scripts/agent.py create mode 100644 personas/_shared/skills/detecting-modbus-protocol-anomalies/LICENSE create mode 100644 personas/_shared/skills/detecting-modbus-protocol-anomalies/SKILL.md create mode 100644 personas/_shared/skills/detecting-modbus-protocol-anomalies/references/api-reference.md create mode 100644 personas/_shared/skills/detecting-modbus-protocol-anomalies/scripts/agent.py create mode 100644 personas/_shared/skills/detecting-network-anomalies-with-zeek/LICENSE create mode 100644 personas/_shared/skills/detecting-network-anomalies-with-zeek/SKILL.md create mode 100644 personas/_shared/skills/detecting-network-anomalies-with-zeek/references/api-reference.md create mode 100644 personas/_shared/skills/detecting-network-anomalies-with-zeek/scripts/agent.py create mode 100644 personas/_shared/skills/detecting-network-scanning-with-ids-signatures/LICENSE create mode 100644 personas/_shared/skills/detecting-network-scanning-with-ids-signatures/SKILL.md create mode 100644 personas/_shared/skills/detecting-network-scanning-with-ids-signatures/references/api-reference.md create mode 100644 personas/_shared/skills/detecting-network-scanning-with-ids-signatures/scripts/agent.py create mode 100644 personas/_shared/skills/detecting-ntlm-relay-with-event-correlation/LICENSE create mode 100644 personas/_shared/skills/detecting-ntlm-relay-with-event-correlation/SKILL.md create mode 100644 personas/_shared/skills/detecting-ntlm-relay-with-event-correlation/references/api-reference.md create mode 100644 personas/_shared/skills/detecting-ntlm-relay-with-event-correlation/scripts/agent.py create mode 100644 personas/_shared/skills/detecting-ntlm-relay-with-event-correlation/scripts/audit_smb_signing.ps1 create mode 100644 personas/_shared/skills/detecting-ntlm-relay-with-event-correlation/scripts/detect_ntlm_relay.py create mode 100644 personas/_shared/skills/detecting-oauth-token-theft/LICENSE create mode 100644 personas/_shared/skills/detecting-oauth-token-theft/SKILL.md create mode 100644 personas/_shared/skills/detecting-oauth-token-theft/references/api-reference.md create mode 100644 personas/_shared/skills/detecting-oauth-token-theft/scripts/agent.py create mode 100644 personas/_shared/skills/detecting-pass-the-hash-attacks/LICENSE create mode 100644 personas/_shared/skills/detecting-pass-the-hash-attacks/SKILL.md create mode 100644 personas/_shared/skills/detecting-pass-the-hash-attacks/assets/template.md create mode 100644 personas/_shared/skills/detecting-pass-the-hash-attacks/references/api-reference.md create mode 100644 personas/_shared/skills/detecting-pass-the-hash-attacks/references/standards.md create mode 100644 personas/_shared/skills/detecting-pass-the-hash-attacks/references/workflows.md create mode 100644 personas/_shared/skills/detecting-pass-the-hash-attacks/scripts/agent.py create mode 100644 personas/_shared/skills/detecting-pass-the-hash-attacks/scripts/process.py create mode 100644 personas/_shared/skills/detecting-pass-the-ticket-attacks/LICENSE create mode 100644 personas/_shared/skills/detecting-pass-the-ticket-attacks/SKILL.md create mode 100644 personas/_shared/skills/detecting-pass-the-ticket-attacks/references/api-reference.md create mode 100644 personas/_shared/skills/detecting-pass-the-ticket-attacks/scripts/agent.py create mode 100644 personas/_shared/skills/detecting-port-scanning-with-fail2ban/LICENSE create mode 100644 personas/_shared/skills/detecting-port-scanning-with-fail2ban/SKILL.md create mode 100644 personas/_shared/skills/detecting-port-scanning-with-fail2ban/references/api-reference.md create mode 100644 personas/_shared/skills/detecting-port-scanning-with-fail2ban/scripts/agent.py create mode 100644 personas/_shared/skills/detecting-privilege-escalation-attempts/LICENSE create mode 100644 personas/_shared/skills/detecting-privilege-escalation-attempts/SKILL.md create mode 100644 personas/_shared/skills/detecting-privilege-escalation-attempts/assets/template.md create mode 100644 personas/_shared/skills/detecting-privilege-escalation-attempts/references/api-reference.md create mode 100644 personas/_shared/skills/detecting-privilege-escalation-attempts/references/standards.md create mode 100644 personas/_shared/skills/detecting-privilege-escalation-attempts/references/workflows.md create mode 100644 personas/_shared/skills/detecting-privilege-escalation-attempts/scripts/agent.py create mode 100644 personas/_shared/skills/detecting-privilege-escalation-attempts/scripts/process.py create mode 100644 personas/_shared/skills/detecting-privilege-escalation-in-kubernetes-pods/LICENSE create mode 100644 personas/_shared/skills/detecting-privilege-escalation-in-kubernetes-pods/SKILL.md create mode 100644 personas/_shared/skills/detecting-privilege-escalation-in-kubernetes-pods/assets/template.md create mode 100644 personas/_shared/skills/detecting-privilege-escalation-in-kubernetes-pods/references/api-reference.md create mode 100644 personas/_shared/skills/detecting-privilege-escalation-in-kubernetes-pods/references/standards.md create mode 100644 personas/_shared/skills/detecting-privilege-escalation-in-kubernetes-pods/references/workflows.md create mode 100644 personas/_shared/skills/detecting-privilege-escalation-in-kubernetes-pods/scripts/agent.py create mode 100644 personas/_shared/skills/detecting-privilege-escalation-in-kubernetes-pods/scripts/process.py create mode 100644 personas/_shared/skills/detecting-process-hollowing-technique/LICENSE create mode 100644 personas/_shared/skills/detecting-process-hollowing-technique/SKILL.md create mode 100644 personas/_shared/skills/detecting-process-hollowing-technique/assets/template.md create mode 100644 personas/_shared/skills/detecting-process-hollowing-technique/references/api-reference.md create mode 100644 personas/_shared/skills/detecting-process-hollowing-technique/references/standards.md create mode 100644 personas/_shared/skills/detecting-process-hollowing-technique/references/workflows.md create mode 100644 personas/_shared/skills/detecting-process-hollowing-technique/scripts/agent.py create mode 100644 personas/_shared/skills/detecting-process-hollowing-technique/scripts/process.py create mode 100644 personas/_shared/skills/detecting-process-injection-techniques/LICENSE create mode 100644 personas/_shared/skills/detecting-process-injection-techniques/SKILL.md create mode 100644 personas/_shared/skills/detecting-process-injection-techniques/references/api-reference.md create mode 100644 personas/_shared/skills/detecting-process-injection-techniques/scripts/agent.py create mode 100644 personas/_shared/skills/detecting-qr-code-phishing-with-email-security/LICENSE create mode 100644 personas/_shared/skills/detecting-qr-code-phishing-with-email-security/SKILL.md create mode 100644 personas/_shared/skills/detecting-qr-code-phishing-with-email-security/assets/template.md create mode 100644 personas/_shared/skills/detecting-qr-code-phishing-with-email-security/references/api-reference.md create mode 100644 personas/_shared/skills/detecting-qr-code-phishing-with-email-security/references/standards.md create mode 100644 personas/_shared/skills/detecting-qr-code-phishing-with-email-security/references/workflows.md create mode 100644 personas/_shared/skills/detecting-qr-code-phishing-with-email-security/scripts/agent.py create mode 100644 personas/_shared/skills/detecting-qr-code-phishing-with-email-security/scripts/process.py create mode 100644 personas/_shared/skills/detecting-ransomware-encryption-behavior/LICENSE create mode 100644 personas/_shared/skills/detecting-ransomware-encryption-behavior/SKILL.md create mode 100644 personas/_shared/skills/detecting-ransomware-encryption-behavior/references/api-reference.md create mode 100644 personas/_shared/skills/detecting-ransomware-encryption-behavior/scripts/agent.py create mode 100644 personas/_shared/skills/detecting-ransomware-precursors-in-network/LICENSE create mode 100644 personas/_shared/skills/detecting-ransomware-precursors-in-network/SKILL.md create mode 100644 personas/_shared/skills/detecting-ransomware-precursors-in-network/assets/template.md create mode 100644 personas/_shared/skills/detecting-ransomware-precursors-in-network/references/api-reference.md create mode 100644 personas/_shared/skills/detecting-ransomware-precursors-in-network/references/standards.md create mode 100644 personas/_shared/skills/detecting-ransomware-precursors-in-network/references/workflows.md create mode 100644 personas/_shared/skills/detecting-ransomware-precursors-in-network/scripts/agent.py create mode 100644 personas/_shared/skills/detecting-ransomware-precursors-in-network/scripts/process.py create mode 100644 personas/_shared/skills/detecting-rdp-brute-force-attacks/LICENSE create mode 100644 personas/_shared/skills/detecting-rdp-brute-force-attacks/SKILL.md create mode 100644 personas/_shared/skills/detecting-rdp-brute-force-attacks/references/api-reference.md create mode 100644 personas/_shared/skills/detecting-rdp-brute-force-attacks/scripts/agent.py create mode 100644 personas/_shared/skills/detecting-rootkit-activity/LICENSE create mode 100644 personas/_shared/skills/detecting-rootkit-activity/SKILL.md create mode 100644 personas/_shared/skills/detecting-rootkit-activity/references/api-reference.md create mode 100644 personas/_shared/skills/detecting-rootkit-activity/scripts/agent.py create mode 100644 personas/_shared/skills/detecting-s3-data-exfiltration-attempts/LICENSE create mode 100644 personas/_shared/skills/detecting-s3-data-exfiltration-attempts/SKILL.md create mode 100644 personas/_shared/skills/detecting-s3-data-exfiltration-attempts/references/api-reference.md create mode 100644 personas/_shared/skills/detecting-s3-data-exfiltration-attempts/scripts/agent.py create mode 100644 personas/_shared/skills/detecting-serverless-function-injection/LICENSE create mode 100644 personas/_shared/skills/detecting-serverless-function-injection/SKILL.md create mode 100644 personas/_shared/skills/detecting-serverless-function-injection/references/api-reference.md create mode 100644 personas/_shared/skills/detecting-serverless-function-injection/scripts/agent.py create mode 100644 personas/_shared/skills/detecting-service-account-abuse/LICENSE create mode 100644 personas/_shared/skills/detecting-service-account-abuse/SKILL.md create mode 100644 personas/_shared/skills/detecting-service-account-abuse/assets/template.md create mode 100644 personas/_shared/skills/detecting-service-account-abuse/references/api-reference.md create mode 100644 personas/_shared/skills/detecting-service-account-abuse/references/standards.md create mode 100644 personas/_shared/skills/detecting-service-account-abuse/references/workflows.md create mode 100644 personas/_shared/skills/detecting-service-account-abuse/scripts/agent.py create mode 100644 personas/_shared/skills/detecting-service-account-abuse/scripts/process.py create mode 100644 personas/_shared/skills/detecting-shadow-api-endpoints/LICENSE create mode 100644 personas/_shared/skills/detecting-shadow-api-endpoints/SKILL.md create mode 100644 personas/_shared/skills/detecting-shadow-api-endpoints/references/api-reference.md create mode 100644 personas/_shared/skills/detecting-shadow-api-endpoints/scripts/agent.py create mode 100644 personas/_shared/skills/detecting-shadow-it-cloud-usage/LICENSE create mode 100644 personas/_shared/skills/detecting-shadow-it-cloud-usage/SKILL.md create mode 100644 personas/_shared/skills/detecting-shadow-it-cloud-usage/references/api-reference.md create mode 100644 personas/_shared/skills/detecting-shadow-it-cloud-usage/scripts/agent.py create mode 100644 personas/_shared/skills/detecting-spearphishing-with-email-gateway/LICENSE create mode 100644 personas/_shared/skills/detecting-spearphishing-with-email-gateway/SKILL.md create mode 100644 personas/_shared/skills/detecting-spearphishing-with-email-gateway/assets/template.md create mode 100644 personas/_shared/skills/detecting-spearphishing-with-email-gateway/references/api-reference.md create mode 100644 personas/_shared/skills/detecting-spearphishing-with-email-gateway/references/standards.md create mode 100644 personas/_shared/skills/detecting-spearphishing-with-email-gateway/references/workflows.md create mode 100644 personas/_shared/skills/detecting-spearphishing-with-email-gateway/scripts/agent.py create mode 100644 personas/_shared/skills/detecting-spearphishing-with-email-gateway/scripts/process.py create mode 100644 personas/_shared/skills/detecting-sql-injection-via-waf-logs/LICENSE create mode 100644 personas/_shared/skills/detecting-sql-injection-via-waf-logs/SKILL.md create mode 100644 personas/_shared/skills/detecting-sql-injection-via-waf-logs/references/api-reference.md create mode 100644 personas/_shared/skills/detecting-sql-injection-via-waf-logs/scripts/agent.py create mode 100644 personas/_shared/skills/detecting-stuxnet-style-attacks/LICENSE create mode 100644 personas/_shared/skills/detecting-stuxnet-style-attacks/SKILL.md create mode 100644 personas/_shared/skills/detecting-stuxnet-style-attacks/references/api-reference.md create mode 100644 personas/_shared/skills/detecting-stuxnet-style-attacks/scripts/agent.py create mode 100644 personas/_shared/skills/detecting-supply-chain-attacks-in-ci-cd/LICENSE create mode 100644 personas/_shared/skills/detecting-supply-chain-attacks-in-ci-cd/SKILL.md create mode 100644 personas/_shared/skills/detecting-supply-chain-attacks-in-ci-cd/references/api-reference.md create mode 100644 personas/_shared/skills/detecting-supply-chain-attacks-in-ci-cd/scripts/agent.py create mode 100644 personas/_shared/skills/detecting-suspicious-oauth-application-consent/LICENSE create mode 100644 personas/_shared/skills/detecting-suspicious-oauth-application-consent/SKILL.md create mode 100644 personas/_shared/skills/detecting-suspicious-oauth-application-consent/references/api-reference.md create mode 100644 personas/_shared/skills/detecting-suspicious-oauth-application-consent/scripts/agent.py create mode 100644 personas/_shared/skills/detecting-suspicious-powershell-execution/LICENSE create mode 100644 personas/_shared/skills/detecting-suspicious-powershell-execution/SKILL.md create mode 100644 personas/_shared/skills/detecting-suspicious-powershell-execution/assets/template.md create mode 100644 personas/_shared/skills/detecting-suspicious-powershell-execution/references/api-reference.md create mode 100644 personas/_shared/skills/detecting-suspicious-powershell-execution/references/standards.md create mode 100644 personas/_shared/skills/detecting-suspicious-powershell-execution/references/workflows.md create mode 100644 personas/_shared/skills/detecting-suspicious-powershell-execution/scripts/agent.py create mode 100644 personas/_shared/skills/detecting-suspicious-powershell-execution/scripts/process.py create mode 100644 personas/_shared/skills/detecting-t1003-credential-dumping-with-edr/LICENSE create mode 100644 personas/_shared/skills/detecting-t1003-credential-dumping-with-edr/SKILL.md create mode 100644 personas/_shared/skills/detecting-t1003-credential-dumping-with-edr/assets/template.md create mode 100644 personas/_shared/skills/detecting-t1003-credential-dumping-with-edr/references/api-reference.md create mode 100644 personas/_shared/skills/detecting-t1003-credential-dumping-with-edr/references/standards.md create mode 100644 personas/_shared/skills/detecting-t1003-credential-dumping-with-edr/references/workflows.md create mode 100644 personas/_shared/skills/detecting-t1003-credential-dumping-with-edr/scripts/agent.py create mode 100644 personas/_shared/skills/detecting-t1003-credential-dumping-with-edr/scripts/process.py create mode 100644 personas/_shared/skills/detecting-t1055-process-injection-with-sysmon/LICENSE create mode 100644 personas/_shared/skills/detecting-t1055-process-injection-with-sysmon/SKILL.md create mode 100644 personas/_shared/skills/detecting-t1055-process-injection-with-sysmon/assets/template.md create mode 100644 personas/_shared/skills/detecting-t1055-process-injection-with-sysmon/references/api-reference.md create mode 100644 personas/_shared/skills/detecting-t1055-process-injection-with-sysmon/references/standards.md create mode 100644 personas/_shared/skills/detecting-t1055-process-injection-with-sysmon/references/workflows.md create mode 100644 personas/_shared/skills/detecting-t1055-process-injection-with-sysmon/scripts/agent.py create mode 100644 personas/_shared/skills/detecting-t1055-process-injection-with-sysmon/scripts/process.py create mode 100644 personas/_shared/skills/detecting-t1548-abuse-elevation-control-mechanism/LICENSE create mode 100644 personas/_shared/skills/detecting-t1548-abuse-elevation-control-mechanism/SKILL.md create mode 100644 personas/_shared/skills/detecting-t1548-abuse-elevation-control-mechanism/assets/template.md create mode 100644 personas/_shared/skills/detecting-t1548-abuse-elevation-control-mechanism/references/api-reference.md create mode 100644 personas/_shared/skills/detecting-t1548-abuse-elevation-control-mechanism/references/standards.md create mode 100644 personas/_shared/skills/detecting-t1548-abuse-elevation-control-mechanism/references/workflows.md create mode 100644 personas/_shared/skills/detecting-t1548-abuse-elevation-control-mechanism/scripts/agent.py create mode 100644 personas/_shared/skills/detecting-t1548-abuse-elevation-control-mechanism/scripts/process.py create mode 100644 personas/_shared/skills/detecting-typosquatting-packages-in-npm-pypi/LICENSE create mode 100644 personas/_shared/skills/detecting-typosquatting-packages-in-npm-pypi/SKILL.md create mode 100644 personas/_shared/skills/detecting-typosquatting-packages-in-npm-pypi/references/api-reference.md create mode 100644 personas/_shared/skills/detecting-typosquatting-packages-in-npm-pypi/scripts/agent.py create mode 100644 personas/_shared/skills/detecting-wmi-persistence/LICENSE create mode 100644 personas/_shared/skills/detecting-wmi-persistence/SKILL.md create mode 100644 personas/_shared/skills/detecting-wmi-persistence/references/api-reference.md create mode 100644 personas/_shared/skills/detecting-wmi-persistence/scripts/agent.py create mode 100644 personas/_shared/skills/eradicating-malware-from-infected-systems/LICENSE create mode 100644 personas/_shared/skills/eradicating-malware-from-infected-systems/SKILL.md create mode 100644 personas/_shared/skills/eradicating-malware-from-infected-systems/assets/template.md create mode 100644 personas/_shared/skills/eradicating-malware-from-infected-systems/references/api-reference.md create mode 100644 personas/_shared/skills/eradicating-malware-from-infected-systems/references/standards.md create mode 100644 personas/_shared/skills/eradicating-malware-from-infected-systems/references/workflows.md create mode 100644 personas/_shared/skills/eradicating-malware-from-infected-systems/scripts/agent.py create mode 100644 personas/_shared/skills/eradicating-malware-from-infected-systems/scripts/process.py create mode 100644 personas/_shared/skills/evaluating-threat-intelligence-platforms/LICENSE create mode 100644 personas/_shared/skills/evaluating-threat-intelligence-platforms/SKILL.md create mode 100644 personas/_shared/skills/evaluating-threat-intelligence-platforms/references/api-reference.md create mode 100644 personas/_shared/skills/evaluating-threat-intelligence-platforms/scripts/agent.py create mode 100644 personas/_shared/skills/executing-active-directory-attack-simulation/LICENSE create mode 100644 personas/_shared/skills/executing-active-directory-attack-simulation/SKILL.md create mode 100644 personas/_shared/skills/executing-active-directory-attack-simulation/references/api-reference.md create mode 100644 personas/_shared/skills/executing-active-directory-attack-simulation/scripts/agent.py create mode 100644 personas/_shared/skills/executing-phishing-simulation-campaign/LICENSE create mode 100644 personas/_shared/skills/executing-phishing-simulation-campaign/SKILL.md create mode 100644 personas/_shared/skills/executing-phishing-simulation-campaign/references/api-reference.md create mode 100644 personas/_shared/skills/executing-phishing-simulation-campaign/scripts/agent.py create mode 100644 personas/_shared/skills/executing-red-team-engagement-planning/LICENSE create mode 100644 personas/_shared/skills/executing-red-team-engagement-planning/SKILL.md create mode 100644 personas/_shared/skills/executing-red-team-engagement-planning/assets/template.md create mode 100644 personas/_shared/skills/executing-red-team-engagement-planning/references/api-reference.md create mode 100644 personas/_shared/skills/executing-red-team-engagement-planning/references/standards.md create mode 100644 personas/_shared/skills/executing-red-team-engagement-planning/references/workflows.md create mode 100644 personas/_shared/skills/executing-red-team-engagement-planning/scripts/agent.py create mode 100644 personas/_shared/skills/executing-red-team-engagement-planning/scripts/process.py create mode 100644 personas/_shared/skills/executing-red-team-exercise/LICENSE create mode 100644 personas/_shared/skills/executing-red-team-exercise/SKILL.md create mode 100644 personas/_shared/skills/executing-red-team-exercise/references/api-reference.md create mode 100644 personas/_shared/skills/executing-red-team-exercise/scripts/agent.py create mode 100644 personas/_shared/skills/exploiting-active-directory-certificate-services-esc1/LICENSE create mode 100644 personas/_shared/skills/exploiting-active-directory-certificate-services-esc1/SKILL.md create mode 100644 personas/_shared/skills/exploiting-active-directory-certificate-services-esc1/assets/template.md create mode 100644 personas/_shared/skills/exploiting-active-directory-certificate-services-esc1/references/api-reference.md create mode 100644 personas/_shared/skills/exploiting-active-directory-certificate-services-esc1/references/standards.md create mode 100644 personas/_shared/skills/exploiting-active-directory-certificate-services-esc1/references/workflows.md create mode 100644 personas/_shared/skills/exploiting-active-directory-certificate-services-esc1/scripts/agent.py create mode 100644 personas/_shared/skills/exploiting-active-directory-certificate-services-esc1/scripts/process.py create mode 100644 personas/_shared/skills/exploiting-active-directory-with-bloodhound/LICENSE create mode 100644 personas/_shared/skills/exploiting-active-directory-with-bloodhound/SKILL.md create mode 100644 personas/_shared/skills/exploiting-active-directory-with-bloodhound/assets/template.md create mode 100644 personas/_shared/skills/exploiting-active-directory-with-bloodhound/references/api-reference.md create mode 100644 personas/_shared/skills/exploiting-active-directory-with-bloodhound/references/standards.md create mode 100644 personas/_shared/skills/exploiting-active-directory-with-bloodhound/references/workflows.md create mode 100644 personas/_shared/skills/exploiting-active-directory-with-bloodhound/scripts/agent.py create mode 100644 personas/_shared/skills/exploiting-active-directory-with-bloodhound/scripts/process.py create mode 100644 personas/_shared/skills/exploiting-api-injection-vulnerabilities/LICENSE create mode 100644 personas/_shared/skills/exploiting-api-injection-vulnerabilities/SKILL.md create mode 100644 personas/_shared/skills/exploiting-api-injection-vulnerabilities/references/api-reference.md create mode 100644 personas/_shared/skills/exploiting-api-injection-vulnerabilities/scripts/agent.py create mode 100644 personas/_shared/skills/exploiting-bgp-hijacking-vulnerabilities/LICENSE create mode 100644 personas/_shared/skills/exploiting-bgp-hijacking-vulnerabilities/SKILL.md create mode 100644 personas/_shared/skills/exploiting-bgp-hijacking-vulnerabilities/references/api-reference.md create mode 100644 personas/_shared/skills/exploiting-bgp-hijacking-vulnerabilities/scripts/agent.py create mode 100644 personas/_shared/skills/exploiting-broken-function-level-authorization/LICENSE create mode 100644 personas/_shared/skills/exploiting-broken-function-level-authorization/SKILL.md create mode 100644 personas/_shared/skills/exploiting-broken-function-level-authorization/references/api-reference.md create mode 100644 personas/_shared/skills/exploiting-broken-function-level-authorization/scripts/agent.py create mode 100644 personas/_shared/skills/exploiting-broken-link-hijacking/LICENSE create mode 100644 personas/_shared/skills/exploiting-broken-link-hijacking/SKILL.md create mode 100644 personas/_shared/skills/exploiting-broken-link-hijacking/references/api-reference.md create mode 100644 personas/_shared/skills/exploiting-broken-link-hijacking/scripts/agent.py create mode 100644 personas/_shared/skills/exploiting-constrained-delegation-abuse/LICENSE create mode 100644 personas/_shared/skills/exploiting-constrained-delegation-abuse/SKILL.md create mode 100644 personas/_shared/skills/exploiting-constrained-delegation-abuse/references/api-reference.md create mode 100644 personas/_shared/skills/exploiting-constrained-delegation-abuse/references/standards.md create mode 100644 personas/_shared/skills/exploiting-constrained-delegation-abuse/references/workflows.md create mode 100644 personas/_shared/skills/exploiting-constrained-delegation-abuse/scripts/agent.py create mode 100644 personas/_shared/skills/exploiting-deeplink-vulnerabilities/LICENSE create mode 100644 personas/_shared/skills/exploiting-deeplink-vulnerabilities/SKILL.md create mode 100644 personas/_shared/skills/exploiting-deeplink-vulnerabilities/assets/template.md create mode 100644 personas/_shared/skills/exploiting-deeplink-vulnerabilities/references/api-reference.md create mode 100644 personas/_shared/skills/exploiting-deeplink-vulnerabilities/references/standards.md create mode 100644 personas/_shared/skills/exploiting-deeplink-vulnerabilities/references/workflows.md create mode 100644 personas/_shared/skills/exploiting-deeplink-vulnerabilities/scripts/agent.py create mode 100644 personas/_shared/skills/exploiting-deeplink-vulnerabilities/scripts/process.py create mode 100644 personas/_shared/skills/exploiting-excessive-data-exposure-in-api/LICENSE create mode 100644 personas/_shared/skills/exploiting-excessive-data-exposure-in-api/SKILL.md create mode 100644 personas/_shared/skills/exploiting-excessive-data-exposure-in-api/references/api-reference.md create mode 100644 personas/_shared/skills/exploiting-excessive-data-exposure-in-api/scripts/agent.py create mode 100644 personas/_shared/skills/exploiting-http-request-smuggling/LICENSE create mode 100644 personas/_shared/skills/exploiting-http-request-smuggling/SKILL.md create mode 100644 personas/_shared/skills/exploiting-http-request-smuggling/references/api-reference.md create mode 100644 personas/_shared/skills/exploiting-http-request-smuggling/scripts/agent.py create mode 100644 personas/_shared/skills/exploiting-idor-vulnerabilities/LICENSE create mode 100644 personas/_shared/skills/exploiting-idor-vulnerabilities/SKILL.md create mode 100644 personas/_shared/skills/exploiting-idor-vulnerabilities/references/api-reference.md create mode 100644 personas/_shared/skills/exploiting-idor-vulnerabilities/scripts/agent.py create mode 100644 personas/_shared/skills/exploiting-insecure-data-storage-in-mobile/LICENSE create mode 100644 personas/_shared/skills/exploiting-insecure-data-storage-in-mobile/SKILL.md create mode 100644 personas/_shared/skills/exploiting-insecure-data-storage-in-mobile/assets/template.md create mode 100644 personas/_shared/skills/exploiting-insecure-data-storage-in-mobile/references/api-reference.md create mode 100644 personas/_shared/skills/exploiting-insecure-data-storage-in-mobile/references/standards.md create mode 100644 personas/_shared/skills/exploiting-insecure-data-storage-in-mobile/references/workflows.md create mode 100644 personas/_shared/skills/exploiting-insecure-data-storage-in-mobile/scripts/agent.py create mode 100644 personas/_shared/skills/exploiting-insecure-data-storage-in-mobile/scripts/process.py create mode 100644 personas/_shared/skills/exploiting-insecure-deserialization/LICENSE create mode 100644 personas/_shared/skills/exploiting-insecure-deserialization/SKILL.md create mode 100644 personas/_shared/skills/exploiting-insecure-deserialization/references/api-reference.md create mode 100644 personas/_shared/skills/exploiting-insecure-deserialization/scripts/agent.py create mode 100644 personas/_shared/skills/exploiting-ipv6-vulnerabilities/LICENSE create mode 100644 personas/_shared/skills/exploiting-ipv6-vulnerabilities/SKILL.md create mode 100644 personas/_shared/skills/exploiting-ipv6-vulnerabilities/references/api-reference.md create mode 100644 personas/_shared/skills/exploiting-ipv6-vulnerabilities/scripts/agent.py create mode 100644 personas/_shared/skills/exploiting-jwt-algorithm-confusion-attack/LICENSE create mode 100644 personas/_shared/skills/exploiting-jwt-algorithm-confusion-attack/SKILL.md create mode 100644 personas/_shared/skills/exploiting-jwt-algorithm-confusion-attack/references/api-reference.md create mode 100644 personas/_shared/skills/exploiting-jwt-algorithm-confusion-attack/scripts/agent.py create mode 100644 personas/_shared/skills/exploiting-kerberoasting-with-impacket/LICENSE create mode 100644 personas/_shared/skills/exploiting-kerberoasting-with-impacket/SKILL.md create mode 100644 personas/_shared/skills/exploiting-kerberoasting-with-impacket/assets/template.md create mode 100644 personas/_shared/skills/exploiting-kerberoasting-with-impacket/references/api-reference.md create mode 100644 personas/_shared/skills/exploiting-kerberoasting-with-impacket/references/standards.md create mode 100644 personas/_shared/skills/exploiting-kerberoasting-with-impacket/references/workflows.md create mode 100644 personas/_shared/skills/exploiting-kerberoasting-with-impacket/scripts/agent.py create mode 100644 personas/_shared/skills/exploiting-kerberoasting-with-impacket/scripts/process.py create mode 100644 personas/_shared/skills/exploiting-mass-assignment-in-rest-apis/LICENSE create mode 100644 personas/_shared/skills/exploiting-mass-assignment-in-rest-apis/SKILL.md create mode 100644 personas/_shared/skills/exploiting-mass-assignment-in-rest-apis/references/api-reference.md create mode 100644 personas/_shared/skills/exploiting-mass-assignment-in-rest-apis/scripts/agent.py create mode 100644 personas/_shared/skills/exploiting-ms17-010-eternalblue-vulnerability/LICENSE create mode 100644 personas/_shared/skills/exploiting-ms17-010-eternalblue-vulnerability/SKILL.md create mode 100644 personas/_shared/skills/exploiting-ms17-010-eternalblue-vulnerability/assets/template.md create mode 100644 personas/_shared/skills/exploiting-ms17-010-eternalblue-vulnerability/references/api-reference.md create mode 100644 personas/_shared/skills/exploiting-ms17-010-eternalblue-vulnerability/references/standards.md create mode 100644 personas/_shared/skills/exploiting-ms17-010-eternalblue-vulnerability/references/workflows.md create mode 100644 personas/_shared/skills/exploiting-ms17-010-eternalblue-vulnerability/scripts/agent.py create mode 100644 personas/_shared/skills/exploiting-ms17-010-eternalblue-vulnerability/scripts/process.py create mode 100644 personas/_shared/skills/exploiting-nopac-cve-2021-42278-42287/LICENSE create mode 100644 personas/_shared/skills/exploiting-nopac-cve-2021-42278-42287/SKILL.md create mode 100644 personas/_shared/skills/exploiting-nopac-cve-2021-42278-42287/assets/template.md create mode 100644 personas/_shared/skills/exploiting-nopac-cve-2021-42278-42287/references/api-reference.md create mode 100644 personas/_shared/skills/exploiting-nopac-cve-2021-42278-42287/references/standards.md create mode 100644 personas/_shared/skills/exploiting-nopac-cve-2021-42278-42287/references/workflows.md create mode 100644 personas/_shared/skills/exploiting-nopac-cve-2021-42278-42287/scripts/agent.py create mode 100644 personas/_shared/skills/exploiting-nopac-cve-2021-42278-42287/scripts/process.py create mode 100644 personas/_shared/skills/exploiting-nosql-injection-vulnerabilities/LICENSE create mode 100644 personas/_shared/skills/exploiting-nosql-injection-vulnerabilities/SKILL.md create mode 100644 personas/_shared/skills/exploiting-nosql-injection-vulnerabilities/assets/template.md create mode 100644 personas/_shared/skills/exploiting-nosql-injection-vulnerabilities/references/api-reference.md create mode 100644 personas/_shared/skills/exploiting-nosql-injection-vulnerabilities/references/standards.md create mode 100644 personas/_shared/skills/exploiting-nosql-injection-vulnerabilities/references/workflows.md create mode 100644 personas/_shared/skills/exploiting-nosql-injection-vulnerabilities/scripts/agent.py create mode 100644 personas/_shared/skills/exploiting-nosql-injection-vulnerabilities/scripts/process.py create mode 100644 personas/_shared/skills/exploiting-oauth-misconfiguration/LICENSE create mode 100644 personas/_shared/skills/exploiting-oauth-misconfiguration/SKILL.md create mode 100644 personas/_shared/skills/exploiting-oauth-misconfiguration/references/api-reference.md create mode 100644 personas/_shared/skills/exploiting-oauth-misconfiguration/scripts/agent.py create mode 100644 personas/_shared/skills/exploiting-prototype-pollution-in-javascript/LICENSE create mode 100644 personas/_shared/skills/exploiting-prototype-pollution-in-javascript/SKILL.md create mode 100644 personas/_shared/skills/exploiting-prototype-pollution-in-javascript/references/api-reference.md create mode 100644 personas/_shared/skills/exploiting-prototype-pollution-in-javascript/scripts/agent.py create mode 100644 personas/_shared/skills/exploiting-race-condition-vulnerabilities/LICENSE create mode 100644 personas/_shared/skills/exploiting-race-condition-vulnerabilities/SKILL.md create mode 100644 personas/_shared/skills/exploiting-race-condition-vulnerabilities/references/api-reference.md create mode 100644 personas/_shared/skills/exploiting-race-condition-vulnerabilities/scripts/agent.py create mode 100644 personas/_shared/skills/exploiting-server-side-request-forgery/LICENSE create mode 100644 personas/_shared/skills/exploiting-server-side-request-forgery/SKILL.md create mode 100644 personas/_shared/skills/exploiting-server-side-request-forgery/references/api-reference.md create mode 100644 personas/_shared/skills/exploiting-server-side-request-forgery/scripts/agent.py create mode 100644 personas/_shared/skills/exploiting-smb-vulnerabilities-with-metasploit/LICENSE create mode 100644 personas/_shared/skills/exploiting-smb-vulnerabilities-with-metasploit/SKILL.md create mode 100644 personas/_shared/skills/exploiting-smb-vulnerabilities-with-metasploit/references/api-reference.md create mode 100644 personas/_shared/skills/exploiting-smb-vulnerabilities-with-metasploit/scripts/agent.py create mode 100644 personas/_shared/skills/exploiting-sql-injection-vulnerabilities/LICENSE create mode 100644 personas/_shared/skills/exploiting-sql-injection-vulnerabilities/SKILL.md create mode 100644 personas/_shared/skills/exploiting-sql-injection-vulnerabilities/references/api-reference.md create mode 100644 personas/_shared/skills/exploiting-sql-injection-vulnerabilities/scripts/agent.py create mode 100644 personas/_shared/skills/exploiting-sql-injection-with-sqlmap/LICENSE create mode 100644 personas/_shared/skills/exploiting-sql-injection-with-sqlmap/SKILL.md create mode 100644 personas/_shared/skills/exploiting-sql-injection-with-sqlmap/references/api-reference.md create mode 100644 personas/_shared/skills/exploiting-sql-injection-with-sqlmap/scripts/agent.py create mode 100644 personas/_shared/skills/exploiting-template-injection-vulnerabilities/LICENSE create mode 100644 personas/_shared/skills/exploiting-template-injection-vulnerabilities/SKILL.md create mode 100644 personas/_shared/skills/exploiting-template-injection-vulnerabilities/references/api-reference.md create mode 100644 personas/_shared/skills/exploiting-template-injection-vulnerabilities/scripts/agent.py create mode 100644 personas/_shared/skills/exploiting-type-juggling-vulnerabilities/LICENSE create mode 100644 personas/_shared/skills/exploiting-type-juggling-vulnerabilities/SKILL.md create mode 100644 personas/_shared/skills/exploiting-type-juggling-vulnerabilities/references/api-reference.md create mode 100644 personas/_shared/skills/exploiting-type-juggling-vulnerabilities/scripts/agent.py create mode 100644 personas/_shared/skills/exploiting-vulnerabilities-with-metasploit-framework/LICENSE create mode 100644 personas/_shared/skills/exploiting-vulnerabilities-with-metasploit-framework/SKILL.md create mode 100644 personas/_shared/skills/exploiting-vulnerabilities-with-metasploit-framework/assets/template.md create mode 100644 personas/_shared/skills/exploiting-vulnerabilities-with-metasploit-framework/references/api-reference.md create mode 100644 personas/_shared/skills/exploiting-vulnerabilities-with-metasploit-framework/references/standards.md create mode 100644 personas/_shared/skills/exploiting-vulnerabilities-with-metasploit-framework/references/workflows.md create mode 100644 personas/_shared/skills/exploiting-vulnerabilities-with-metasploit-framework/scripts/agent.py create mode 100644 personas/_shared/skills/exploiting-vulnerabilities-with-metasploit-framework/scripts/process.py create mode 100644 personas/_shared/skills/exploiting-websocket-vulnerabilities/LICENSE create mode 100644 personas/_shared/skills/exploiting-websocket-vulnerabilities/SKILL.md create mode 100644 personas/_shared/skills/exploiting-websocket-vulnerabilities/references/api-reference.md create mode 100644 personas/_shared/skills/exploiting-websocket-vulnerabilities/scripts/agent.py create mode 100644 personas/_shared/skills/exploiting-zerologon-vulnerability-cve-2020-1472/LICENSE create mode 100644 personas/_shared/skills/exploiting-zerologon-vulnerability-cve-2020-1472/SKILL.md create mode 100644 personas/_shared/skills/exploiting-zerologon-vulnerability-cve-2020-1472/assets/template.md create mode 100644 personas/_shared/skills/exploiting-zerologon-vulnerability-cve-2020-1472/references/api-reference.md create mode 100644 personas/_shared/skills/exploiting-zerologon-vulnerability-cve-2020-1472/references/standards.md create mode 100644 personas/_shared/skills/exploiting-zerologon-vulnerability-cve-2020-1472/references/workflows.md create mode 100644 personas/_shared/skills/exploiting-zerologon-vulnerability-cve-2020-1472/scripts/agent.py create mode 100644 personas/_shared/skills/exploiting-zerologon-vulnerability-cve-2020-1472/scripts/process.py create mode 100644 personas/_shared/skills/extracting-browser-history-artifacts/LICENSE create mode 100644 personas/_shared/skills/extracting-browser-history-artifacts/SKILL.md create mode 100644 personas/_shared/skills/extracting-browser-history-artifacts/references/api-reference.md create mode 100644 personas/_shared/skills/extracting-browser-history-artifacts/scripts/agent.py create mode 100644 personas/_shared/skills/extracting-config-from-agent-tesla-rat/LICENSE create mode 100644 personas/_shared/skills/extracting-config-from-agent-tesla-rat/SKILL.md create mode 100644 personas/_shared/skills/extracting-config-from-agent-tesla-rat/assets/template.md create mode 100644 personas/_shared/skills/extracting-config-from-agent-tesla-rat/references/api-reference.md create mode 100644 personas/_shared/skills/extracting-config-from-agent-tesla-rat/references/standards.md create mode 100644 personas/_shared/skills/extracting-config-from-agent-tesla-rat/references/workflows.md create mode 100644 personas/_shared/skills/extracting-config-from-agent-tesla-rat/scripts/agent.py create mode 100644 personas/_shared/skills/extracting-credentials-from-memory-dump/LICENSE create mode 100644 personas/_shared/skills/extracting-credentials-from-memory-dump/SKILL.md create mode 100644 personas/_shared/skills/extracting-credentials-from-memory-dump/references/api-reference.md create mode 100644 personas/_shared/skills/extracting-credentials-from-memory-dump/scripts/agent.py create mode 100644 personas/_shared/skills/extracting-iocs-from-malware-samples/LICENSE create mode 100644 personas/_shared/skills/extracting-iocs-from-malware-samples/SKILL.md create mode 100644 personas/_shared/skills/extracting-iocs-from-malware-samples/references/api-reference.md create mode 100644 personas/_shared/skills/extracting-iocs-from-malware-samples/scripts/agent.py create mode 100644 personas/_shared/skills/extracting-memory-artifacts-with-rekall/LICENSE create mode 100644 personas/_shared/skills/extracting-memory-artifacts-with-rekall/SKILL.md create mode 100644 personas/_shared/skills/extracting-memory-artifacts-with-rekall/references/api-reference.md create mode 100644 personas/_shared/skills/extracting-memory-artifacts-with-rekall/scripts/agent.py create mode 100644 personas/_shared/skills/extracting-windows-event-logs-artifacts/LICENSE create mode 100644 personas/_shared/skills/extracting-windows-event-logs-artifacts/SKILL.md create mode 100644 personas/_shared/skills/extracting-windows-event-logs-artifacts/references/api-reference.md create mode 100644 personas/_shared/skills/extracting-windows-event-logs-artifacts/scripts/agent.py create mode 100644 personas/_shared/skills/generating-threat-intelligence-reports/LICENSE create mode 100644 personas/_shared/skills/generating-threat-intelligence-reports/SKILL.md create mode 100644 personas/_shared/skills/generating-threat-intelligence-reports/references/api-reference.md create mode 100644 personas/_shared/skills/generating-threat-intelligence-reports/scripts/agent.py create mode 100644 personas/_shared/skills/hardening-docker-containers-for-production/LICENSE create mode 100644 personas/_shared/skills/hardening-docker-containers-for-production/SKILL.md create mode 100644 personas/_shared/skills/hardening-docker-containers-for-production/assets/template.md create mode 100644 personas/_shared/skills/hardening-docker-containers-for-production/references/api-reference.md create mode 100644 personas/_shared/skills/hardening-docker-containers-for-production/references/standards.md create mode 100644 personas/_shared/skills/hardening-docker-containers-for-production/references/workflows.md create mode 100644 personas/_shared/skills/hardening-docker-containers-for-production/scripts/agent.py create mode 100644 personas/_shared/skills/hardening-docker-containers-for-production/scripts/process.py create mode 100644 personas/_shared/skills/hardening-docker-daemon-configuration/LICENSE create mode 100644 personas/_shared/skills/hardening-docker-daemon-configuration/SKILL.md create mode 100644 personas/_shared/skills/hardening-docker-daemon-configuration/assets/template.md create mode 100644 personas/_shared/skills/hardening-docker-daemon-configuration/references/api-reference.md create mode 100644 personas/_shared/skills/hardening-docker-daemon-configuration/references/standards.md create mode 100644 personas/_shared/skills/hardening-docker-daemon-configuration/references/workflows.md create mode 100644 personas/_shared/skills/hardening-docker-daemon-configuration/scripts/agent.py create mode 100644 personas/_shared/skills/hardening-docker-daemon-configuration/scripts/process.py create mode 100644 personas/_shared/skills/hardening-linux-endpoint-with-cis-benchmark/LICENSE create mode 100644 personas/_shared/skills/hardening-linux-endpoint-with-cis-benchmark/SKILL.md create mode 100644 personas/_shared/skills/hardening-linux-endpoint-with-cis-benchmark/assets/template.md create mode 100644 personas/_shared/skills/hardening-linux-endpoint-with-cis-benchmark/references/api-reference.md create mode 100644 personas/_shared/skills/hardening-linux-endpoint-with-cis-benchmark/references/standards.md create mode 100644 personas/_shared/skills/hardening-linux-endpoint-with-cis-benchmark/references/workflows.md create mode 100644 personas/_shared/skills/hardening-linux-endpoint-with-cis-benchmark/scripts/agent.py create mode 100644 personas/_shared/skills/hardening-linux-endpoint-with-cis-benchmark/scripts/process.py create mode 100644 personas/_shared/skills/hardening-windows-endpoint-with-cis-benchmark/LICENSE create mode 100644 personas/_shared/skills/hardening-windows-endpoint-with-cis-benchmark/SKILL.md create mode 100644 personas/_shared/skills/hardening-windows-endpoint-with-cis-benchmark/assets/template.md create mode 100644 personas/_shared/skills/hardening-windows-endpoint-with-cis-benchmark/references/api-reference.md create mode 100644 personas/_shared/skills/hardening-windows-endpoint-with-cis-benchmark/references/standards.md create mode 100644 personas/_shared/skills/hardening-windows-endpoint-with-cis-benchmark/references/workflows.md create mode 100644 personas/_shared/skills/hardening-windows-endpoint-with-cis-benchmark/scripts/agent.py create mode 100644 personas/_shared/skills/hardening-windows-endpoint-with-cis-benchmark/scripts/process.py create mode 100644 personas/_shared/skills/hunting-advanced-persistent-threats/LICENSE create mode 100644 personas/_shared/skills/hunting-advanced-persistent-threats/SKILL.md create mode 100644 personas/_shared/skills/hunting-advanced-persistent-threats/references/api-reference.md create mode 100644 personas/_shared/skills/hunting-advanced-persistent-threats/scripts/agent.py create mode 100644 personas/_shared/skills/hunting-credential-stuffing-attacks/LICENSE create mode 100644 personas/_shared/skills/hunting-credential-stuffing-attacks/SKILL.md create mode 100644 personas/_shared/skills/hunting-credential-stuffing-attacks/references/api-reference.md create mode 100644 personas/_shared/skills/hunting-credential-stuffing-attacks/scripts/agent.py create mode 100644 personas/_shared/skills/hunting-for-anomalous-powershell-execution/LICENSE create mode 100644 personas/_shared/skills/hunting-for-anomalous-powershell-execution/SKILL.md create mode 100644 personas/_shared/skills/hunting-for-anomalous-powershell-execution/references/api-reference.md create mode 100644 personas/_shared/skills/hunting-for-anomalous-powershell-execution/scripts/agent.py create mode 100644 personas/_shared/skills/hunting-for-beaconing-with-frequency-analysis/LICENSE create mode 100644 personas/_shared/skills/hunting-for-beaconing-with-frequency-analysis/SKILL.md create mode 100644 personas/_shared/skills/hunting-for-beaconing-with-frequency-analysis/assets/template.md create mode 100644 personas/_shared/skills/hunting-for-beaconing-with-frequency-analysis/references/api-reference.md create mode 100644 personas/_shared/skills/hunting-for-beaconing-with-frequency-analysis/references/standards.md create mode 100644 personas/_shared/skills/hunting-for-beaconing-with-frequency-analysis/references/workflows.md create mode 100644 personas/_shared/skills/hunting-for-beaconing-with-frequency-analysis/scripts/agent.py create mode 100644 personas/_shared/skills/hunting-for-beaconing-with-frequency-analysis/scripts/process.py create mode 100644 personas/_shared/skills/hunting-for-cobalt-strike-beacons/LICENSE create mode 100644 personas/_shared/skills/hunting-for-cobalt-strike-beacons/SKILL.md create mode 100644 personas/_shared/skills/hunting-for-cobalt-strike-beacons/references/api-reference.md create mode 100644 personas/_shared/skills/hunting-for-cobalt-strike-beacons/scripts/agent.py create mode 100644 personas/_shared/skills/hunting-for-command-and-control-beaconing/LICENSE create mode 100644 personas/_shared/skills/hunting-for-command-and-control-beaconing/SKILL.md create mode 100644 personas/_shared/skills/hunting-for-command-and-control-beaconing/assets/template.md create mode 100644 personas/_shared/skills/hunting-for-command-and-control-beaconing/references/api-reference.md create mode 100644 personas/_shared/skills/hunting-for-command-and-control-beaconing/references/standards.md create mode 100644 personas/_shared/skills/hunting-for-command-and-control-beaconing/references/workflows.md create mode 100644 personas/_shared/skills/hunting-for-command-and-control-beaconing/scripts/agent.py create mode 100644 personas/_shared/skills/hunting-for-command-and-control-beaconing/scripts/process.py create mode 100644 personas/_shared/skills/hunting-for-data-exfiltration-indicators/LICENSE create mode 100644 personas/_shared/skills/hunting-for-data-exfiltration-indicators/SKILL.md create mode 100644 personas/_shared/skills/hunting-for-data-exfiltration-indicators/assets/template.md create mode 100644 personas/_shared/skills/hunting-for-data-exfiltration-indicators/references/api-reference.md create mode 100644 personas/_shared/skills/hunting-for-data-exfiltration-indicators/references/standards.md create mode 100644 personas/_shared/skills/hunting-for-data-exfiltration-indicators/references/workflows.md create mode 100644 personas/_shared/skills/hunting-for-data-exfiltration-indicators/scripts/agent.py create mode 100644 personas/_shared/skills/hunting-for-data-exfiltration-indicators/scripts/process.py create mode 100644 personas/_shared/skills/hunting-for-data-staging-before-exfiltration/LICENSE create mode 100644 personas/_shared/skills/hunting-for-data-staging-before-exfiltration/SKILL.md create mode 100644 personas/_shared/skills/hunting-for-data-staging-before-exfiltration/references/api-reference.md create mode 100644 personas/_shared/skills/hunting-for-data-staging-before-exfiltration/scripts/agent.py create mode 100644 personas/_shared/skills/hunting-for-dcom-lateral-movement/LICENSE create mode 100644 personas/_shared/skills/hunting-for-dcom-lateral-movement/SKILL.md create mode 100644 personas/_shared/skills/hunting-for-dcom-lateral-movement/references/api-reference.md create mode 100644 personas/_shared/skills/hunting-for-dcom-lateral-movement/scripts/agent.py create mode 100644 personas/_shared/skills/hunting-for-dcom-lateral-movement/scripts/detect_dcom_lateral_movement.py create mode 100644 personas/_shared/skills/hunting-for-dcsync-attacks/LICENSE create mode 100644 personas/_shared/skills/hunting-for-dcsync-attacks/SKILL.md create mode 100644 personas/_shared/skills/hunting-for-dcsync-attacks/references/api-reference.md create mode 100644 personas/_shared/skills/hunting-for-dcsync-attacks/scripts/agent.py create mode 100644 personas/_shared/skills/hunting-for-defense-evasion-via-timestomping/LICENSE create mode 100644 personas/_shared/skills/hunting-for-defense-evasion-via-timestomping/SKILL.md create mode 100644 personas/_shared/skills/hunting-for-defense-evasion-via-timestomping/references/api-reference.md create mode 100644 personas/_shared/skills/hunting-for-defense-evasion-via-timestomping/scripts/agent.py create mode 100644 personas/_shared/skills/hunting-for-dns-based-persistence/LICENSE create mode 100644 personas/_shared/skills/hunting-for-dns-based-persistence/SKILL.md create mode 100644 personas/_shared/skills/hunting-for-dns-based-persistence/references/api-reference.md create mode 100644 personas/_shared/skills/hunting-for-dns-based-persistence/scripts/agent.py create mode 100644 personas/_shared/skills/hunting-for-dns-tunneling-with-zeek/LICENSE create mode 100644 personas/_shared/skills/hunting-for-dns-tunneling-with-zeek/SKILL.md create mode 100644 personas/_shared/skills/hunting-for-dns-tunneling-with-zeek/assets/template.md create mode 100644 personas/_shared/skills/hunting-for-dns-tunneling-with-zeek/references/api-reference.md create mode 100644 personas/_shared/skills/hunting-for-dns-tunneling-with-zeek/references/standards.md create mode 100644 personas/_shared/skills/hunting-for-dns-tunneling-with-zeek/references/workflows.md create mode 100644 personas/_shared/skills/hunting-for-dns-tunneling-with-zeek/scripts/agent.py create mode 100644 personas/_shared/skills/hunting-for-dns-tunneling-with-zeek/scripts/process.py create mode 100644 personas/_shared/skills/hunting-for-domain-fronting-c2-traffic/LICENSE create mode 100644 personas/_shared/skills/hunting-for-domain-fronting-c2-traffic/SKILL.md create mode 100644 personas/_shared/skills/hunting-for-domain-fronting-c2-traffic/references/api-reference.md create mode 100644 personas/_shared/skills/hunting-for-domain-fronting-c2-traffic/scripts/agent.py create mode 100644 personas/_shared/skills/hunting-for-lateral-movement-via-wmi/LICENSE create mode 100644 personas/_shared/skills/hunting-for-lateral-movement-via-wmi/SKILL.md create mode 100644 personas/_shared/skills/hunting-for-lateral-movement-via-wmi/references/api-reference.md create mode 100644 personas/_shared/skills/hunting-for-lateral-movement-via-wmi/scripts/agent.py create mode 100644 personas/_shared/skills/hunting-for-living-off-the-cloud-techniques/LICENSE create mode 100644 personas/_shared/skills/hunting-for-living-off-the-cloud-techniques/SKILL.md create mode 100644 personas/_shared/skills/hunting-for-living-off-the-cloud-techniques/assets/template.md create mode 100644 personas/_shared/skills/hunting-for-living-off-the-cloud-techniques/references/api-reference.md create mode 100644 personas/_shared/skills/hunting-for-living-off-the-cloud-techniques/references/standards.md create mode 100644 personas/_shared/skills/hunting-for-living-off-the-cloud-techniques/references/workflows.md create mode 100644 personas/_shared/skills/hunting-for-living-off-the-cloud-techniques/scripts/agent.py create mode 100644 personas/_shared/skills/hunting-for-living-off-the-cloud-techniques/scripts/process.py create mode 100644 personas/_shared/skills/hunting-for-living-off-the-land-binaries/LICENSE create mode 100644 personas/_shared/skills/hunting-for-living-off-the-land-binaries/SKILL.md create mode 100644 personas/_shared/skills/hunting-for-living-off-the-land-binaries/assets/template.md create mode 100644 personas/_shared/skills/hunting-for-living-off-the-land-binaries/references/api-reference.md create mode 100644 personas/_shared/skills/hunting-for-living-off-the-land-binaries/references/standards.md create mode 100644 personas/_shared/skills/hunting-for-living-off-the-land-binaries/references/workflows.md create mode 100644 personas/_shared/skills/hunting-for-living-off-the-land-binaries/scripts/agent.py create mode 100644 personas/_shared/skills/hunting-for-living-off-the-land-binaries/scripts/process.py create mode 100644 personas/_shared/skills/hunting-for-lolbins-execution-in-endpoint-logs/LICENSE create mode 100644 personas/_shared/skills/hunting-for-lolbins-execution-in-endpoint-logs/SKILL.md create mode 100644 personas/_shared/skills/hunting-for-lolbins-execution-in-endpoint-logs/assets/template.md create mode 100644 personas/_shared/skills/hunting-for-lolbins-execution-in-endpoint-logs/references/api-reference.md create mode 100644 personas/_shared/skills/hunting-for-lolbins-execution-in-endpoint-logs/references/standards.md create mode 100644 personas/_shared/skills/hunting-for-lolbins-execution-in-endpoint-logs/references/workflows.md create mode 100644 personas/_shared/skills/hunting-for-lolbins-execution-in-endpoint-logs/scripts/agent.py create mode 100644 personas/_shared/skills/hunting-for-lolbins-execution-in-endpoint-logs/scripts/process.py create mode 100644 personas/_shared/skills/hunting-for-ntlm-relay-attacks/LICENSE create mode 100644 personas/_shared/skills/hunting-for-ntlm-relay-attacks/SKILL.md create mode 100644 personas/_shared/skills/hunting-for-ntlm-relay-attacks/references/api-reference.md create mode 100644 personas/_shared/skills/hunting-for-ntlm-relay-attacks/scripts/agent.py create mode 100644 personas/_shared/skills/hunting-for-persistence-mechanisms-in-windows/LICENSE create mode 100644 personas/_shared/skills/hunting-for-persistence-mechanisms-in-windows/SKILL.md create mode 100644 personas/_shared/skills/hunting-for-persistence-mechanisms-in-windows/assets/template.md create mode 100644 personas/_shared/skills/hunting-for-persistence-mechanisms-in-windows/references/api-reference.md create mode 100644 personas/_shared/skills/hunting-for-persistence-mechanisms-in-windows/references/standards.md create mode 100644 personas/_shared/skills/hunting-for-persistence-mechanisms-in-windows/references/workflows.md create mode 100644 personas/_shared/skills/hunting-for-persistence-mechanisms-in-windows/scripts/agent.py create mode 100644 personas/_shared/skills/hunting-for-persistence-mechanisms-in-windows/scripts/process.py create mode 100644 personas/_shared/skills/hunting-for-persistence-via-wmi-subscriptions/LICENSE create mode 100644 personas/_shared/skills/hunting-for-persistence-via-wmi-subscriptions/SKILL.md create mode 100644 personas/_shared/skills/hunting-for-persistence-via-wmi-subscriptions/assets/template.md create mode 100644 personas/_shared/skills/hunting-for-persistence-via-wmi-subscriptions/references/api-reference.md create mode 100644 personas/_shared/skills/hunting-for-persistence-via-wmi-subscriptions/references/standards.md create mode 100644 personas/_shared/skills/hunting-for-persistence-via-wmi-subscriptions/references/workflows.md create mode 100644 personas/_shared/skills/hunting-for-persistence-via-wmi-subscriptions/scripts/agent.py create mode 100644 personas/_shared/skills/hunting-for-persistence-via-wmi-subscriptions/scripts/process.py create mode 100644 personas/_shared/skills/hunting-for-process-injection-techniques/LICENSE create mode 100644 personas/_shared/skills/hunting-for-process-injection-techniques/SKILL.md create mode 100644 personas/_shared/skills/hunting-for-process-injection-techniques/references/api-reference.md create mode 100644 personas/_shared/skills/hunting-for-process-injection-techniques/scripts/agent.py create mode 100644 personas/_shared/skills/hunting-for-registry-persistence-mechanisms/LICENSE create mode 100644 personas/_shared/skills/hunting-for-registry-persistence-mechanisms/SKILL.md create mode 100644 personas/_shared/skills/hunting-for-registry-persistence-mechanisms/assets/template.md create mode 100644 personas/_shared/skills/hunting-for-registry-persistence-mechanisms/references/api-reference.md create mode 100644 personas/_shared/skills/hunting-for-registry-persistence-mechanisms/references/standards.md create mode 100644 personas/_shared/skills/hunting-for-registry-persistence-mechanisms/references/workflows.md create mode 100644 personas/_shared/skills/hunting-for-registry-persistence-mechanisms/scripts/agent.py create mode 100644 personas/_shared/skills/hunting-for-registry-persistence-mechanisms/scripts/process.py create mode 100644 personas/_shared/skills/hunting-for-registry-run-key-persistence/LICENSE create mode 100644 personas/_shared/skills/hunting-for-registry-run-key-persistence/SKILL.md create mode 100644 personas/_shared/skills/hunting-for-registry-run-key-persistence/references/api-reference.md create mode 100644 personas/_shared/skills/hunting-for-registry-run-key-persistence/scripts/agent.py create mode 100644 personas/_shared/skills/hunting-for-scheduled-task-persistence/LICENSE create mode 100644 personas/_shared/skills/hunting-for-scheduled-task-persistence/SKILL.md create mode 100644 personas/_shared/skills/hunting-for-scheduled-task-persistence/assets/template.md create mode 100644 personas/_shared/skills/hunting-for-scheduled-task-persistence/references/api-reference.md create mode 100644 personas/_shared/skills/hunting-for-scheduled-task-persistence/references/standards.md create mode 100644 personas/_shared/skills/hunting-for-scheduled-task-persistence/references/workflows.md create mode 100644 personas/_shared/skills/hunting-for-scheduled-task-persistence/scripts/agent.py create mode 100644 personas/_shared/skills/hunting-for-scheduled-task-persistence/scripts/process.py create mode 100644 personas/_shared/skills/hunting-for-shadow-copy-deletion/LICENSE create mode 100644 personas/_shared/skills/hunting-for-shadow-copy-deletion/SKILL.md create mode 100644 personas/_shared/skills/hunting-for-shadow-copy-deletion/assets/template.md create mode 100644 personas/_shared/skills/hunting-for-shadow-copy-deletion/references/api-reference.md create mode 100644 personas/_shared/skills/hunting-for-shadow-copy-deletion/references/standards.md create mode 100644 personas/_shared/skills/hunting-for-shadow-copy-deletion/references/workflows.md create mode 100644 personas/_shared/skills/hunting-for-shadow-copy-deletion/scripts/agent.py create mode 100644 personas/_shared/skills/hunting-for-shadow-copy-deletion/scripts/process.py create mode 100644 personas/_shared/skills/hunting-for-spearphishing-indicators/LICENSE create mode 100644 personas/_shared/skills/hunting-for-spearphishing-indicators/SKILL.md create mode 100644 personas/_shared/skills/hunting-for-spearphishing-indicators/assets/template.md create mode 100644 personas/_shared/skills/hunting-for-spearphishing-indicators/references/api-reference.md create mode 100644 personas/_shared/skills/hunting-for-spearphishing-indicators/references/standards.md create mode 100644 personas/_shared/skills/hunting-for-spearphishing-indicators/references/workflows.md create mode 100644 personas/_shared/skills/hunting-for-spearphishing-indicators/scripts/agent.py create mode 100644 personas/_shared/skills/hunting-for-spearphishing-indicators/scripts/process.py create mode 100644 personas/_shared/skills/hunting-for-startup-folder-persistence/LICENSE create mode 100644 personas/_shared/skills/hunting-for-startup-folder-persistence/SKILL.md create mode 100644 personas/_shared/skills/hunting-for-startup-folder-persistence/references/api-reference.md create mode 100644 personas/_shared/skills/hunting-for-startup-folder-persistence/scripts/agent.py create mode 100644 personas/_shared/skills/hunting-for-supply-chain-compromise/LICENSE create mode 100644 personas/_shared/skills/hunting-for-supply-chain-compromise/SKILL.md create mode 100644 personas/_shared/skills/hunting-for-supply-chain-compromise/assets/template.md create mode 100644 personas/_shared/skills/hunting-for-supply-chain-compromise/references/api-reference.md create mode 100644 personas/_shared/skills/hunting-for-supply-chain-compromise/references/standards.md create mode 100644 personas/_shared/skills/hunting-for-supply-chain-compromise/references/workflows.md create mode 100644 personas/_shared/skills/hunting-for-supply-chain-compromise/scripts/agent.py create mode 100644 personas/_shared/skills/hunting-for-supply-chain-compromise/scripts/process.py create mode 100644 personas/_shared/skills/hunting-for-suspicious-scheduled-tasks/LICENSE create mode 100644 personas/_shared/skills/hunting-for-suspicious-scheduled-tasks/SKILL.md create mode 100644 personas/_shared/skills/hunting-for-suspicious-scheduled-tasks/assets/template.md create mode 100644 personas/_shared/skills/hunting-for-suspicious-scheduled-tasks/references/api-reference.md create mode 100644 personas/_shared/skills/hunting-for-suspicious-scheduled-tasks/references/standards.md create mode 100644 personas/_shared/skills/hunting-for-suspicious-scheduled-tasks/references/workflows.md create mode 100644 personas/_shared/skills/hunting-for-suspicious-scheduled-tasks/scripts/agent.py create mode 100644 personas/_shared/skills/hunting-for-suspicious-scheduled-tasks/scripts/process.py create mode 100644 personas/_shared/skills/hunting-for-t1098-account-manipulation/LICENSE create mode 100644 personas/_shared/skills/hunting-for-t1098-account-manipulation/SKILL.md create mode 100644 personas/_shared/skills/hunting-for-t1098-account-manipulation/references/api-reference.md create mode 100644 personas/_shared/skills/hunting-for-t1098-account-manipulation/scripts/agent.py create mode 100644 personas/_shared/skills/hunting-for-unusual-network-connections/LICENSE create mode 100644 personas/_shared/skills/hunting-for-unusual-network-connections/SKILL.md create mode 100644 personas/_shared/skills/hunting-for-unusual-network-connections/assets/template.md create mode 100644 personas/_shared/skills/hunting-for-unusual-network-connections/references/api-reference.md create mode 100644 personas/_shared/skills/hunting-for-unusual-network-connections/references/standards.md create mode 100644 personas/_shared/skills/hunting-for-unusual-network-connections/references/workflows.md create mode 100644 personas/_shared/skills/hunting-for-unusual-network-connections/scripts/agent.py create mode 100644 personas/_shared/skills/hunting-for-unusual-network-connections/scripts/process.py create mode 100644 personas/_shared/skills/hunting-for-unusual-service-installations/LICENSE create mode 100644 personas/_shared/skills/hunting-for-unusual-service-installations/SKILL.md create mode 100644 personas/_shared/skills/hunting-for-unusual-service-installations/references/api-reference.md create mode 100644 personas/_shared/skills/hunting-for-unusual-service-installations/scripts/agent.py create mode 100644 personas/_shared/skills/hunting-for-webshell-activity/LICENSE create mode 100644 personas/_shared/skills/hunting-for-webshell-activity/SKILL.md create mode 100644 personas/_shared/skills/hunting-for-webshell-activity/assets/template.md create mode 100644 personas/_shared/skills/hunting-for-webshell-activity/references/api-reference.md create mode 100644 personas/_shared/skills/hunting-for-webshell-activity/references/standards.md create mode 100644 personas/_shared/skills/hunting-for-webshell-activity/references/workflows.md create mode 100644 personas/_shared/skills/hunting-for-webshell-activity/scripts/agent.py create mode 100644 personas/_shared/skills/hunting-for-webshell-activity/scripts/process.py create mode 100644 personas/_shared/skills/implementing-aes-encryption-for-data-at-rest/LICENSE create mode 100644 personas/_shared/skills/implementing-aes-encryption-for-data-at-rest/SKILL.md create mode 100644 personas/_shared/skills/implementing-aes-encryption-for-data-at-rest/assets/template.md create mode 100644 personas/_shared/skills/implementing-aes-encryption-for-data-at-rest/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-aes-encryption-for-data-at-rest/references/standards.md create mode 100644 personas/_shared/skills/implementing-aes-encryption-for-data-at-rest/references/workflows.md create mode 100644 personas/_shared/skills/implementing-aes-encryption-for-data-at-rest/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-aes-encryption-for-data-at-rest/scripts/process.py create mode 100644 personas/_shared/skills/implementing-alert-fatigue-reduction/LICENSE create mode 100644 personas/_shared/skills/implementing-alert-fatigue-reduction/SKILL.md create mode 100644 personas/_shared/skills/implementing-alert-fatigue-reduction/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-alert-fatigue-reduction/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-anti-phishing-training-program/LICENSE create mode 100644 personas/_shared/skills/implementing-anti-phishing-training-program/SKILL.md create mode 100644 personas/_shared/skills/implementing-anti-phishing-training-program/assets/template.md create mode 100644 personas/_shared/skills/implementing-anti-phishing-training-program/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-anti-phishing-training-program/references/standards.md create mode 100644 personas/_shared/skills/implementing-anti-phishing-training-program/references/workflows.md create mode 100644 personas/_shared/skills/implementing-anti-phishing-training-program/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-anti-phishing-training-program/scripts/process.py create mode 100644 personas/_shared/skills/implementing-anti-ransomware-group-policy/LICENSE create mode 100644 personas/_shared/skills/implementing-anti-ransomware-group-policy/SKILL.md create mode 100644 personas/_shared/skills/implementing-anti-ransomware-group-policy/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-anti-ransomware-group-policy/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-api-abuse-detection-with-rate-limiting/LICENSE create mode 100644 personas/_shared/skills/implementing-api-abuse-detection-with-rate-limiting/SKILL.md create mode 100644 personas/_shared/skills/implementing-api-abuse-detection-with-rate-limiting/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-api-abuse-detection-with-rate-limiting/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-api-gateway-security-controls/LICENSE create mode 100644 personas/_shared/skills/implementing-api-gateway-security-controls/SKILL.md create mode 100644 personas/_shared/skills/implementing-api-gateway-security-controls/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-api-gateway-security-controls/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-api-key-security-controls/LICENSE create mode 100644 personas/_shared/skills/implementing-api-key-security-controls/SKILL.md create mode 100644 personas/_shared/skills/implementing-api-key-security-controls/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-api-key-security-controls/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-api-rate-limiting-and-throttling/LICENSE create mode 100644 personas/_shared/skills/implementing-api-rate-limiting-and-throttling/SKILL.md create mode 100644 personas/_shared/skills/implementing-api-rate-limiting-and-throttling/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-api-rate-limiting-and-throttling/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-api-schema-validation-security/LICENSE create mode 100644 personas/_shared/skills/implementing-api-schema-validation-security/SKILL.md create mode 100644 personas/_shared/skills/implementing-api-schema-validation-security/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-api-schema-validation-security/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-api-security-posture-management/LICENSE create mode 100644 personas/_shared/skills/implementing-api-security-posture-management/SKILL.md create mode 100644 personas/_shared/skills/implementing-api-security-posture-management/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-api-security-posture-management/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-api-security-testing-with-42crunch/LICENSE create mode 100644 personas/_shared/skills/implementing-api-security-testing-with-42crunch/SKILL.md create mode 100644 personas/_shared/skills/implementing-api-security-testing-with-42crunch/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-api-security-testing-with-42crunch/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-api-threat-protection-with-apigee/LICENSE create mode 100644 personas/_shared/skills/implementing-api-threat-protection-with-apigee/SKILL.md create mode 100644 personas/_shared/skills/implementing-api-threat-protection-with-apigee/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-api-threat-protection-with-apigee/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-application-whitelisting-with-applocker/LICENSE create mode 100644 personas/_shared/skills/implementing-application-whitelisting-with-applocker/SKILL.md create mode 100644 personas/_shared/skills/implementing-application-whitelisting-with-applocker/assets/template.md create mode 100644 personas/_shared/skills/implementing-application-whitelisting-with-applocker/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-application-whitelisting-with-applocker/references/standards.md create mode 100644 personas/_shared/skills/implementing-application-whitelisting-with-applocker/references/workflows.md create mode 100644 personas/_shared/skills/implementing-application-whitelisting-with-applocker/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-application-whitelisting-with-applocker/scripts/process.py create mode 100644 personas/_shared/skills/implementing-aqua-security-for-container-scanning/LICENSE create mode 100644 personas/_shared/skills/implementing-aqua-security-for-container-scanning/SKILL.md create mode 100644 personas/_shared/skills/implementing-aqua-security-for-container-scanning/assets/template.md create mode 100644 personas/_shared/skills/implementing-aqua-security-for-container-scanning/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-aqua-security-for-container-scanning/references/standards.md create mode 100644 personas/_shared/skills/implementing-aqua-security-for-container-scanning/references/workflows.md create mode 100644 personas/_shared/skills/implementing-aqua-security-for-container-scanning/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-aqua-security-for-container-scanning/scripts/process.py create mode 100644 personas/_shared/skills/implementing-attack-path-analysis-with-xm-cyber/LICENSE create mode 100644 personas/_shared/skills/implementing-attack-path-analysis-with-xm-cyber/SKILL.md create mode 100644 personas/_shared/skills/implementing-attack-path-analysis-with-xm-cyber/assets/template.md create mode 100644 personas/_shared/skills/implementing-attack-path-analysis-with-xm-cyber/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-attack-path-analysis-with-xm-cyber/references/standards.md create mode 100644 personas/_shared/skills/implementing-attack-path-analysis-with-xm-cyber/references/workflows.md create mode 100644 personas/_shared/skills/implementing-attack-path-analysis-with-xm-cyber/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-attack-path-analysis-with-xm-cyber/scripts/process.py create mode 100644 personas/_shared/skills/implementing-attack-surface-management/LICENSE create mode 100644 personas/_shared/skills/implementing-attack-surface-management/SKILL.md create mode 100644 personas/_shared/skills/implementing-attack-surface-management/references/asm-reference.md create mode 100644 personas/_shared/skills/implementing-attack-surface-management/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-aws-config-rules-for-compliance/LICENSE create mode 100644 personas/_shared/skills/implementing-aws-config-rules-for-compliance/SKILL.md create mode 100644 personas/_shared/skills/implementing-aws-config-rules-for-compliance/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-aws-config-rules-for-compliance/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-aws-iam-permission-boundaries/LICENSE create mode 100644 personas/_shared/skills/implementing-aws-iam-permission-boundaries/SKILL.md create mode 100644 personas/_shared/skills/implementing-aws-iam-permission-boundaries/assets/template.md create mode 100644 personas/_shared/skills/implementing-aws-iam-permission-boundaries/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-aws-iam-permission-boundaries/references/standards.md create mode 100644 personas/_shared/skills/implementing-aws-iam-permission-boundaries/references/workflows.md create mode 100644 personas/_shared/skills/implementing-aws-iam-permission-boundaries/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-aws-iam-permission-boundaries/scripts/process.py create mode 100644 personas/_shared/skills/implementing-aws-macie-for-data-classification/LICENSE create mode 100644 personas/_shared/skills/implementing-aws-macie-for-data-classification/SKILL.md create mode 100644 personas/_shared/skills/implementing-aws-macie-for-data-classification/assets/template.md create mode 100644 personas/_shared/skills/implementing-aws-macie-for-data-classification/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-aws-macie-for-data-classification/references/standards.md create mode 100644 personas/_shared/skills/implementing-aws-macie-for-data-classification/references/workflows.md create mode 100644 personas/_shared/skills/implementing-aws-macie-for-data-classification/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-aws-macie-for-data-classification/scripts/process.py create mode 100644 personas/_shared/skills/implementing-aws-nitro-enclave-security/LICENSE create mode 100644 personas/_shared/skills/implementing-aws-nitro-enclave-security/SKILL.md create mode 100644 personas/_shared/skills/implementing-aws-nitro-enclave-security/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-aws-nitro-enclave-security/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-aws-security-hub-compliance/LICENSE create mode 100644 personas/_shared/skills/implementing-aws-security-hub-compliance/SKILL.md create mode 100644 personas/_shared/skills/implementing-aws-security-hub-compliance/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-aws-security-hub-compliance/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-aws-security-hub/LICENSE create mode 100644 personas/_shared/skills/implementing-aws-security-hub/SKILL.md create mode 100644 personas/_shared/skills/implementing-aws-security-hub/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-aws-security-hub/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-azure-ad-privileged-identity-management/LICENSE create mode 100644 personas/_shared/skills/implementing-azure-ad-privileged-identity-management/SKILL.md create mode 100644 personas/_shared/skills/implementing-azure-ad-privileged-identity-management/assets/template.md create mode 100644 personas/_shared/skills/implementing-azure-ad-privileged-identity-management/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-azure-ad-privileged-identity-management/references/standards.md create mode 100644 personas/_shared/skills/implementing-azure-ad-privileged-identity-management/references/workflows.md create mode 100644 personas/_shared/skills/implementing-azure-ad-privileged-identity-management/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-azure-ad-privileged-identity-management/scripts/process.py create mode 100644 personas/_shared/skills/implementing-azure-defender-for-cloud/LICENSE create mode 100644 personas/_shared/skills/implementing-azure-defender-for-cloud/SKILL.md create mode 100644 personas/_shared/skills/implementing-azure-defender-for-cloud/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-azure-defender-for-cloud/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-beyondcorp-zero-trust-access-model/LICENSE create mode 100644 personas/_shared/skills/implementing-beyondcorp-zero-trust-access-model/SKILL.md create mode 100644 personas/_shared/skills/implementing-beyondcorp-zero-trust-access-model/assets/template.md create mode 100644 personas/_shared/skills/implementing-beyondcorp-zero-trust-access-model/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-beyondcorp-zero-trust-access-model/references/standards.md create mode 100644 personas/_shared/skills/implementing-beyondcorp-zero-trust-access-model/references/workflows.md create mode 100644 personas/_shared/skills/implementing-beyondcorp-zero-trust-access-model/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-beyondcorp-zero-trust-access-model/scripts/process.py create mode 100644 personas/_shared/skills/implementing-bgp-security-with-rpki/LICENSE create mode 100644 personas/_shared/skills/implementing-bgp-security-with-rpki/SKILL.md create mode 100644 personas/_shared/skills/implementing-bgp-security-with-rpki/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-bgp-security-with-rpki/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-browser-isolation-for-zero-trust/LICENSE create mode 100644 personas/_shared/skills/implementing-browser-isolation-for-zero-trust/SKILL.md create mode 100644 personas/_shared/skills/implementing-browser-isolation-for-zero-trust/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-browser-isolation-for-zero-trust/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-canary-tokens-for-network-intrusion/LICENSE create mode 100644 personas/_shared/skills/implementing-canary-tokens-for-network-intrusion/SKILL.md create mode 100644 personas/_shared/skills/implementing-canary-tokens-for-network-intrusion/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-canary-tokens-for-network-intrusion/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-cisa-zero-trust-maturity-model/LICENSE create mode 100644 personas/_shared/skills/implementing-cisa-zero-trust-maturity-model/SKILL.md create mode 100644 personas/_shared/skills/implementing-cisa-zero-trust-maturity-model/assets/template.md create mode 100644 personas/_shared/skills/implementing-cisa-zero-trust-maturity-model/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-cisa-zero-trust-maturity-model/references/standards.md create mode 100644 personas/_shared/skills/implementing-cisa-zero-trust-maturity-model/references/workflows.md create mode 100644 personas/_shared/skills/implementing-cisa-zero-trust-maturity-model/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-cisa-zero-trust-maturity-model/scripts/process.py create mode 100644 personas/_shared/skills/implementing-cloud-dlp-for-data-protection/LICENSE create mode 100644 personas/_shared/skills/implementing-cloud-dlp-for-data-protection/SKILL.md create mode 100644 personas/_shared/skills/implementing-cloud-dlp-for-data-protection/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-cloud-dlp-for-data-protection/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-cloud-security-posture-management/LICENSE create mode 100644 personas/_shared/skills/implementing-cloud-security-posture-management/SKILL.es.md create mode 100644 personas/_shared/skills/implementing-cloud-security-posture-management/SKILL.md create mode 100644 personas/_shared/skills/implementing-cloud-security-posture-management/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-cloud-security-posture-management/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-cloud-trail-log-analysis/LICENSE create mode 100644 personas/_shared/skills/implementing-cloud-trail-log-analysis/SKILL.md create mode 100644 personas/_shared/skills/implementing-cloud-trail-log-analysis/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-cloud-trail-log-analysis/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-cloud-vulnerability-posture-management/LICENSE create mode 100644 personas/_shared/skills/implementing-cloud-vulnerability-posture-management/SKILL.md create mode 100644 personas/_shared/skills/implementing-cloud-vulnerability-posture-management/assets/template.md create mode 100644 personas/_shared/skills/implementing-cloud-vulnerability-posture-management/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-cloud-vulnerability-posture-management/references/standards.md create mode 100644 personas/_shared/skills/implementing-cloud-vulnerability-posture-management/references/workflows.md create mode 100644 personas/_shared/skills/implementing-cloud-vulnerability-posture-management/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-cloud-vulnerability-posture-management/scripts/process.py create mode 100644 personas/_shared/skills/implementing-cloud-waf-rules/LICENSE create mode 100644 personas/_shared/skills/implementing-cloud-waf-rules/SKILL.md create mode 100644 personas/_shared/skills/implementing-cloud-waf-rules/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-cloud-waf-rules/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-cloud-workload-protection/LICENSE create mode 100644 personas/_shared/skills/implementing-cloud-workload-protection/SKILL.md create mode 100644 personas/_shared/skills/implementing-cloud-workload-protection/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-cloud-workload-protection/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-code-signing-for-artifacts/LICENSE create mode 100644 personas/_shared/skills/implementing-code-signing-for-artifacts/SKILL.md create mode 100644 personas/_shared/skills/implementing-code-signing-for-artifacts/assets/template.md create mode 100644 personas/_shared/skills/implementing-code-signing-for-artifacts/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-code-signing-for-artifacts/references/standards.md create mode 100644 personas/_shared/skills/implementing-code-signing-for-artifacts/references/workflows.md create mode 100644 personas/_shared/skills/implementing-code-signing-for-artifacts/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-code-signing-for-artifacts/scripts/process.py create mode 100644 personas/_shared/skills/implementing-conditional-access-policies-azure-ad/LICENSE create mode 100644 personas/_shared/skills/implementing-conditional-access-policies-azure-ad/SKILL.md create mode 100644 personas/_shared/skills/implementing-conditional-access-policies-azure-ad/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-conditional-access-policies-azure-ad/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-conduit-security-for-ot-remote-access/LICENSE create mode 100644 personas/_shared/skills/implementing-conduit-security-for-ot-remote-access/SKILL.md create mode 100644 personas/_shared/skills/implementing-conduit-security-for-ot-remote-access/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-conduit-security-for-ot-remote-access/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-container-image-minimal-base-with-distroless/LICENSE create mode 100644 personas/_shared/skills/implementing-container-image-minimal-base-with-distroless/SKILL.md create mode 100644 personas/_shared/skills/implementing-container-image-minimal-base-with-distroless/assets/template.md create mode 100644 personas/_shared/skills/implementing-container-image-minimal-base-with-distroless/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-container-image-minimal-base-with-distroless/references/standards.md create mode 100644 personas/_shared/skills/implementing-container-image-minimal-base-with-distroless/references/workflows.md create mode 100644 personas/_shared/skills/implementing-container-image-minimal-base-with-distroless/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-container-image-minimal-base-with-distroless/scripts/process.py create mode 100644 personas/_shared/skills/implementing-container-network-policies-with-calico/LICENSE create mode 100644 personas/_shared/skills/implementing-container-network-policies-with-calico/SKILL.md create mode 100644 personas/_shared/skills/implementing-container-network-policies-with-calico/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-container-network-policies-with-calico/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-continuous-security-validation-with-bas/LICENSE create mode 100644 personas/_shared/skills/implementing-continuous-security-validation-with-bas/SKILL.md create mode 100644 personas/_shared/skills/implementing-continuous-security-validation-with-bas/assets/template.md create mode 100644 personas/_shared/skills/implementing-continuous-security-validation-with-bas/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-continuous-security-validation-with-bas/references/standards.md create mode 100644 personas/_shared/skills/implementing-continuous-security-validation-with-bas/references/workflows.md create mode 100644 personas/_shared/skills/implementing-continuous-security-validation-with-bas/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-continuous-security-validation-with-bas/scripts/process.py create mode 100644 personas/_shared/skills/implementing-data-loss-prevention-with-microsoft-purview/LICENSE create mode 100644 personas/_shared/skills/implementing-data-loss-prevention-with-microsoft-purview/SKILL.md create mode 100644 personas/_shared/skills/implementing-data-loss-prevention-with-microsoft-purview/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-data-loss-prevention-with-microsoft-purview/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-ddos-mitigation-with-cloudflare/LICENSE create mode 100644 personas/_shared/skills/implementing-ddos-mitigation-with-cloudflare/SKILL.md create mode 100644 personas/_shared/skills/implementing-ddos-mitigation-with-cloudflare/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-ddos-mitigation-with-cloudflare/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-deception-based-detection-with-canarytoken/LICENSE create mode 100644 personas/_shared/skills/implementing-deception-based-detection-with-canarytoken/SKILL.md create mode 100644 personas/_shared/skills/implementing-deception-based-detection-with-canarytoken/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-deception-based-detection-with-canarytoken/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-delinea-secret-server-for-pam/LICENSE create mode 100644 personas/_shared/skills/implementing-delinea-secret-server-for-pam/SKILL.md create mode 100644 personas/_shared/skills/implementing-delinea-secret-server-for-pam/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-delinea-secret-server-for-pam/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-device-posture-assessment-in-zero-trust/LICENSE create mode 100644 personas/_shared/skills/implementing-device-posture-assessment-in-zero-trust/SKILL.md create mode 100644 personas/_shared/skills/implementing-device-posture-assessment-in-zero-trust/assets/template.md create mode 100644 personas/_shared/skills/implementing-device-posture-assessment-in-zero-trust/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-device-posture-assessment-in-zero-trust/references/standards.md create mode 100644 personas/_shared/skills/implementing-device-posture-assessment-in-zero-trust/references/workflows.md create mode 100644 personas/_shared/skills/implementing-device-posture-assessment-in-zero-trust/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-device-posture-assessment-in-zero-trust/scripts/process.py create mode 100644 personas/_shared/skills/implementing-devsecops-security-scanning/LICENSE create mode 100644 personas/_shared/skills/implementing-devsecops-security-scanning/SKILL.md create mode 100644 personas/_shared/skills/implementing-devsecops-security-scanning/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-devsecops-security-scanning/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-diamond-model-analysis/LICENSE create mode 100644 personas/_shared/skills/implementing-diamond-model-analysis/SKILL.md create mode 100644 personas/_shared/skills/implementing-diamond-model-analysis/assets/template.md create mode 100644 personas/_shared/skills/implementing-diamond-model-analysis/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-diamond-model-analysis/references/standards.md create mode 100644 personas/_shared/skills/implementing-diamond-model-analysis/references/workflows.md create mode 100644 personas/_shared/skills/implementing-diamond-model-analysis/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-diamond-model-analysis/scripts/process.py create mode 100644 personas/_shared/skills/implementing-digital-signatures-with-ed25519/LICENSE create mode 100644 personas/_shared/skills/implementing-digital-signatures-with-ed25519/SKILL.md create mode 100644 personas/_shared/skills/implementing-digital-signatures-with-ed25519/assets/template.md create mode 100644 personas/_shared/skills/implementing-digital-signatures-with-ed25519/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-digital-signatures-with-ed25519/references/standards.md create mode 100644 personas/_shared/skills/implementing-digital-signatures-with-ed25519/references/workflows.md create mode 100644 personas/_shared/skills/implementing-digital-signatures-with-ed25519/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-digital-signatures-with-ed25519/scripts/process.py create mode 100644 personas/_shared/skills/implementing-disk-encryption-with-bitlocker/LICENSE create mode 100644 personas/_shared/skills/implementing-disk-encryption-with-bitlocker/SKILL.md create mode 100644 personas/_shared/skills/implementing-disk-encryption-with-bitlocker/assets/template.md create mode 100644 personas/_shared/skills/implementing-disk-encryption-with-bitlocker/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-disk-encryption-with-bitlocker/references/standards.md create mode 100644 personas/_shared/skills/implementing-disk-encryption-with-bitlocker/references/workflows.md create mode 100644 personas/_shared/skills/implementing-disk-encryption-with-bitlocker/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-disk-encryption-with-bitlocker/scripts/process.py create mode 100644 personas/_shared/skills/implementing-dmarc-dkim-spf-email-security/LICENSE create mode 100644 personas/_shared/skills/implementing-dmarc-dkim-spf-email-security/SKILL.md create mode 100644 personas/_shared/skills/implementing-dmarc-dkim-spf-email-security/assets/template.md create mode 100644 personas/_shared/skills/implementing-dmarc-dkim-spf-email-security/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-dmarc-dkim-spf-email-security/references/standards.md create mode 100644 personas/_shared/skills/implementing-dmarc-dkim-spf-email-security/references/workflows.md create mode 100644 personas/_shared/skills/implementing-dmarc-dkim-spf-email-security/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-dmarc-dkim-spf-email-security/scripts/process.py create mode 100644 personas/_shared/skills/implementing-dragos-platform-for-ot-monitoring/LICENSE create mode 100644 personas/_shared/skills/implementing-dragos-platform-for-ot-monitoring/SKILL.md create mode 100644 personas/_shared/skills/implementing-dragos-platform-for-ot-monitoring/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-dragos-platform-for-ot-monitoring/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-ebpf-security-monitoring/LICENSE create mode 100644 personas/_shared/skills/implementing-ebpf-security-monitoring/SKILL.md create mode 100644 personas/_shared/skills/implementing-ebpf-security-monitoring/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-ebpf-security-monitoring/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-email-sandboxing-with-proofpoint/LICENSE create mode 100644 personas/_shared/skills/implementing-email-sandboxing-with-proofpoint/SKILL.md create mode 100644 personas/_shared/skills/implementing-email-sandboxing-with-proofpoint/assets/template.md create mode 100644 personas/_shared/skills/implementing-email-sandboxing-with-proofpoint/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-email-sandboxing-with-proofpoint/references/standards.md create mode 100644 personas/_shared/skills/implementing-email-sandboxing-with-proofpoint/references/workflows.md create mode 100644 personas/_shared/skills/implementing-email-sandboxing-with-proofpoint/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-email-sandboxing-with-proofpoint/scripts/process.py create mode 100644 personas/_shared/skills/implementing-end-to-end-encryption-for-messaging/LICENSE create mode 100644 personas/_shared/skills/implementing-end-to-end-encryption-for-messaging/SKILL.md create mode 100644 personas/_shared/skills/implementing-end-to-end-encryption-for-messaging/assets/template.md create mode 100644 personas/_shared/skills/implementing-end-to-end-encryption-for-messaging/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-end-to-end-encryption-for-messaging/references/standards.md create mode 100644 personas/_shared/skills/implementing-end-to-end-encryption-for-messaging/references/workflows.md create mode 100644 personas/_shared/skills/implementing-end-to-end-encryption-for-messaging/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-end-to-end-encryption-for-messaging/scripts/process.py create mode 100644 personas/_shared/skills/implementing-endpoint-detection-with-wazuh/LICENSE create mode 100644 personas/_shared/skills/implementing-endpoint-detection-with-wazuh/SKILL.md create mode 100644 personas/_shared/skills/implementing-endpoint-detection-with-wazuh/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-endpoint-detection-with-wazuh/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-endpoint-dlp-controls/LICENSE create mode 100644 personas/_shared/skills/implementing-endpoint-dlp-controls/SKILL.md create mode 100644 personas/_shared/skills/implementing-endpoint-dlp-controls/assets/template.md create mode 100644 personas/_shared/skills/implementing-endpoint-dlp-controls/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-endpoint-dlp-controls/references/standards.md create mode 100644 personas/_shared/skills/implementing-endpoint-dlp-controls/references/workflows.md create mode 100644 personas/_shared/skills/implementing-endpoint-dlp-controls/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-endpoint-dlp-controls/scripts/process.py create mode 100644 personas/_shared/skills/implementing-envelope-encryption-with-aws-kms/LICENSE create mode 100644 personas/_shared/skills/implementing-envelope-encryption-with-aws-kms/SKILL.md create mode 100644 personas/_shared/skills/implementing-envelope-encryption-with-aws-kms/assets/template.md create mode 100644 personas/_shared/skills/implementing-envelope-encryption-with-aws-kms/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-envelope-encryption-with-aws-kms/references/standards.md create mode 100644 personas/_shared/skills/implementing-envelope-encryption-with-aws-kms/references/workflows.md create mode 100644 personas/_shared/skills/implementing-envelope-encryption-with-aws-kms/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-envelope-encryption-with-aws-kms/scripts/process.py create mode 100644 personas/_shared/skills/implementing-epss-score-for-vulnerability-prioritization/LICENSE create mode 100644 personas/_shared/skills/implementing-epss-score-for-vulnerability-prioritization/SKILL.md create mode 100644 personas/_shared/skills/implementing-epss-score-for-vulnerability-prioritization/assets/template.md create mode 100644 personas/_shared/skills/implementing-epss-score-for-vulnerability-prioritization/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-epss-score-for-vulnerability-prioritization/references/standards.md create mode 100644 personas/_shared/skills/implementing-epss-score-for-vulnerability-prioritization/references/workflows.md create mode 100644 personas/_shared/skills/implementing-epss-score-for-vulnerability-prioritization/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-epss-score-for-vulnerability-prioritization/scripts/process.py create mode 100644 personas/_shared/skills/implementing-file-integrity-monitoring-with-aide/LICENSE create mode 100644 personas/_shared/skills/implementing-file-integrity-monitoring-with-aide/SKILL.md create mode 100644 personas/_shared/skills/implementing-file-integrity-monitoring-with-aide/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-file-integrity-monitoring-with-aide/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-fuzz-testing-in-cicd-with-aflplusplus/LICENSE create mode 100644 personas/_shared/skills/implementing-fuzz-testing-in-cicd-with-aflplusplus/SKILL.md create mode 100644 personas/_shared/skills/implementing-fuzz-testing-in-cicd-with-aflplusplus/assets/template.md create mode 100644 personas/_shared/skills/implementing-fuzz-testing-in-cicd-with-aflplusplus/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-fuzz-testing-in-cicd-with-aflplusplus/references/standards.md create mode 100644 personas/_shared/skills/implementing-fuzz-testing-in-cicd-with-aflplusplus/references/workflows.md create mode 100644 personas/_shared/skills/implementing-fuzz-testing-in-cicd-with-aflplusplus/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-fuzz-testing-in-cicd-with-aflplusplus/scripts/process.py create mode 100644 personas/_shared/skills/implementing-gcp-binary-authorization/LICENSE create mode 100644 personas/_shared/skills/implementing-gcp-binary-authorization/SKILL.md create mode 100644 personas/_shared/skills/implementing-gcp-binary-authorization/assets/template.md create mode 100644 personas/_shared/skills/implementing-gcp-binary-authorization/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-gcp-binary-authorization/references/standards.md create mode 100644 personas/_shared/skills/implementing-gcp-binary-authorization/references/workflows.md create mode 100644 personas/_shared/skills/implementing-gcp-binary-authorization/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-gcp-binary-authorization/scripts/process.py create mode 100644 personas/_shared/skills/implementing-gcp-organization-policy-constraints/LICENSE create mode 100644 personas/_shared/skills/implementing-gcp-organization-policy-constraints/SKILL.md create mode 100644 personas/_shared/skills/implementing-gcp-organization-policy-constraints/assets/template.md create mode 100644 personas/_shared/skills/implementing-gcp-organization-policy-constraints/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-gcp-organization-policy-constraints/references/standards.md create mode 100644 personas/_shared/skills/implementing-gcp-organization-policy-constraints/references/workflows.md create mode 100644 personas/_shared/skills/implementing-gcp-organization-policy-constraints/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-gcp-organization-policy-constraints/scripts/process.py create mode 100644 personas/_shared/skills/implementing-gcp-vpc-firewall-rules/LICENSE create mode 100644 personas/_shared/skills/implementing-gcp-vpc-firewall-rules/SKILL.md create mode 100644 personas/_shared/skills/implementing-gcp-vpc-firewall-rules/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-gcp-vpc-firewall-rules/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-gdpr-data-protection-controls/LICENSE create mode 100644 personas/_shared/skills/implementing-gdpr-data-protection-controls/SKILL.md create mode 100644 personas/_shared/skills/implementing-gdpr-data-protection-controls/assets/template.md create mode 100644 personas/_shared/skills/implementing-gdpr-data-protection-controls/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-gdpr-data-protection-controls/references/standards.md create mode 100644 personas/_shared/skills/implementing-gdpr-data-protection-controls/references/workflows.md create mode 100644 personas/_shared/skills/implementing-gdpr-data-protection-controls/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-gdpr-data-protection-controls/scripts/process.py create mode 100644 personas/_shared/skills/implementing-gdpr-data-subject-access-request/LICENSE create mode 100644 personas/_shared/skills/implementing-gdpr-data-subject-access-request/SKILL.md create mode 100644 personas/_shared/skills/implementing-gdpr-data-subject-access-request/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-gdpr-data-subject-access-request/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-github-advanced-security-for-code-scanning/LICENSE create mode 100644 personas/_shared/skills/implementing-github-advanced-security-for-code-scanning/SKILL.md create mode 100644 personas/_shared/skills/implementing-github-advanced-security-for-code-scanning/assets/template.md create mode 100644 personas/_shared/skills/implementing-github-advanced-security-for-code-scanning/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-github-advanced-security-for-code-scanning/references/standards.md create mode 100644 personas/_shared/skills/implementing-github-advanced-security-for-code-scanning/references/workflows.md create mode 100644 personas/_shared/skills/implementing-github-advanced-security-for-code-scanning/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-github-advanced-security-for-code-scanning/scripts/process.py create mode 100644 personas/_shared/skills/implementing-google-workspace-admin-security/LICENSE create mode 100644 personas/_shared/skills/implementing-google-workspace-admin-security/SKILL.md create mode 100644 personas/_shared/skills/implementing-google-workspace-admin-security/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-google-workspace-admin-security/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-google-workspace-phishing-protection/LICENSE create mode 100644 personas/_shared/skills/implementing-google-workspace-phishing-protection/SKILL.md create mode 100644 personas/_shared/skills/implementing-google-workspace-phishing-protection/assets/template.md create mode 100644 personas/_shared/skills/implementing-google-workspace-phishing-protection/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-google-workspace-phishing-protection/references/standards.md create mode 100644 personas/_shared/skills/implementing-google-workspace-phishing-protection/references/workflows.md create mode 100644 personas/_shared/skills/implementing-google-workspace-phishing-protection/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-google-workspace-phishing-protection/scripts/process.py create mode 100644 personas/_shared/skills/implementing-google-workspace-sso-configuration/LICENSE create mode 100644 personas/_shared/skills/implementing-google-workspace-sso-configuration/SKILL.md create mode 100644 personas/_shared/skills/implementing-google-workspace-sso-configuration/assets/template.md create mode 100644 personas/_shared/skills/implementing-google-workspace-sso-configuration/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-google-workspace-sso-configuration/references/standards.md create mode 100644 personas/_shared/skills/implementing-google-workspace-sso-configuration/references/workflows.md create mode 100644 personas/_shared/skills/implementing-google-workspace-sso-configuration/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-google-workspace-sso-configuration/scripts/process.py create mode 100644 personas/_shared/skills/implementing-hardware-security-key-authentication/LICENSE create mode 100644 personas/_shared/skills/implementing-hardware-security-key-authentication/SKILL.md create mode 100644 personas/_shared/skills/implementing-hardware-security-key-authentication/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-hardware-security-key-authentication/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-hashicorp-vault-dynamic-secrets/LICENSE create mode 100644 personas/_shared/skills/implementing-hashicorp-vault-dynamic-secrets/SKILL.md create mode 100644 personas/_shared/skills/implementing-hashicorp-vault-dynamic-secrets/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-hashicorp-vault-dynamic-secrets/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-honeypot-for-ransomware-detection/LICENSE create mode 100644 personas/_shared/skills/implementing-honeypot-for-ransomware-detection/SKILL.md create mode 100644 personas/_shared/skills/implementing-honeypot-for-ransomware-detection/assets/template.md create mode 100644 personas/_shared/skills/implementing-honeypot-for-ransomware-detection/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-honeypot-for-ransomware-detection/references/standards.md create mode 100644 personas/_shared/skills/implementing-honeypot-for-ransomware-detection/references/workflows.md create mode 100644 personas/_shared/skills/implementing-honeypot-for-ransomware-detection/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-honeypot-for-ransomware-detection/scripts/process.py create mode 100644 personas/_shared/skills/implementing-honeytokens-for-breach-detection/LICENSE create mode 100644 personas/_shared/skills/implementing-honeytokens-for-breach-detection/SKILL.md create mode 100644 personas/_shared/skills/implementing-honeytokens-for-breach-detection/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-honeytokens-for-breach-detection/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-ics-firewall-with-tofino/LICENSE create mode 100644 personas/_shared/skills/implementing-ics-firewall-with-tofino/SKILL.md create mode 100644 personas/_shared/skills/implementing-ics-firewall-with-tofino/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-ics-firewall-with-tofino/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-identity-governance-with-sailpoint/LICENSE create mode 100644 personas/_shared/skills/implementing-identity-governance-with-sailpoint/SKILL.md create mode 100644 personas/_shared/skills/implementing-identity-governance-with-sailpoint/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-identity-governance-with-sailpoint/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-identity-verification-for-zero-trust/LICENSE create mode 100644 personas/_shared/skills/implementing-identity-verification-for-zero-trust/SKILL.md create mode 100644 personas/_shared/skills/implementing-identity-verification-for-zero-trust/assets/template.md create mode 100644 personas/_shared/skills/implementing-identity-verification-for-zero-trust/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-identity-verification-for-zero-trust/references/standards.md create mode 100644 personas/_shared/skills/implementing-identity-verification-for-zero-trust/references/workflows.md create mode 100644 personas/_shared/skills/implementing-identity-verification-for-zero-trust/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-identity-verification-for-zero-trust/scripts/process.py create mode 100644 personas/_shared/skills/implementing-iec-62443-security-zones/LICENSE create mode 100644 personas/_shared/skills/implementing-iec-62443-security-zones/SKILL.md create mode 100644 personas/_shared/skills/implementing-iec-62443-security-zones/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-iec-62443-security-zones/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-image-provenance-verification-with-cosign/LICENSE create mode 100644 personas/_shared/skills/implementing-image-provenance-verification-with-cosign/SKILL.md create mode 100644 personas/_shared/skills/implementing-image-provenance-verification-with-cosign/assets/template.md create mode 100644 personas/_shared/skills/implementing-image-provenance-verification-with-cosign/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-image-provenance-verification-with-cosign/references/standards.md create mode 100644 personas/_shared/skills/implementing-image-provenance-verification-with-cosign/references/workflows.md create mode 100644 personas/_shared/skills/implementing-image-provenance-verification-with-cosign/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-image-provenance-verification-with-cosign/scripts/process.py create mode 100644 personas/_shared/skills/implementing-immutable-backup-with-restic/LICENSE create mode 100644 personas/_shared/skills/implementing-immutable-backup-with-restic/SKILL.md create mode 100644 personas/_shared/skills/implementing-immutable-backup-with-restic/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-immutable-backup-with-restic/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-infrastructure-as-code-security-scanning/LICENSE create mode 100644 personas/_shared/skills/implementing-infrastructure-as-code-security-scanning/SKILL.md create mode 100644 personas/_shared/skills/implementing-infrastructure-as-code-security-scanning/assets/template.md create mode 100644 personas/_shared/skills/implementing-infrastructure-as-code-security-scanning/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-infrastructure-as-code-security-scanning/references/standards.md create mode 100644 personas/_shared/skills/implementing-infrastructure-as-code-security-scanning/references/workflows.md create mode 100644 personas/_shared/skills/implementing-infrastructure-as-code-security-scanning/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-infrastructure-as-code-security-scanning/scripts/process.py create mode 100644 personas/_shared/skills/implementing-iso-27001-information-security-management/LICENSE create mode 100644 personas/_shared/skills/implementing-iso-27001-information-security-management/SKILL.md create mode 100644 personas/_shared/skills/implementing-iso-27001-information-security-management/assets/template.md create mode 100644 personas/_shared/skills/implementing-iso-27001-information-security-management/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-iso-27001-information-security-management/references/standards.md create mode 100644 personas/_shared/skills/implementing-iso-27001-information-security-management/references/workflows.md create mode 100644 personas/_shared/skills/implementing-iso-27001-information-security-management/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-iso-27001-information-security-management/scripts/process.py create mode 100644 personas/_shared/skills/implementing-just-in-time-access-provisioning/LICENSE create mode 100644 personas/_shared/skills/implementing-just-in-time-access-provisioning/SKILL.md create mode 100644 personas/_shared/skills/implementing-just-in-time-access-provisioning/assets/template.md create mode 100644 personas/_shared/skills/implementing-just-in-time-access-provisioning/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-just-in-time-access-provisioning/references/standards.md create mode 100644 personas/_shared/skills/implementing-just-in-time-access-provisioning/references/workflows.md create mode 100644 personas/_shared/skills/implementing-just-in-time-access-provisioning/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-just-in-time-access-provisioning/scripts/process.py create mode 100644 personas/_shared/skills/implementing-jwt-signing-and-verification/LICENSE create mode 100644 personas/_shared/skills/implementing-jwt-signing-and-verification/SKILL.md create mode 100644 personas/_shared/skills/implementing-jwt-signing-and-verification/assets/template.md create mode 100644 personas/_shared/skills/implementing-jwt-signing-and-verification/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-jwt-signing-and-verification/references/standards.md create mode 100644 personas/_shared/skills/implementing-jwt-signing-and-verification/references/workflows.md create mode 100644 personas/_shared/skills/implementing-jwt-signing-and-verification/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-jwt-signing-and-verification/scripts/process.py create mode 100644 personas/_shared/skills/implementing-kubernetes-network-policy-with-calico/LICENSE create mode 100644 personas/_shared/skills/implementing-kubernetes-network-policy-with-calico/SKILL.md create mode 100644 personas/_shared/skills/implementing-kubernetes-network-policy-with-calico/assets/template.md create mode 100644 personas/_shared/skills/implementing-kubernetes-network-policy-with-calico/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-kubernetes-network-policy-with-calico/references/standards.md create mode 100644 personas/_shared/skills/implementing-kubernetes-network-policy-with-calico/references/workflows.md create mode 100644 personas/_shared/skills/implementing-kubernetes-network-policy-with-calico/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-kubernetes-network-policy-with-calico/scripts/process.py create mode 100644 personas/_shared/skills/implementing-kubernetes-pod-security-standards/LICENSE create mode 100644 personas/_shared/skills/implementing-kubernetes-pod-security-standards/SKILL.md create mode 100644 personas/_shared/skills/implementing-kubernetes-pod-security-standards/assets/template.md create mode 100644 personas/_shared/skills/implementing-kubernetes-pod-security-standards/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-kubernetes-pod-security-standards/references/standards.md create mode 100644 personas/_shared/skills/implementing-kubernetes-pod-security-standards/references/workflows.md create mode 100644 personas/_shared/skills/implementing-kubernetes-pod-security-standards/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-kubernetes-pod-security-standards/scripts/process.py create mode 100644 personas/_shared/skills/implementing-llm-guardrails-for-security/LICENSE create mode 100644 personas/_shared/skills/implementing-llm-guardrails-for-security/SKILL.md create mode 100644 personas/_shared/skills/implementing-llm-guardrails-for-security/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-llm-guardrails-for-security/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-log-forwarding-with-fluentd/LICENSE create mode 100644 personas/_shared/skills/implementing-log-forwarding-with-fluentd/SKILL.md create mode 100644 personas/_shared/skills/implementing-log-forwarding-with-fluentd/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-log-forwarding-with-fluentd/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-log-integrity-with-blockchain/LICENSE create mode 100644 personas/_shared/skills/implementing-log-integrity-with-blockchain/SKILL.md create mode 100644 personas/_shared/skills/implementing-log-integrity-with-blockchain/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-log-integrity-with-blockchain/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-memory-protection-with-dep-aslr/LICENSE create mode 100644 personas/_shared/skills/implementing-memory-protection-with-dep-aslr/SKILL.md create mode 100644 personas/_shared/skills/implementing-memory-protection-with-dep-aslr/assets/template.md create mode 100644 personas/_shared/skills/implementing-memory-protection-with-dep-aslr/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-memory-protection-with-dep-aslr/references/standards.md create mode 100644 personas/_shared/skills/implementing-memory-protection-with-dep-aslr/references/workflows.md create mode 100644 personas/_shared/skills/implementing-memory-protection-with-dep-aslr/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-memory-protection-with-dep-aslr/scripts/process.py create mode 100644 personas/_shared/skills/implementing-microsegmentation-with-guardicore/LICENSE create mode 100644 personas/_shared/skills/implementing-microsegmentation-with-guardicore/SKILL.md create mode 100644 personas/_shared/skills/implementing-microsegmentation-with-guardicore/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-microsegmentation-with-guardicore/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-mimecast-targeted-attack-protection/LICENSE create mode 100644 personas/_shared/skills/implementing-mimecast-targeted-attack-protection/SKILL.md create mode 100644 personas/_shared/skills/implementing-mimecast-targeted-attack-protection/assets/template.md create mode 100644 personas/_shared/skills/implementing-mimecast-targeted-attack-protection/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-mimecast-targeted-attack-protection/references/standards.md create mode 100644 personas/_shared/skills/implementing-mimecast-targeted-attack-protection/references/workflows.md create mode 100644 personas/_shared/skills/implementing-mimecast-targeted-attack-protection/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-mimecast-targeted-attack-protection/scripts/process.py create mode 100644 personas/_shared/skills/implementing-mitre-attack-coverage-mapping/LICENSE create mode 100644 personas/_shared/skills/implementing-mitre-attack-coverage-mapping/SKILL.md create mode 100644 personas/_shared/skills/implementing-mitre-attack-coverage-mapping/assets/template.md create mode 100644 personas/_shared/skills/implementing-mitre-attack-coverage-mapping/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-mitre-attack-coverage-mapping/references/standards.md create mode 100644 personas/_shared/skills/implementing-mitre-attack-coverage-mapping/references/workflows.md create mode 100644 personas/_shared/skills/implementing-mitre-attack-coverage-mapping/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-mitre-attack-coverage-mapping/scripts/process.py create mode 100644 personas/_shared/skills/implementing-mobile-application-management/LICENSE create mode 100644 personas/_shared/skills/implementing-mobile-application-management/SKILL.md create mode 100644 personas/_shared/skills/implementing-mobile-application-management/assets/template.md create mode 100644 personas/_shared/skills/implementing-mobile-application-management/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-mobile-application-management/references/standards.md create mode 100644 personas/_shared/skills/implementing-mobile-application-management/references/workflows.md create mode 100644 personas/_shared/skills/implementing-mobile-application-management/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-mobile-application-management/scripts/process.py create mode 100644 personas/_shared/skills/implementing-mtls-for-zero-trust-services/LICENSE create mode 100644 personas/_shared/skills/implementing-mtls-for-zero-trust-services/SKILL.md create mode 100644 personas/_shared/skills/implementing-mtls-for-zero-trust-services/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-mtls-for-zero-trust-services/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-nerc-cip-compliance-controls/LICENSE create mode 100644 personas/_shared/skills/implementing-nerc-cip-compliance-controls/SKILL.md create mode 100644 personas/_shared/skills/implementing-nerc-cip-compliance-controls/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-nerc-cip-compliance-controls/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-network-access-control-with-cisco-ise/LICENSE create mode 100644 personas/_shared/skills/implementing-network-access-control-with-cisco-ise/SKILL.md create mode 100644 personas/_shared/skills/implementing-network-access-control-with-cisco-ise/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-network-access-control-with-cisco-ise/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-network-access-control/LICENSE create mode 100644 personas/_shared/skills/implementing-network-access-control/SKILL.md create mode 100644 personas/_shared/skills/implementing-network-access-control/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-network-access-control/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-network-deception-with-honeypots/LICENSE create mode 100644 personas/_shared/skills/implementing-network-deception-with-honeypots/SKILL.md create mode 100644 personas/_shared/skills/implementing-network-deception-with-honeypots/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-network-deception-with-honeypots/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-network-intrusion-prevention-with-suricata/LICENSE create mode 100644 personas/_shared/skills/implementing-network-intrusion-prevention-with-suricata/SKILL.md create mode 100644 personas/_shared/skills/implementing-network-intrusion-prevention-with-suricata/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-network-intrusion-prevention-with-suricata/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-network-policies-for-kubernetes/LICENSE create mode 100644 personas/_shared/skills/implementing-network-policies-for-kubernetes/SKILL.md create mode 100644 personas/_shared/skills/implementing-network-policies-for-kubernetes/assets/template.md create mode 100644 personas/_shared/skills/implementing-network-policies-for-kubernetes/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-network-policies-for-kubernetes/references/standards.md create mode 100644 personas/_shared/skills/implementing-network-policies-for-kubernetes/references/workflows.md create mode 100644 personas/_shared/skills/implementing-network-policies-for-kubernetes/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-network-policies-for-kubernetes/scripts/process.py create mode 100644 personas/_shared/skills/implementing-network-segmentation-for-ot/LICENSE create mode 100644 personas/_shared/skills/implementing-network-segmentation-for-ot/SKILL.md create mode 100644 personas/_shared/skills/implementing-network-segmentation-for-ot/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-network-segmentation-for-ot/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-network-segmentation-with-firewall-zones/LICENSE create mode 100644 personas/_shared/skills/implementing-network-segmentation-with-firewall-zones/SKILL.md create mode 100644 personas/_shared/skills/implementing-network-segmentation-with-firewall-zones/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-network-segmentation-with-firewall-zones/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-network-traffic-analysis-with-arkime/LICENSE create mode 100644 personas/_shared/skills/implementing-network-traffic-analysis-with-arkime/SKILL.md create mode 100644 personas/_shared/skills/implementing-network-traffic-analysis-with-arkime/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-network-traffic-analysis-with-arkime/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-network-traffic-baselining/LICENSE create mode 100644 personas/_shared/skills/implementing-network-traffic-baselining/SKILL.md create mode 100644 personas/_shared/skills/implementing-network-traffic-baselining/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-network-traffic-baselining/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-next-generation-firewall-with-palo-alto/LICENSE create mode 100644 personas/_shared/skills/implementing-next-generation-firewall-with-palo-alto/SKILL.md create mode 100644 personas/_shared/skills/implementing-next-generation-firewall-with-palo-alto/assets/template.md create mode 100644 personas/_shared/skills/implementing-next-generation-firewall-with-palo-alto/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-next-generation-firewall-with-palo-alto/references/standards.md create mode 100644 personas/_shared/skills/implementing-next-generation-firewall-with-palo-alto/references/workflows.md create mode 100644 personas/_shared/skills/implementing-next-generation-firewall-with-palo-alto/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-next-generation-firewall-with-palo-alto/scripts/process.py create mode 100644 personas/_shared/skills/implementing-opa-gatekeeper-for-policy-enforcement/LICENSE create mode 100644 personas/_shared/skills/implementing-opa-gatekeeper-for-policy-enforcement/SKILL.md create mode 100644 personas/_shared/skills/implementing-opa-gatekeeper-for-policy-enforcement/assets/template.md create mode 100644 personas/_shared/skills/implementing-opa-gatekeeper-for-policy-enforcement/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-opa-gatekeeper-for-policy-enforcement/references/standards.md create mode 100644 personas/_shared/skills/implementing-opa-gatekeeper-for-policy-enforcement/references/workflows.md create mode 100644 personas/_shared/skills/implementing-opa-gatekeeper-for-policy-enforcement/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-opa-gatekeeper-for-policy-enforcement/scripts/process.py create mode 100644 personas/_shared/skills/implementing-ot-incident-response-playbook/LICENSE create mode 100644 personas/_shared/skills/implementing-ot-incident-response-playbook/SKILL.md create mode 100644 personas/_shared/skills/implementing-ot-incident-response-playbook/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-ot-incident-response-playbook/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-ot-network-traffic-analysis-with-nozomi/LICENSE create mode 100644 personas/_shared/skills/implementing-ot-network-traffic-analysis-with-nozomi/SKILL.md create mode 100644 personas/_shared/skills/implementing-ot-network-traffic-analysis-with-nozomi/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-ot-network-traffic-analysis-with-nozomi/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-pam-for-database-access/LICENSE create mode 100644 personas/_shared/skills/implementing-pam-for-database-access/SKILL.md create mode 100644 personas/_shared/skills/implementing-pam-for-database-access/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-pam-for-database-access/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-passwordless-auth-with-microsoft-entra/LICENSE create mode 100644 personas/_shared/skills/implementing-passwordless-auth-with-microsoft-entra/SKILL.md create mode 100644 personas/_shared/skills/implementing-passwordless-auth-with-microsoft-entra/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-passwordless-auth-with-microsoft-entra/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-passwordless-authentication-with-fido2/LICENSE create mode 100644 personas/_shared/skills/implementing-passwordless-authentication-with-fido2/SKILL.md create mode 100644 personas/_shared/skills/implementing-passwordless-authentication-with-fido2/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-passwordless-authentication-with-fido2/references/standards.md create mode 100644 personas/_shared/skills/implementing-passwordless-authentication-with-fido2/references/workflows.md create mode 100644 personas/_shared/skills/implementing-passwordless-authentication-with-fido2/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-patch-management-for-ot-systems/LICENSE create mode 100644 personas/_shared/skills/implementing-patch-management-for-ot-systems/SKILL.md create mode 100644 personas/_shared/skills/implementing-patch-management-for-ot-systems/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-patch-management-for-ot-systems/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-patch-management-workflow/LICENSE create mode 100644 personas/_shared/skills/implementing-patch-management-workflow/SKILL.md create mode 100644 personas/_shared/skills/implementing-patch-management-workflow/assets/template.md create mode 100644 personas/_shared/skills/implementing-patch-management-workflow/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-patch-management-workflow/references/standards.md create mode 100644 personas/_shared/skills/implementing-patch-management-workflow/references/workflows.md create mode 100644 personas/_shared/skills/implementing-patch-management-workflow/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-patch-management-workflow/scripts/process.py create mode 100644 personas/_shared/skills/implementing-pci-dss-compliance-controls/LICENSE create mode 100644 personas/_shared/skills/implementing-pci-dss-compliance-controls/SKILL.md create mode 100644 personas/_shared/skills/implementing-pci-dss-compliance-controls/assets/template.md create mode 100644 personas/_shared/skills/implementing-pci-dss-compliance-controls/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-pci-dss-compliance-controls/references/standards.md create mode 100644 personas/_shared/skills/implementing-pci-dss-compliance-controls/references/workflows.md create mode 100644 personas/_shared/skills/implementing-pci-dss-compliance-controls/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-pod-security-admission-controller/LICENSE create mode 100644 personas/_shared/skills/implementing-pod-security-admission-controller/SKILL.md create mode 100644 personas/_shared/skills/implementing-pod-security-admission-controller/assets/template.md create mode 100644 personas/_shared/skills/implementing-pod-security-admission-controller/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-pod-security-admission-controller/references/standards.md create mode 100644 personas/_shared/skills/implementing-pod-security-admission-controller/references/workflows.md create mode 100644 personas/_shared/skills/implementing-pod-security-admission-controller/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-pod-security-admission-controller/scripts/process.py create mode 100644 personas/_shared/skills/implementing-policy-as-code-with-open-policy-agent/LICENSE create mode 100644 personas/_shared/skills/implementing-policy-as-code-with-open-policy-agent/SKILL.md create mode 100644 personas/_shared/skills/implementing-policy-as-code-with-open-policy-agent/assets/template.md create mode 100644 personas/_shared/skills/implementing-policy-as-code-with-open-policy-agent/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-policy-as-code-with-open-policy-agent/references/standards.md create mode 100644 personas/_shared/skills/implementing-policy-as-code-with-open-policy-agent/references/workflows.md create mode 100644 personas/_shared/skills/implementing-policy-as-code-with-open-policy-agent/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-policy-as-code-with-open-policy-agent/scripts/process.py create mode 100644 personas/_shared/skills/implementing-privileged-access-management-with-cyberark/LICENSE create mode 100644 personas/_shared/skills/implementing-privileged-access-management-with-cyberark/SKILL.md create mode 100644 personas/_shared/skills/implementing-privileged-access-management-with-cyberark/assets/template.md create mode 100644 personas/_shared/skills/implementing-privileged-access-management-with-cyberark/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-privileged-access-management-with-cyberark/references/standards.md create mode 100644 personas/_shared/skills/implementing-privileged-access-management-with-cyberark/references/workflows.md create mode 100644 personas/_shared/skills/implementing-privileged-access-management-with-cyberark/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-privileged-access-management-with-cyberark/scripts/process.py create mode 100644 personas/_shared/skills/implementing-privileged-access-workstation/LICENSE create mode 100644 personas/_shared/skills/implementing-privileged-access-workstation/SKILL.md create mode 100644 personas/_shared/skills/implementing-privileged-access-workstation/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-privileged-access-workstation/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-privileged-session-monitoring/LICENSE create mode 100644 personas/_shared/skills/implementing-privileged-session-monitoring/SKILL.md create mode 100644 personas/_shared/skills/implementing-privileged-session-monitoring/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-privileged-session-monitoring/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-proofpoint-email-security-gateway/LICENSE create mode 100644 personas/_shared/skills/implementing-proofpoint-email-security-gateway/SKILL.md create mode 100644 personas/_shared/skills/implementing-proofpoint-email-security-gateway/assets/template.md create mode 100644 personas/_shared/skills/implementing-proofpoint-email-security-gateway/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-proofpoint-email-security-gateway/references/standards.md create mode 100644 personas/_shared/skills/implementing-proofpoint-email-security-gateway/references/workflows.md create mode 100644 personas/_shared/skills/implementing-proofpoint-email-security-gateway/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-proofpoint-email-security-gateway/scripts/process.py create mode 100644 personas/_shared/skills/implementing-purdue-model-network-segmentation/LICENSE create mode 100644 personas/_shared/skills/implementing-purdue-model-network-segmentation/SKILL.md create mode 100644 personas/_shared/skills/implementing-purdue-model-network-segmentation/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-purdue-model-network-segmentation/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-ransomware-backup-strategy/LICENSE create mode 100644 personas/_shared/skills/implementing-ransomware-backup-strategy/SKILL.md create mode 100644 personas/_shared/skills/implementing-ransomware-backup-strategy/assets/template.md create mode 100644 personas/_shared/skills/implementing-ransomware-backup-strategy/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-ransomware-backup-strategy/references/standards.md create mode 100644 personas/_shared/skills/implementing-ransomware-backup-strategy/references/workflows.md create mode 100644 personas/_shared/skills/implementing-ransomware-backup-strategy/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-ransomware-backup-strategy/scripts/process.py create mode 100644 personas/_shared/skills/implementing-ransomware-kill-switch-detection/LICENSE create mode 100644 personas/_shared/skills/implementing-ransomware-kill-switch-detection/SKILL.md create mode 100644 personas/_shared/skills/implementing-ransomware-kill-switch-detection/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-ransomware-kill-switch-detection/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-rapid7-insightvm-for-scanning/LICENSE create mode 100644 personas/_shared/skills/implementing-rapid7-insightvm-for-scanning/SKILL.md create mode 100644 personas/_shared/skills/implementing-rapid7-insightvm-for-scanning/assets/template.md create mode 100644 personas/_shared/skills/implementing-rapid7-insightvm-for-scanning/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-rapid7-insightvm-for-scanning/references/standards.md create mode 100644 personas/_shared/skills/implementing-rapid7-insightvm-for-scanning/references/workflows.md create mode 100644 personas/_shared/skills/implementing-rapid7-insightvm-for-scanning/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-rapid7-insightvm-for-scanning/scripts/process.py create mode 100644 personas/_shared/skills/implementing-rbac-hardening-for-kubernetes/LICENSE create mode 100644 personas/_shared/skills/implementing-rbac-hardening-for-kubernetes/SKILL.md create mode 100644 personas/_shared/skills/implementing-rbac-hardening-for-kubernetes/assets/template.md create mode 100644 personas/_shared/skills/implementing-rbac-hardening-for-kubernetes/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-rbac-hardening-for-kubernetes/references/standards.md create mode 100644 personas/_shared/skills/implementing-rbac-hardening-for-kubernetes/references/workflows.md create mode 100644 personas/_shared/skills/implementing-rbac-hardening-for-kubernetes/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-rbac-hardening-for-kubernetes/scripts/process.py create mode 100644 personas/_shared/skills/implementing-rsa-key-pair-management/LICENSE create mode 100644 personas/_shared/skills/implementing-rsa-key-pair-management/SKILL.md create mode 100644 personas/_shared/skills/implementing-rsa-key-pair-management/assets/template.md create mode 100644 personas/_shared/skills/implementing-rsa-key-pair-management/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-rsa-key-pair-management/references/standards.md create mode 100644 personas/_shared/skills/implementing-rsa-key-pair-management/references/workflows.md create mode 100644 personas/_shared/skills/implementing-rsa-key-pair-management/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-rsa-key-pair-management/scripts/process.py create mode 100644 personas/_shared/skills/implementing-runtime-application-self-protection/LICENSE create mode 100644 personas/_shared/skills/implementing-runtime-application-self-protection/SKILL.md create mode 100644 personas/_shared/skills/implementing-runtime-application-self-protection/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-runtime-application-self-protection/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-runtime-security-with-tetragon/LICENSE create mode 100644 personas/_shared/skills/implementing-runtime-security-with-tetragon/SKILL.md create mode 100644 personas/_shared/skills/implementing-runtime-security-with-tetragon/assets/template.md create mode 100644 personas/_shared/skills/implementing-runtime-security-with-tetragon/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-runtime-security-with-tetragon/references/standards.md create mode 100644 personas/_shared/skills/implementing-runtime-security-with-tetragon/references/workflows.md create mode 100644 personas/_shared/skills/implementing-runtime-security-with-tetragon/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-runtime-security-with-tetragon/scripts/process.py create mode 100644 personas/_shared/skills/implementing-saml-sso-with-okta/LICENSE create mode 100644 personas/_shared/skills/implementing-saml-sso-with-okta/SKILL.md create mode 100644 personas/_shared/skills/implementing-saml-sso-with-okta/assets/template.md create mode 100644 personas/_shared/skills/implementing-saml-sso-with-okta/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-saml-sso-with-okta/references/standards.md create mode 100644 personas/_shared/skills/implementing-saml-sso-with-okta/references/workflows.md create mode 100644 personas/_shared/skills/implementing-saml-sso-with-okta/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-saml-sso-with-okta/scripts/process.py create mode 100644 personas/_shared/skills/implementing-scim-provisioning-with-okta/LICENSE create mode 100644 personas/_shared/skills/implementing-scim-provisioning-with-okta/SKILL.md create mode 100644 personas/_shared/skills/implementing-scim-provisioning-with-okta/assets/template.md create mode 100644 personas/_shared/skills/implementing-scim-provisioning-with-okta/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-scim-provisioning-with-okta/references/standards.md create mode 100644 personas/_shared/skills/implementing-scim-provisioning-with-okta/references/workflows.md create mode 100644 personas/_shared/skills/implementing-scim-provisioning-with-okta/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-scim-provisioning-with-okta/scripts/process.py create mode 100644 personas/_shared/skills/implementing-secret-scanning-with-gitleaks/LICENSE create mode 100644 personas/_shared/skills/implementing-secret-scanning-with-gitleaks/SKILL.md create mode 100644 personas/_shared/skills/implementing-secret-scanning-with-gitleaks/assets/template.md create mode 100644 personas/_shared/skills/implementing-secret-scanning-with-gitleaks/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-secret-scanning-with-gitleaks/references/standards.md create mode 100644 personas/_shared/skills/implementing-secret-scanning-with-gitleaks/references/workflows.md create mode 100644 personas/_shared/skills/implementing-secret-scanning-with-gitleaks/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-secret-scanning-with-gitleaks/scripts/process.py create mode 100644 personas/_shared/skills/implementing-secrets-management-with-vault/LICENSE create mode 100644 personas/_shared/skills/implementing-secrets-management-with-vault/SKILL.md create mode 100644 personas/_shared/skills/implementing-secrets-management-with-vault/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-secrets-management-with-vault/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-secrets-scanning-in-ci-cd/LICENSE create mode 100644 personas/_shared/skills/implementing-secrets-scanning-in-ci-cd/SKILL.md create mode 100644 personas/_shared/skills/implementing-secrets-scanning-in-ci-cd/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-secrets-scanning-in-ci-cd/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-security-chaos-engineering/LICENSE create mode 100644 personas/_shared/skills/implementing-security-chaos-engineering/SKILL.md create mode 100644 personas/_shared/skills/implementing-security-chaos-engineering/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-security-chaos-engineering/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-security-information-sharing-with-stix2/LICENSE create mode 100644 personas/_shared/skills/implementing-security-information-sharing-with-stix2/SKILL.md create mode 100644 personas/_shared/skills/implementing-security-information-sharing-with-stix2/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-security-information-sharing-with-stix2/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-security-monitoring-with-datadog/LICENSE create mode 100644 personas/_shared/skills/implementing-security-monitoring-with-datadog/SKILL.md create mode 100644 personas/_shared/skills/implementing-security-monitoring-with-datadog/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-security-monitoring-with-datadog/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-semgrep-for-custom-sast-rules/LICENSE create mode 100644 personas/_shared/skills/implementing-semgrep-for-custom-sast-rules/SKILL.md create mode 100644 personas/_shared/skills/implementing-semgrep-for-custom-sast-rules/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-semgrep-for-custom-sast-rules/references/standards.md create mode 100644 personas/_shared/skills/implementing-semgrep-for-custom-sast-rules/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-siem-correlation-rules-for-apt/LICENSE create mode 100644 personas/_shared/skills/implementing-siem-correlation-rules-for-apt/SKILL.md create mode 100644 personas/_shared/skills/implementing-siem-correlation-rules-for-apt/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-siem-correlation-rules-for-apt/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-siem-use-case-tuning/LICENSE create mode 100644 personas/_shared/skills/implementing-siem-use-case-tuning/SKILL.md create mode 100644 personas/_shared/skills/implementing-siem-use-case-tuning/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-siem-use-case-tuning/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-siem-use-cases-for-detection/LICENSE create mode 100644 personas/_shared/skills/implementing-siem-use-cases-for-detection/SKILL.md create mode 100644 personas/_shared/skills/implementing-siem-use-cases-for-detection/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-siem-use-cases-for-detection/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-sigstore-for-software-signing/LICENSE create mode 100644 personas/_shared/skills/implementing-sigstore-for-software-signing/SKILL.md create mode 100644 personas/_shared/skills/implementing-sigstore-for-software-signing/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-sigstore-for-software-signing/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-soar-automation-with-phantom/LICENSE create mode 100644 personas/_shared/skills/implementing-soar-automation-with-phantom/SKILL.md create mode 100644 personas/_shared/skills/implementing-soar-automation-with-phantom/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-soar-automation-with-phantom/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-soar-playbook-for-phishing/LICENSE create mode 100644 personas/_shared/skills/implementing-soar-playbook-for-phishing/SKILL.md create mode 100644 personas/_shared/skills/implementing-soar-playbook-for-phishing/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-soar-playbook-for-phishing/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-soar-playbook-with-palo-alto-xsoar/LICENSE create mode 100644 personas/_shared/skills/implementing-soar-playbook-with-palo-alto-xsoar/SKILL.md create mode 100644 personas/_shared/skills/implementing-soar-playbook-with-palo-alto-xsoar/assets/template.md create mode 100644 personas/_shared/skills/implementing-soar-playbook-with-palo-alto-xsoar/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-soar-playbook-with-palo-alto-xsoar/references/standards.md create mode 100644 personas/_shared/skills/implementing-soar-playbook-with-palo-alto-xsoar/references/workflows.md create mode 100644 personas/_shared/skills/implementing-soar-playbook-with-palo-alto-xsoar/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-soar-playbook-with-palo-alto-xsoar/scripts/process.py create mode 100644 personas/_shared/skills/implementing-stix-taxii-feed-integration/LICENSE create mode 100644 personas/_shared/skills/implementing-stix-taxii-feed-integration/SKILL.md create mode 100644 personas/_shared/skills/implementing-stix-taxii-feed-integration/assets/template.md create mode 100644 personas/_shared/skills/implementing-stix-taxii-feed-integration/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-stix-taxii-feed-integration/references/standards.md create mode 100644 personas/_shared/skills/implementing-stix-taxii-feed-integration/references/workflows.md create mode 100644 personas/_shared/skills/implementing-stix-taxii-feed-integration/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-stix-taxii-feed-integration/scripts/process.py create mode 100644 personas/_shared/skills/implementing-supply-chain-security-with-in-toto/LICENSE create mode 100644 personas/_shared/skills/implementing-supply-chain-security-with-in-toto/SKILL.md create mode 100644 personas/_shared/skills/implementing-supply-chain-security-with-in-toto/assets/template.md create mode 100644 personas/_shared/skills/implementing-supply-chain-security-with-in-toto/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-supply-chain-security-with-in-toto/references/standards.md create mode 100644 personas/_shared/skills/implementing-supply-chain-security-with-in-toto/references/workflows.md create mode 100644 personas/_shared/skills/implementing-supply-chain-security-with-in-toto/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-supply-chain-security-with-in-toto/scripts/process.py create mode 100644 personas/_shared/skills/implementing-syslog-centralization-with-rsyslog/LICENSE create mode 100644 personas/_shared/skills/implementing-syslog-centralization-with-rsyslog/SKILL.md create mode 100644 personas/_shared/skills/implementing-syslog-centralization-with-rsyslog/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-syslog-centralization-with-rsyslog/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-taxii-server-with-opentaxii/LICENSE create mode 100644 personas/_shared/skills/implementing-taxii-server-with-opentaxii/SKILL.md create mode 100644 personas/_shared/skills/implementing-taxii-server-with-opentaxii/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-taxii-server-with-opentaxii/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-threat-intelligence-lifecycle-management/LICENSE create mode 100644 personas/_shared/skills/implementing-threat-intelligence-lifecycle-management/SKILL.md create mode 100644 personas/_shared/skills/implementing-threat-intelligence-lifecycle-management/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-threat-intelligence-lifecycle-management/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-threat-modeling-with-mitre-attack/LICENSE create mode 100644 personas/_shared/skills/implementing-threat-modeling-with-mitre-attack/SKILL.md create mode 100644 personas/_shared/skills/implementing-threat-modeling-with-mitre-attack/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-threat-modeling-with-mitre-attack/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-ticketing-system-for-incidents/LICENSE create mode 100644 personas/_shared/skills/implementing-ticketing-system-for-incidents/SKILL.md create mode 100644 personas/_shared/skills/implementing-ticketing-system-for-incidents/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-ticketing-system-for-incidents/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-usb-device-control-policy/LICENSE create mode 100644 personas/_shared/skills/implementing-usb-device-control-policy/SKILL.md create mode 100644 personas/_shared/skills/implementing-usb-device-control-policy/assets/template.md create mode 100644 personas/_shared/skills/implementing-usb-device-control-policy/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-usb-device-control-policy/references/standards.md create mode 100644 personas/_shared/skills/implementing-usb-device-control-policy/references/workflows.md create mode 100644 personas/_shared/skills/implementing-usb-device-control-policy/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-usb-device-control-policy/scripts/process.py create mode 100644 personas/_shared/skills/implementing-velociraptor-for-ir-collection/LICENSE create mode 100644 personas/_shared/skills/implementing-velociraptor-for-ir-collection/SKILL.md create mode 100644 personas/_shared/skills/implementing-velociraptor-for-ir-collection/assets/template.md create mode 100644 personas/_shared/skills/implementing-velociraptor-for-ir-collection/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-velociraptor-for-ir-collection/references/standards.md create mode 100644 personas/_shared/skills/implementing-velociraptor-for-ir-collection/references/workflows.md create mode 100644 personas/_shared/skills/implementing-velociraptor-for-ir-collection/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-velociraptor-for-ir-collection/scripts/process.py create mode 100644 personas/_shared/skills/implementing-vulnerability-management-with-greenbone/LICENSE create mode 100644 personas/_shared/skills/implementing-vulnerability-management-with-greenbone/SKILL.md create mode 100644 personas/_shared/skills/implementing-vulnerability-management-with-greenbone/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-vulnerability-management-with-greenbone/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-vulnerability-remediation-sla/LICENSE create mode 100644 personas/_shared/skills/implementing-vulnerability-remediation-sla/SKILL.md create mode 100644 personas/_shared/skills/implementing-vulnerability-remediation-sla/assets/template.md create mode 100644 personas/_shared/skills/implementing-vulnerability-remediation-sla/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-vulnerability-remediation-sla/references/standards.md create mode 100644 personas/_shared/skills/implementing-vulnerability-remediation-sla/references/workflows.md create mode 100644 personas/_shared/skills/implementing-vulnerability-remediation-sla/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-vulnerability-remediation-sla/scripts/process.py create mode 100644 personas/_shared/skills/implementing-vulnerability-sla-breach-alerting/LICENSE create mode 100644 personas/_shared/skills/implementing-vulnerability-sla-breach-alerting/SKILL.md create mode 100644 personas/_shared/skills/implementing-vulnerability-sla-breach-alerting/assets/template.md create mode 100644 personas/_shared/skills/implementing-vulnerability-sla-breach-alerting/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-vulnerability-sla-breach-alerting/references/standards.md create mode 100644 personas/_shared/skills/implementing-vulnerability-sla-breach-alerting/references/workflows.md create mode 100644 personas/_shared/skills/implementing-vulnerability-sla-breach-alerting/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-vulnerability-sla-breach-alerting/scripts/process.py create mode 100644 personas/_shared/skills/implementing-web-application-logging-with-modsecurity/LICENSE create mode 100644 personas/_shared/skills/implementing-web-application-logging-with-modsecurity/SKILL.md create mode 100644 personas/_shared/skills/implementing-web-application-logging-with-modsecurity/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-web-application-logging-with-modsecurity/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-zero-knowledge-proof-for-authentication/LICENSE create mode 100644 personas/_shared/skills/implementing-zero-knowledge-proof-for-authentication/SKILL.md create mode 100644 personas/_shared/skills/implementing-zero-knowledge-proof-for-authentication/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-zero-knowledge-proof-for-authentication/references/standards.md create mode 100644 personas/_shared/skills/implementing-zero-knowledge-proof-for-authentication/references/workflows.md create mode 100644 personas/_shared/skills/implementing-zero-knowledge-proof-for-authentication/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-zero-standing-privilege-with-cyberark/LICENSE create mode 100644 personas/_shared/skills/implementing-zero-standing-privilege-with-cyberark/SKILL.md create mode 100644 personas/_shared/skills/implementing-zero-standing-privilege-with-cyberark/assets/template.md create mode 100644 personas/_shared/skills/implementing-zero-standing-privilege-with-cyberark/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-zero-standing-privilege-with-cyberark/references/standards.md create mode 100644 personas/_shared/skills/implementing-zero-standing-privilege-with-cyberark/references/workflows.md create mode 100644 personas/_shared/skills/implementing-zero-standing-privilege-with-cyberark/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-zero-standing-privilege-with-cyberark/scripts/process.py create mode 100644 personas/_shared/skills/implementing-zero-trust-dns-with-nextdns/LICENSE create mode 100644 personas/_shared/skills/implementing-zero-trust-dns-with-nextdns/SKILL.md create mode 100644 personas/_shared/skills/implementing-zero-trust-dns-with-nextdns/assets/template.md create mode 100644 personas/_shared/skills/implementing-zero-trust-dns-with-nextdns/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-zero-trust-dns-with-nextdns/references/standards.md create mode 100644 personas/_shared/skills/implementing-zero-trust-dns-with-nextdns/references/workflows.md create mode 100644 personas/_shared/skills/implementing-zero-trust-dns-with-nextdns/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-zero-trust-dns-with-nextdns/scripts/process.py create mode 100644 personas/_shared/skills/implementing-zero-trust-for-saas-applications/LICENSE create mode 100644 personas/_shared/skills/implementing-zero-trust-for-saas-applications/SKILL.md create mode 100644 personas/_shared/skills/implementing-zero-trust-for-saas-applications/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-zero-trust-for-saas-applications/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-zero-trust-in-cloud/LICENSE create mode 100644 personas/_shared/skills/implementing-zero-trust-in-cloud/SKILL.md create mode 100644 personas/_shared/skills/implementing-zero-trust-in-cloud/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-zero-trust-in-cloud/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-zero-trust-network-access-with-zscaler/LICENSE create mode 100644 personas/_shared/skills/implementing-zero-trust-network-access-with-zscaler/SKILL.md create mode 100644 personas/_shared/skills/implementing-zero-trust-network-access-with-zscaler/assets/template.md create mode 100644 personas/_shared/skills/implementing-zero-trust-network-access-with-zscaler/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-zero-trust-network-access-with-zscaler/references/standards.md create mode 100644 personas/_shared/skills/implementing-zero-trust-network-access-with-zscaler/references/workflows.md create mode 100644 personas/_shared/skills/implementing-zero-trust-network-access-with-zscaler/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-zero-trust-network-access-with-zscaler/scripts/process.py create mode 100644 personas/_shared/skills/implementing-zero-trust-network-access/LICENSE create mode 100644 personas/_shared/skills/implementing-zero-trust-network-access/SKILL.md create mode 100644 personas/_shared/skills/implementing-zero-trust-network-access/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-zero-trust-network-access/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-zero-trust-with-beyondcorp/LICENSE create mode 100644 personas/_shared/skills/implementing-zero-trust-with-beyondcorp/SKILL.md create mode 100644 personas/_shared/skills/implementing-zero-trust-with-beyondcorp/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-zero-trust-with-beyondcorp/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-zero-trust-with-hashicorp-boundary/LICENSE create mode 100644 personas/_shared/skills/implementing-zero-trust-with-hashicorp-boundary/SKILL.md create mode 100644 personas/_shared/skills/implementing-zero-trust-with-hashicorp-boundary/assets/template.md create mode 100644 personas/_shared/skills/implementing-zero-trust-with-hashicorp-boundary/references/api-reference.md create mode 100644 personas/_shared/skills/implementing-zero-trust-with-hashicorp-boundary/references/standards.md create mode 100644 personas/_shared/skills/implementing-zero-trust-with-hashicorp-boundary/references/workflows.md create mode 100644 personas/_shared/skills/implementing-zero-trust-with-hashicorp-boundary/scripts/agent.py create mode 100644 personas/_shared/skills/implementing-zero-trust-with-hashicorp-boundary/scripts/process.py create mode 100644 personas/_shared/skills/integrating-dast-with-owasp-zap-in-pipeline/LICENSE create mode 100644 personas/_shared/skills/integrating-dast-with-owasp-zap-in-pipeline/SKILL.md create mode 100644 personas/_shared/skills/integrating-dast-with-owasp-zap-in-pipeline/assets/template.md create mode 100644 personas/_shared/skills/integrating-dast-with-owasp-zap-in-pipeline/references/api-reference.md create mode 100644 personas/_shared/skills/integrating-dast-with-owasp-zap-in-pipeline/references/standards.md create mode 100644 personas/_shared/skills/integrating-dast-with-owasp-zap-in-pipeline/references/workflows.md create mode 100644 personas/_shared/skills/integrating-dast-with-owasp-zap-in-pipeline/scripts/agent.py create mode 100644 personas/_shared/skills/integrating-dast-with-owasp-zap-in-pipeline/scripts/process.py create mode 100644 personas/_shared/skills/integrating-sast-into-github-actions-pipeline/LICENSE create mode 100644 personas/_shared/skills/integrating-sast-into-github-actions-pipeline/SKILL.md create mode 100644 personas/_shared/skills/integrating-sast-into-github-actions-pipeline/assets/template.md create mode 100644 personas/_shared/skills/integrating-sast-into-github-actions-pipeline/references/api-reference.md create mode 100644 personas/_shared/skills/integrating-sast-into-github-actions-pipeline/references/standards.md create mode 100644 personas/_shared/skills/integrating-sast-into-github-actions-pipeline/references/workflows.md create mode 100644 personas/_shared/skills/integrating-sast-into-github-actions-pipeline/scripts/agent.py create mode 100644 personas/_shared/skills/integrating-sast-into-github-actions-pipeline/scripts/process.py create mode 100644 personas/_shared/skills/intercepting-mobile-traffic-with-burpsuite/LICENSE create mode 100644 personas/_shared/skills/intercepting-mobile-traffic-with-burpsuite/SKILL.md create mode 100644 personas/_shared/skills/intercepting-mobile-traffic-with-burpsuite/assets/template.md create mode 100644 personas/_shared/skills/intercepting-mobile-traffic-with-burpsuite/references/api-reference.md create mode 100644 personas/_shared/skills/intercepting-mobile-traffic-with-burpsuite/references/standards.md create mode 100644 personas/_shared/skills/intercepting-mobile-traffic-with-burpsuite/references/workflows.md create mode 100644 personas/_shared/skills/intercepting-mobile-traffic-with-burpsuite/scripts/agent.py create mode 100644 personas/_shared/skills/intercepting-mobile-traffic-with-burpsuite/scripts/process.py create mode 100644 personas/_shared/skills/investigating-insider-threat-indicators/LICENSE create mode 100644 personas/_shared/skills/investigating-insider-threat-indicators/SKILL.md create mode 100644 personas/_shared/skills/investigating-insider-threat-indicators/references/api-reference.md create mode 100644 personas/_shared/skills/investigating-insider-threat-indicators/scripts/agent.py create mode 100644 personas/_shared/skills/investigating-phishing-email-incident/LICENSE create mode 100644 personas/_shared/skills/investigating-phishing-email-incident/SKILL.md create mode 100644 personas/_shared/skills/investigating-phishing-email-incident/references/api-reference.md create mode 100644 personas/_shared/skills/investigating-phishing-email-incident/scripts/agent.py create mode 100644 personas/_shared/skills/investigating-ransomware-attack-artifacts/LICENSE create mode 100644 personas/_shared/skills/investigating-ransomware-attack-artifacts/SKILL.md create mode 100644 personas/_shared/skills/investigating-ransomware-attack-artifacts/references/api-reference.md create mode 100644 personas/_shared/skills/investigating-ransomware-attack-artifacts/scripts/agent.py create mode 100644 personas/_shared/skills/managing-cloud-identity-with-okta/LICENSE create mode 100644 personas/_shared/skills/managing-cloud-identity-with-okta/SKILL.md create mode 100644 personas/_shared/skills/managing-cloud-identity-with-okta/references/api-reference.md create mode 100644 personas/_shared/skills/managing-cloud-identity-with-okta/scripts/agent.py create mode 100644 personas/_shared/skills/managing-intelligence-lifecycle/LICENSE create mode 100644 personas/_shared/skills/managing-intelligence-lifecycle/SKILL.md create mode 100644 personas/_shared/skills/managing-intelligence-lifecycle/references/api-reference.md create mode 100644 personas/_shared/skills/managing-intelligence-lifecycle/scripts/agent.py create mode 100644 personas/_shared/skills/mapping-mitre-attack-techniques/LICENSE create mode 100644 personas/_shared/skills/mapping-mitre-attack-techniques/SKILL.md create mode 100644 personas/_shared/skills/mapping-mitre-attack-techniques/references/api-reference.md create mode 100644 personas/_shared/skills/mapping-mitre-attack-techniques/scripts/agent.py create mode 100644 personas/_shared/skills/monitoring-darkweb-sources/LICENSE create mode 100644 personas/_shared/skills/monitoring-darkweb-sources/SKILL.md create mode 100644 personas/_shared/skills/monitoring-darkweb-sources/references/api-reference.md create mode 100644 personas/_shared/skills/monitoring-darkweb-sources/scripts/agent.py create mode 100644 personas/_shared/skills/monitoring-scada-modbus-traffic-anomalies/LICENSE create mode 100644 personas/_shared/skills/monitoring-scada-modbus-traffic-anomalies/SKILL.md create mode 100644 personas/_shared/skills/monitoring-scada-modbus-traffic-anomalies/references/api-reference.md create mode 100644 personas/_shared/skills/monitoring-scada-modbus-traffic-anomalies/scripts/agent.py create mode 100644 personas/_shared/skills/performing-access-recertification-with-saviynt/LICENSE create mode 100644 personas/_shared/skills/performing-access-recertification-with-saviynt/SKILL.md create mode 100644 personas/_shared/skills/performing-access-recertification-with-saviynt/assets/template.md create mode 100644 personas/_shared/skills/performing-access-recertification-with-saviynt/references/api-reference.md create mode 100644 personas/_shared/skills/performing-access-recertification-with-saviynt/references/standards.md create mode 100644 personas/_shared/skills/performing-access-recertification-with-saviynt/references/workflows.md create mode 100644 personas/_shared/skills/performing-access-recertification-with-saviynt/scripts/agent.py create mode 100644 personas/_shared/skills/performing-access-recertification-with-saviynt/scripts/process.py create mode 100644 personas/_shared/skills/performing-access-review-and-certification/LICENSE create mode 100644 personas/_shared/skills/performing-access-review-and-certification/SKILL.md create mode 100644 personas/_shared/skills/performing-access-review-and-certification/assets/template.md create mode 100644 personas/_shared/skills/performing-access-review-and-certification/references/api-reference.md create mode 100644 personas/_shared/skills/performing-access-review-and-certification/references/standards.md create mode 100644 personas/_shared/skills/performing-access-review-and-certification/references/workflows.md create mode 100644 personas/_shared/skills/performing-access-review-and-certification/scripts/agent.py create mode 100644 personas/_shared/skills/performing-access-review-and-certification/scripts/process.py create mode 100644 personas/_shared/skills/performing-active-directory-bloodhound-analysis/LICENSE create mode 100644 personas/_shared/skills/performing-active-directory-bloodhound-analysis/SKILL.md create mode 100644 personas/_shared/skills/performing-active-directory-bloodhound-analysis/assets/template.md create mode 100644 personas/_shared/skills/performing-active-directory-bloodhound-analysis/references/api-reference.md create mode 100644 personas/_shared/skills/performing-active-directory-bloodhound-analysis/references/standards.md create mode 100644 personas/_shared/skills/performing-active-directory-bloodhound-analysis/references/workflows.md create mode 100644 personas/_shared/skills/performing-active-directory-bloodhound-analysis/scripts/agent.py create mode 100644 personas/_shared/skills/performing-active-directory-bloodhound-analysis/scripts/process.py create mode 100644 personas/_shared/skills/performing-active-directory-compromise-investigation/LICENSE create mode 100644 personas/_shared/skills/performing-active-directory-compromise-investigation/SKILL.md create mode 100644 personas/_shared/skills/performing-active-directory-compromise-investigation/assets/template.md create mode 100644 personas/_shared/skills/performing-active-directory-compromise-investigation/references/api-reference.md create mode 100644 personas/_shared/skills/performing-active-directory-compromise-investigation/references/standards.md create mode 100644 personas/_shared/skills/performing-active-directory-compromise-investigation/references/workflows.md create mode 100644 personas/_shared/skills/performing-active-directory-compromise-investigation/scripts/agent.py create mode 100644 personas/_shared/skills/performing-active-directory-compromise-investigation/scripts/process.py create mode 100644 personas/_shared/skills/performing-active-directory-forest-trust-attack/LICENSE create mode 100644 personas/_shared/skills/performing-active-directory-forest-trust-attack/SKILL.md create mode 100644 personas/_shared/skills/performing-active-directory-forest-trust-attack/references/api-reference.md create mode 100644 personas/_shared/skills/performing-active-directory-forest-trust-attack/scripts/agent.py create mode 100644 personas/_shared/skills/performing-active-directory-penetration-test/LICENSE create mode 100644 personas/_shared/skills/performing-active-directory-penetration-test/SKILL.md create mode 100644 personas/_shared/skills/performing-active-directory-penetration-test/assets/template.md create mode 100644 personas/_shared/skills/performing-active-directory-penetration-test/references/api-reference.md create mode 100644 personas/_shared/skills/performing-active-directory-penetration-test/references/standards.md create mode 100644 personas/_shared/skills/performing-active-directory-penetration-test/references/workflows.md create mode 100644 personas/_shared/skills/performing-active-directory-penetration-test/scripts/agent.py create mode 100644 personas/_shared/skills/performing-active-directory-penetration-test/scripts/process.py create mode 100644 personas/_shared/skills/performing-active-directory-vulnerability-assessment/LICENSE create mode 100644 personas/_shared/skills/performing-active-directory-vulnerability-assessment/SKILL.md create mode 100644 personas/_shared/skills/performing-active-directory-vulnerability-assessment/assets/template.md create mode 100644 personas/_shared/skills/performing-active-directory-vulnerability-assessment/references/api-reference.md create mode 100644 personas/_shared/skills/performing-active-directory-vulnerability-assessment/references/standards.md create mode 100644 personas/_shared/skills/performing-active-directory-vulnerability-assessment/references/workflows.md create mode 100644 personas/_shared/skills/performing-active-directory-vulnerability-assessment/scripts/agent.py create mode 100644 personas/_shared/skills/performing-active-directory-vulnerability-assessment/scripts/process.py create mode 100644 personas/_shared/skills/performing-adversary-in-the-middle-phishing-detection/LICENSE create mode 100644 personas/_shared/skills/performing-adversary-in-the-middle-phishing-detection/SKILL.md create mode 100644 personas/_shared/skills/performing-adversary-in-the-middle-phishing-detection/assets/template.md create mode 100644 personas/_shared/skills/performing-adversary-in-the-middle-phishing-detection/references/api-reference.md create mode 100644 personas/_shared/skills/performing-adversary-in-the-middle-phishing-detection/references/standards.md create mode 100644 personas/_shared/skills/performing-adversary-in-the-middle-phishing-detection/references/workflows.md create mode 100644 personas/_shared/skills/performing-adversary-in-the-middle-phishing-detection/scripts/agent.py create mode 100644 personas/_shared/skills/performing-adversary-in-the-middle-phishing-detection/scripts/process.py create mode 100644 personas/_shared/skills/performing-agentless-vulnerability-scanning/LICENSE create mode 100644 personas/_shared/skills/performing-agentless-vulnerability-scanning/SKILL.md create mode 100644 personas/_shared/skills/performing-agentless-vulnerability-scanning/assets/template.md create mode 100644 personas/_shared/skills/performing-agentless-vulnerability-scanning/references/api-reference.md create mode 100644 personas/_shared/skills/performing-agentless-vulnerability-scanning/references/standards.md create mode 100644 personas/_shared/skills/performing-agentless-vulnerability-scanning/references/workflows.md create mode 100644 personas/_shared/skills/performing-agentless-vulnerability-scanning/scripts/agent.py create mode 100644 personas/_shared/skills/performing-agentless-vulnerability-scanning/scripts/process.py create mode 100644 personas/_shared/skills/performing-ai-driven-osint-correlation/LICENSE create mode 100644 personas/_shared/skills/performing-ai-driven-osint-correlation/SKILL.md create mode 100644 personas/_shared/skills/performing-ai-driven-osint-correlation/references/api-reference.md create mode 100644 personas/_shared/skills/performing-ai-driven-osint-correlation/scripts/agent.py create mode 100644 personas/_shared/skills/performing-alert-triage-with-elastic-siem/LICENSE create mode 100644 personas/_shared/skills/performing-alert-triage-with-elastic-siem/SKILL.md create mode 100644 personas/_shared/skills/performing-alert-triage-with-elastic-siem/assets/template.md create mode 100644 personas/_shared/skills/performing-alert-triage-with-elastic-siem/references/api-reference.md create mode 100644 personas/_shared/skills/performing-alert-triage-with-elastic-siem/references/standards.md create mode 100644 personas/_shared/skills/performing-alert-triage-with-elastic-siem/references/workflows.md create mode 100644 personas/_shared/skills/performing-alert-triage-with-elastic-siem/scripts/agent.py create mode 100644 personas/_shared/skills/performing-alert-triage-with-elastic-siem/scripts/process.py create mode 100644 personas/_shared/skills/performing-android-app-static-analysis-with-mobsf/LICENSE create mode 100644 personas/_shared/skills/performing-android-app-static-analysis-with-mobsf/SKILL.md create mode 100644 personas/_shared/skills/performing-android-app-static-analysis-with-mobsf/assets/template.md create mode 100644 personas/_shared/skills/performing-android-app-static-analysis-with-mobsf/references/api-reference.md create mode 100644 personas/_shared/skills/performing-android-app-static-analysis-with-mobsf/references/standards.md create mode 100644 personas/_shared/skills/performing-android-app-static-analysis-with-mobsf/references/workflows.md create mode 100644 personas/_shared/skills/performing-android-app-static-analysis-with-mobsf/scripts/agent.py create mode 100644 personas/_shared/skills/performing-android-app-static-analysis-with-mobsf/scripts/process.py create mode 100644 personas/_shared/skills/performing-api-fuzzing-with-restler/LICENSE create mode 100644 personas/_shared/skills/performing-api-fuzzing-with-restler/SKILL.md create mode 100644 personas/_shared/skills/performing-api-fuzzing-with-restler/references/api-reference.md create mode 100644 personas/_shared/skills/performing-api-fuzzing-with-restler/scripts/agent.py create mode 100644 personas/_shared/skills/performing-api-inventory-and-discovery/LICENSE create mode 100644 personas/_shared/skills/performing-api-inventory-and-discovery/SKILL.md create mode 100644 personas/_shared/skills/performing-api-inventory-and-discovery/references/api-reference.md create mode 100644 personas/_shared/skills/performing-api-inventory-and-discovery/scripts/agent.py create mode 100644 personas/_shared/skills/performing-api-rate-limiting-bypass/LICENSE create mode 100644 personas/_shared/skills/performing-api-rate-limiting-bypass/SKILL.md create mode 100644 personas/_shared/skills/performing-api-rate-limiting-bypass/references/api-reference.md create mode 100644 personas/_shared/skills/performing-api-rate-limiting-bypass/scripts/agent.py create mode 100644 personas/_shared/skills/performing-api-security-testing-with-postman/LICENSE create mode 100644 personas/_shared/skills/performing-api-security-testing-with-postman/SKILL.md create mode 100644 personas/_shared/skills/performing-api-security-testing-with-postman/references/api-reference.md create mode 100644 personas/_shared/skills/performing-api-security-testing-with-postman/scripts/agent.py create mode 100644 personas/_shared/skills/performing-arp-spoofing-attack-simulation/LICENSE create mode 100644 personas/_shared/skills/performing-arp-spoofing-attack-simulation/SKILL.md create mode 100644 personas/_shared/skills/performing-arp-spoofing-attack-simulation/references/api-reference.md create mode 100644 personas/_shared/skills/performing-arp-spoofing-attack-simulation/scripts/agent.py create mode 100644 personas/_shared/skills/performing-asset-criticality-scoring-for-vulns/LICENSE create mode 100644 personas/_shared/skills/performing-asset-criticality-scoring-for-vulns/SKILL.md create mode 100644 personas/_shared/skills/performing-asset-criticality-scoring-for-vulns/assets/template.md create mode 100644 personas/_shared/skills/performing-asset-criticality-scoring-for-vulns/references/api-reference.md create mode 100644 personas/_shared/skills/performing-asset-criticality-scoring-for-vulns/references/standards.md create mode 100644 personas/_shared/skills/performing-asset-criticality-scoring-for-vulns/references/workflows.md create mode 100644 personas/_shared/skills/performing-asset-criticality-scoring-for-vulns/scripts/agent.py create mode 100644 personas/_shared/skills/performing-asset-criticality-scoring-for-vulns/scripts/process.py create mode 100644 personas/_shared/skills/performing-authenticated-scan-with-openvas/LICENSE create mode 100644 personas/_shared/skills/performing-authenticated-scan-with-openvas/SKILL.md create mode 100644 personas/_shared/skills/performing-authenticated-scan-with-openvas/assets/template.md create mode 100644 personas/_shared/skills/performing-authenticated-scan-with-openvas/references/api-reference.md create mode 100644 personas/_shared/skills/performing-authenticated-scan-with-openvas/references/standards.md create mode 100644 personas/_shared/skills/performing-authenticated-scan-with-openvas/references/workflows.md create mode 100644 personas/_shared/skills/performing-authenticated-scan-with-openvas/scripts/agent.py create mode 100644 personas/_shared/skills/performing-authenticated-scan-with-openvas/scripts/process.py create mode 100644 personas/_shared/skills/performing-authenticated-vulnerability-scan/LICENSE create mode 100644 personas/_shared/skills/performing-authenticated-vulnerability-scan/SKILL.md create mode 100644 personas/_shared/skills/performing-authenticated-vulnerability-scan/assets/template.md create mode 100644 personas/_shared/skills/performing-authenticated-vulnerability-scan/references/api-reference.md create mode 100644 personas/_shared/skills/performing-authenticated-vulnerability-scan/references/standards.md create mode 100644 personas/_shared/skills/performing-authenticated-vulnerability-scan/references/workflows.md create mode 100644 personas/_shared/skills/performing-authenticated-vulnerability-scan/scripts/agent.py create mode 100644 personas/_shared/skills/performing-authenticated-vulnerability-scan/scripts/process.py create mode 100644 personas/_shared/skills/performing-automated-malware-analysis-with-cape/LICENSE create mode 100644 personas/_shared/skills/performing-automated-malware-analysis-with-cape/SKILL.md create mode 100644 personas/_shared/skills/performing-automated-malware-analysis-with-cape/assets/template.md create mode 100644 personas/_shared/skills/performing-automated-malware-analysis-with-cape/references/api-reference.md create mode 100644 personas/_shared/skills/performing-automated-malware-analysis-with-cape/references/standards.md create mode 100644 personas/_shared/skills/performing-automated-malware-analysis-with-cape/references/workflows.md create mode 100644 personas/_shared/skills/performing-automated-malware-analysis-with-cape/scripts/agent.py create mode 100644 personas/_shared/skills/performing-aws-account-enumeration-with-scout-suite/LICENSE create mode 100644 personas/_shared/skills/performing-aws-account-enumeration-with-scout-suite/SKILL.md create mode 100644 personas/_shared/skills/performing-aws-account-enumeration-with-scout-suite/assets/template.md create mode 100644 personas/_shared/skills/performing-aws-account-enumeration-with-scout-suite/references/api-reference.md create mode 100644 personas/_shared/skills/performing-aws-account-enumeration-with-scout-suite/references/standards.md create mode 100644 personas/_shared/skills/performing-aws-account-enumeration-with-scout-suite/references/workflows.md create mode 100644 personas/_shared/skills/performing-aws-account-enumeration-with-scout-suite/scripts/agent.py create mode 100644 personas/_shared/skills/performing-aws-account-enumeration-with-scout-suite/scripts/process.py create mode 100644 personas/_shared/skills/performing-aws-privilege-escalation-assessment/LICENSE create mode 100644 personas/_shared/skills/performing-aws-privilege-escalation-assessment/SKILL.md create mode 100644 personas/_shared/skills/performing-aws-privilege-escalation-assessment/references/api-reference.md create mode 100644 personas/_shared/skills/performing-aws-privilege-escalation-assessment/scripts/agent.py create mode 100644 personas/_shared/skills/performing-bandwidth-throttling-attack-simulation/LICENSE create mode 100644 personas/_shared/skills/performing-bandwidth-throttling-attack-simulation/SKILL.md create mode 100644 personas/_shared/skills/performing-bandwidth-throttling-attack-simulation/references/api-reference.md create mode 100644 personas/_shared/skills/performing-bandwidth-throttling-attack-simulation/scripts/agent.py create mode 100644 personas/_shared/skills/performing-binary-exploitation-analysis/LICENSE create mode 100644 personas/_shared/skills/performing-binary-exploitation-analysis/SKILL.md create mode 100644 personas/_shared/skills/performing-binary-exploitation-analysis/references/api-reference.md create mode 100644 personas/_shared/skills/performing-binary-exploitation-analysis/scripts/agent.py create mode 100644 personas/_shared/skills/performing-blind-ssrf-exploitation/LICENSE create mode 100644 personas/_shared/skills/performing-blind-ssrf-exploitation/SKILL.md create mode 100644 personas/_shared/skills/performing-blind-ssrf-exploitation/references/api-reference.md create mode 100644 personas/_shared/skills/performing-blind-ssrf-exploitation/scripts/agent.py create mode 100644 personas/_shared/skills/performing-bluetooth-security-assessment/LICENSE create mode 100644 personas/_shared/skills/performing-bluetooth-security-assessment/SKILL.md create mode 100644 personas/_shared/skills/performing-bluetooth-security-assessment/references/api-reference.md create mode 100644 personas/_shared/skills/performing-bluetooth-security-assessment/scripts/agent.py create mode 100644 personas/_shared/skills/performing-brand-monitoring-for-impersonation/LICENSE create mode 100644 personas/_shared/skills/performing-brand-monitoring-for-impersonation/SKILL.md create mode 100644 personas/_shared/skills/performing-brand-monitoring-for-impersonation/references/api-reference.md create mode 100644 personas/_shared/skills/performing-brand-monitoring-for-impersonation/scripts/agent.py create mode 100644 personas/_shared/skills/performing-clickjacking-attack-test/LICENSE create mode 100644 personas/_shared/skills/performing-clickjacking-attack-test/SKILL.md create mode 100644 personas/_shared/skills/performing-clickjacking-attack-test/references/api-reference.md create mode 100644 personas/_shared/skills/performing-clickjacking-attack-test/scripts/agent.py create mode 100644 personas/_shared/skills/performing-cloud-asset-inventory-with-cartography/LICENSE create mode 100644 personas/_shared/skills/performing-cloud-asset-inventory-with-cartography/SKILL.md create mode 100644 personas/_shared/skills/performing-cloud-asset-inventory-with-cartography/assets/template.md create mode 100644 personas/_shared/skills/performing-cloud-asset-inventory-with-cartography/references/api-reference.md create mode 100644 personas/_shared/skills/performing-cloud-asset-inventory-with-cartography/references/standards.md create mode 100644 personas/_shared/skills/performing-cloud-asset-inventory-with-cartography/references/workflows.md create mode 100644 personas/_shared/skills/performing-cloud-asset-inventory-with-cartography/scripts/agent.py create mode 100644 personas/_shared/skills/performing-cloud-asset-inventory-with-cartography/scripts/process.py create mode 100644 personas/_shared/skills/performing-cloud-forensics-investigation/LICENSE create mode 100644 personas/_shared/skills/performing-cloud-forensics-investigation/SKILL.md create mode 100644 personas/_shared/skills/performing-cloud-forensics-investigation/references/api-reference.md create mode 100644 personas/_shared/skills/performing-cloud-forensics-investigation/scripts/agent.py create mode 100644 personas/_shared/skills/performing-cloud-forensics-with-aws-cloudtrail/LICENSE create mode 100644 personas/_shared/skills/performing-cloud-forensics-with-aws-cloudtrail/SKILL.md create mode 100644 personas/_shared/skills/performing-cloud-forensics-with-aws-cloudtrail/references/api-reference.md create mode 100644 personas/_shared/skills/performing-cloud-forensics-with-aws-cloudtrail/scripts/agent.py create mode 100644 personas/_shared/skills/performing-cloud-incident-containment-procedures/LICENSE create mode 100644 personas/_shared/skills/performing-cloud-incident-containment-procedures/SKILL.md create mode 100644 personas/_shared/skills/performing-cloud-incident-containment-procedures/assets/template.md create mode 100644 personas/_shared/skills/performing-cloud-incident-containment-procedures/references/api-reference.md create mode 100644 personas/_shared/skills/performing-cloud-incident-containment-procedures/references/standards.md create mode 100644 personas/_shared/skills/performing-cloud-incident-containment-procedures/references/workflows.md create mode 100644 personas/_shared/skills/performing-cloud-incident-containment-procedures/scripts/agent.py create mode 100644 personas/_shared/skills/performing-cloud-incident-containment-procedures/scripts/process.py create mode 100644 personas/_shared/skills/performing-cloud-log-forensics-with-athena/LICENSE create mode 100644 personas/_shared/skills/performing-cloud-log-forensics-with-athena/SKILL.md create mode 100644 personas/_shared/skills/performing-cloud-log-forensics-with-athena/references/athena-forensics-reference.md create mode 100644 personas/_shared/skills/performing-cloud-log-forensics-with-athena/scripts/agent.py create mode 100644 personas/_shared/skills/performing-cloud-native-forensics-with-falco/LICENSE create mode 100644 personas/_shared/skills/performing-cloud-native-forensics-with-falco/SKILL.md create mode 100644 personas/_shared/skills/performing-cloud-native-forensics-with-falco/references/api-reference.md create mode 100644 personas/_shared/skills/performing-cloud-native-forensics-with-falco/scripts/agent.py create mode 100644 personas/_shared/skills/performing-cloud-native-threat-hunting-with-aws-detective/SKILL.md create mode 100644 personas/_shared/skills/performing-cloud-native-threat-hunting-with-aws-detective/assets/template.md create mode 100644 personas/_shared/skills/performing-cloud-native-threat-hunting-with-aws-detective/references/standards.md create mode 100644 personas/_shared/skills/performing-cloud-native-threat-hunting-with-aws-detective/references/workflows.md create mode 100644 personas/_shared/skills/performing-cloud-native-threat-hunting-with-aws-detective/scripts/process.py create mode 100644 personas/_shared/skills/performing-cloud-penetration-testing-with-pacu/LICENSE create mode 100644 personas/_shared/skills/performing-cloud-penetration-testing-with-pacu/SKILL.md create mode 100644 personas/_shared/skills/performing-cloud-penetration-testing-with-pacu/references/api-reference.md create mode 100644 personas/_shared/skills/performing-cloud-penetration-testing-with-pacu/scripts/agent.py create mode 100644 personas/_shared/skills/performing-cloud-storage-forensic-acquisition/LICENSE create mode 100644 personas/_shared/skills/performing-cloud-storage-forensic-acquisition/SKILL.md create mode 100644 personas/_shared/skills/performing-cloud-storage-forensic-acquisition/assets/template.md create mode 100644 personas/_shared/skills/performing-cloud-storage-forensic-acquisition/references/api-reference.md create mode 100644 personas/_shared/skills/performing-cloud-storage-forensic-acquisition/references/standards.md create mode 100644 personas/_shared/skills/performing-cloud-storage-forensic-acquisition/references/workflows.md create mode 100644 personas/_shared/skills/performing-cloud-storage-forensic-acquisition/scripts/agent.py create mode 100644 personas/_shared/skills/performing-cloud-storage-forensic-acquisition/scripts/process.py create mode 100644 personas/_shared/skills/performing-container-escape-detection/LICENSE create mode 100644 personas/_shared/skills/performing-container-escape-detection/SKILL.md create mode 100644 personas/_shared/skills/performing-container-escape-detection/references/api-reference.md create mode 100644 personas/_shared/skills/performing-container-escape-detection/scripts/agent.py create mode 100644 personas/_shared/skills/performing-container-image-hardening/LICENSE create mode 100644 personas/_shared/skills/performing-container-image-hardening/SKILL.md create mode 100644 personas/_shared/skills/performing-container-image-hardening/assets/template.md create mode 100644 personas/_shared/skills/performing-container-image-hardening/references/api-reference.md create mode 100644 personas/_shared/skills/performing-container-image-hardening/references/standards.md create mode 100644 personas/_shared/skills/performing-container-image-hardening/references/workflows.md create mode 100644 personas/_shared/skills/performing-container-image-hardening/scripts/agent.py create mode 100644 personas/_shared/skills/performing-container-image-hardening/scripts/process.py create mode 100644 personas/_shared/skills/performing-container-security-scanning-with-trivy/LICENSE create mode 100644 personas/_shared/skills/performing-container-security-scanning-with-trivy/SKILL.md create mode 100644 personas/_shared/skills/performing-container-security-scanning-with-trivy/references/api-reference.md create mode 100644 personas/_shared/skills/performing-container-security-scanning-with-trivy/scripts/agent.py create mode 100644 personas/_shared/skills/performing-content-security-policy-bypass/LICENSE create mode 100644 personas/_shared/skills/performing-content-security-policy-bypass/SKILL.md create mode 100644 personas/_shared/skills/performing-content-security-policy-bypass/references/api-reference.md create mode 100644 personas/_shared/skills/performing-content-security-policy-bypass/scripts/agent.py create mode 100644 personas/_shared/skills/performing-credential-access-with-lazagne/LICENSE create mode 100644 personas/_shared/skills/performing-credential-access-with-lazagne/SKILL.md create mode 100644 personas/_shared/skills/performing-credential-access-with-lazagne/assets/template.md create mode 100644 personas/_shared/skills/performing-credential-access-with-lazagne/references/api-reference.md create mode 100644 personas/_shared/skills/performing-credential-access-with-lazagne/references/standards.md create mode 100644 personas/_shared/skills/performing-credential-access-with-lazagne/references/workflows.md create mode 100644 personas/_shared/skills/performing-credential-access-with-lazagne/scripts/agent.py create mode 100644 personas/_shared/skills/performing-credential-access-with-lazagne/scripts/process.py create mode 100644 personas/_shared/skills/performing-cryptographic-audit-of-application/LICENSE create mode 100644 personas/_shared/skills/performing-cryptographic-audit-of-application/SKILL.md create mode 100644 personas/_shared/skills/performing-cryptographic-audit-of-application/assets/template.md create mode 100644 personas/_shared/skills/performing-cryptographic-audit-of-application/references/api-reference.md create mode 100644 personas/_shared/skills/performing-cryptographic-audit-of-application/references/standards.md create mode 100644 personas/_shared/skills/performing-cryptographic-audit-of-application/references/workflows.md create mode 100644 personas/_shared/skills/performing-cryptographic-audit-of-application/scripts/agent.py create mode 100644 personas/_shared/skills/performing-cryptographic-audit-of-application/scripts/process.py create mode 100644 personas/_shared/skills/performing-csrf-attack-simulation/LICENSE create mode 100644 personas/_shared/skills/performing-csrf-attack-simulation/SKILL.md create mode 100644 personas/_shared/skills/performing-csrf-attack-simulation/references/api-reference.md create mode 100644 personas/_shared/skills/performing-csrf-attack-simulation/scripts/agent.py create mode 100644 personas/_shared/skills/performing-cve-prioritization-with-kev-catalog/LICENSE create mode 100644 personas/_shared/skills/performing-cve-prioritization-with-kev-catalog/SKILL.md create mode 100644 personas/_shared/skills/performing-cve-prioritization-with-kev-catalog/assets/template.md create mode 100644 personas/_shared/skills/performing-cve-prioritization-with-kev-catalog/references/api-reference.md create mode 100644 personas/_shared/skills/performing-cve-prioritization-with-kev-catalog/references/standards.md create mode 100644 personas/_shared/skills/performing-cve-prioritization-with-kev-catalog/references/workflows.md create mode 100644 personas/_shared/skills/performing-cve-prioritization-with-kev-catalog/scripts/agent.py create mode 100644 personas/_shared/skills/performing-cve-prioritization-with-kev-catalog/scripts/process.py create mode 100644 personas/_shared/skills/performing-dark-web-monitoring-for-threats/LICENSE create mode 100644 personas/_shared/skills/performing-dark-web-monitoring-for-threats/SKILL.md create mode 100644 personas/_shared/skills/performing-dark-web-monitoring-for-threats/assets/template.md create mode 100644 personas/_shared/skills/performing-dark-web-monitoring-for-threats/references/api-reference.md create mode 100644 personas/_shared/skills/performing-dark-web-monitoring-for-threats/references/standards.md create mode 100644 personas/_shared/skills/performing-dark-web-monitoring-for-threats/references/workflows.md create mode 100644 personas/_shared/skills/performing-dark-web-monitoring-for-threats/scripts/agent.py create mode 100644 personas/_shared/skills/performing-dark-web-monitoring-for-threats/scripts/process.py create mode 100644 personas/_shared/skills/performing-deception-technology-deployment/LICENSE create mode 100644 personas/_shared/skills/performing-deception-technology-deployment/SKILL.md create mode 100644 personas/_shared/skills/performing-deception-technology-deployment/references/api-reference.md create mode 100644 personas/_shared/skills/performing-deception-technology-deployment/scripts/agent.py create mode 100644 personas/_shared/skills/performing-directory-traversal-testing/LICENSE create mode 100644 personas/_shared/skills/performing-directory-traversal-testing/SKILL.md create mode 100644 personas/_shared/skills/performing-directory-traversal-testing/references/api-reference.md create mode 100644 personas/_shared/skills/performing-directory-traversal-testing/scripts/agent.py create mode 100644 personas/_shared/skills/performing-disk-forensics-investigation/LICENSE create mode 100644 personas/_shared/skills/performing-disk-forensics-investigation/SKILL.md create mode 100644 personas/_shared/skills/performing-disk-forensics-investigation/references/api-reference.md create mode 100644 personas/_shared/skills/performing-disk-forensics-investigation/scripts/agent.py create mode 100644 personas/_shared/skills/performing-dmarc-policy-enforcement-rollout/LICENSE create mode 100644 personas/_shared/skills/performing-dmarc-policy-enforcement-rollout/SKILL.md create mode 100644 personas/_shared/skills/performing-dmarc-policy-enforcement-rollout/assets/template.md create mode 100644 personas/_shared/skills/performing-dmarc-policy-enforcement-rollout/references/api-reference.md create mode 100644 personas/_shared/skills/performing-dmarc-policy-enforcement-rollout/references/standards.md create mode 100644 personas/_shared/skills/performing-dmarc-policy-enforcement-rollout/references/workflows.md create mode 100644 personas/_shared/skills/performing-dmarc-policy-enforcement-rollout/scripts/agent.py create mode 100644 personas/_shared/skills/performing-dmarc-policy-enforcement-rollout/scripts/process.py create mode 100644 personas/_shared/skills/performing-dns-enumeration-and-zone-transfer/LICENSE create mode 100644 personas/_shared/skills/performing-dns-enumeration-and-zone-transfer/SKILL.md create mode 100644 personas/_shared/skills/performing-dns-enumeration-and-zone-transfer/references/api-reference.md create mode 100644 personas/_shared/skills/performing-dns-enumeration-and-zone-transfer/scripts/agent.py create mode 100644 personas/_shared/skills/performing-dns-tunneling-detection/LICENSE create mode 100644 personas/_shared/skills/performing-dns-tunneling-detection/SKILL.md create mode 100644 personas/_shared/skills/performing-dns-tunneling-detection/references/api-reference.md create mode 100644 personas/_shared/skills/performing-dns-tunneling-detection/scripts/agent.py create mode 100644 personas/_shared/skills/performing-docker-bench-security-assessment/LICENSE create mode 100644 personas/_shared/skills/performing-docker-bench-security-assessment/SKILL.md create mode 100644 personas/_shared/skills/performing-docker-bench-security-assessment/assets/template.md create mode 100644 personas/_shared/skills/performing-docker-bench-security-assessment/references/api-reference.md create mode 100644 personas/_shared/skills/performing-docker-bench-security-assessment/references/standards.md create mode 100644 personas/_shared/skills/performing-docker-bench-security-assessment/references/workflows.md create mode 100644 personas/_shared/skills/performing-docker-bench-security-assessment/scripts/agent.py create mode 100644 personas/_shared/skills/performing-docker-bench-security-assessment/scripts/process.py create mode 100644 personas/_shared/skills/performing-dynamic-analysis-of-android-app/LICENSE create mode 100644 personas/_shared/skills/performing-dynamic-analysis-of-android-app/SKILL.md create mode 100644 personas/_shared/skills/performing-dynamic-analysis-of-android-app/assets/template.md create mode 100644 personas/_shared/skills/performing-dynamic-analysis-of-android-app/references/api-reference.md create mode 100644 personas/_shared/skills/performing-dynamic-analysis-of-android-app/references/standards.md create mode 100644 personas/_shared/skills/performing-dynamic-analysis-of-android-app/references/workflows.md create mode 100644 personas/_shared/skills/performing-dynamic-analysis-of-android-app/scripts/agent.py create mode 100644 personas/_shared/skills/performing-dynamic-analysis-of-android-app/scripts/process.py create mode 100644 personas/_shared/skills/performing-dynamic-analysis-with-any-run/LICENSE create mode 100644 personas/_shared/skills/performing-dynamic-analysis-with-any-run/SKILL.md create mode 100644 personas/_shared/skills/performing-dynamic-analysis-with-any-run/references/api-reference.md create mode 100644 personas/_shared/skills/performing-dynamic-analysis-with-any-run/scripts/agent.py create mode 100644 personas/_shared/skills/performing-endpoint-forensics-investigation/LICENSE create mode 100644 personas/_shared/skills/performing-endpoint-forensics-investigation/SKILL.md create mode 100644 personas/_shared/skills/performing-endpoint-forensics-investigation/assets/template.md create mode 100644 personas/_shared/skills/performing-endpoint-forensics-investigation/references/api-reference.md create mode 100644 personas/_shared/skills/performing-endpoint-forensics-investigation/references/standards.md create mode 100644 personas/_shared/skills/performing-endpoint-forensics-investigation/references/workflows.md create mode 100644 personas/_shared/skills/performing-endpoint-forensics-investigation/scripts/agent.py create mode 100644 personas/_shared/skills/performing-endpoint-forensics-investigation/scripts/process.py create mode 100644 personas/_shared/skills/performing-endpoint-vulnerability-remediation/LICENSE create mode 100644 personas/_shared/skills/performing-endpoint-vulnerability-remediation/SKILL.md create mode 100644 personas/_shared/skills/performing-endpoint-vulnerability-remediation/assets/template.md create mode 100644 personas/_shared/skills/performing-endpoint-vulnerability-remediation/references/api-reference.md create mode 100644 personas/_shared/skills/performing-endpoint-vulnerability-remediation/references/standards.md create mode 100644 personas/_shared/skills/performing-endpoint-vulnerability-remediation/references/workflows.md create mode 100644 personas/_shared/skills/performing-endpoint-vulnerability-remediation/scripts/agent.py create mode 100644 personas/_shared/skills/performing-endpoint-vulnerability-remediation/scripts/process.py create mode 100644 personas/_shared/skills/performing-entitlement-review-with-sailpoint-iiq/LICENSE create mode 100644 personas/_shared/skills/performing-entitlement-review-with-sailpoint-iiq/SKILL.md create mode 100644 personas/_shared/skills/performing-entitlement-review-with-sailpoint-iiq/references/api-reference.md create mode 100644 personas/_shared/skills/performing-entitlement-review-with-sailpoint-iiq/scripts/agent.py create mode 100644 personas/_shared/skills/performing-external-network-penetration-test/LICENSE create mode 100644 personas/_shared/skills/performing-external-network-penetration-test/SKILL.md create mode 100644 personas/_shared/skills/performing-external-network-penetration-test/assets/template.md create mode 100644 personas/_shared/skills/performing-external-network-penetration-test/references/api-reference.md create mode 100644 personas/_shared/skills/performing-external-network-penetration-test/references/standards.md create mode 100644 personas/_shared/skills/performing-external-network-penetration-test/references/workflows.md create mode 100644 personas/_shared/skills/performing-external-network-penetration-test/scripts/agent.py create mode 100644 personas/_shared/skills/performing-external-network-penetration-test/scripts/process.py create mode 100644 personas/_shared/skills/performing-false-positive-reduction-in-siem/LICENSE create mode 100644 personas/_shared/skills/performing-false-positive-reduction-in-siem/SKILL.md create mode 100644 personas/_shared/skills/performing-false-positive-reduction-in-siem/assets/template.md create mode 100644 personas/_shared/skills/performing-false-positive-reduction-in-siem/references/api-reference.md create mode 100644 personas/_shared/skills/performing-false-positive-reduction-in-siem/references/standards.md create mode 100644 personas/_shared/skills/performing-false-positive-reduction-in-siem/references/workflows.md create mode 100644 personas/_shared/skills/performing-false-positive-reduction-in-siem/scripts/agent.py create mode 100644 personas/_shared/skills/performing-false-positive-reduction-in-siem/scripts/process.py create mode 100644 personas/_shared/skills/performing-file-carving-with-foremost/LICENSE create mode 100644 personas/_shared/skills/performing-file-carving-with-foremost/SKILL.md create mode 100644 personas/_shared/skills/performing-file-carving-with-foremost/references/api-reference.md create mode 100644 personas/_shared/skills/performing-file-carving-with-foremost/scripts/agent.py create mode 100644 personas/_shared/skills/performing-firmware-extraction-with-binwalk/LICENSE create mode 100644 personas/_shared/skills/performing-firmware-extraction-with-binwalk/SKILL.md create mode 100644 personas/_shared/skills/performing-firmware-extraction-with-binwalk/references/api-reference.md create mode 100644 personas/_shared/skills/performing-firmware-extraction-with-binwalk/scripts/agent.py create mode 100644 personas/_shared/skills/performing-firmware-malware-analysis/LICENSE create mode 100644 personas/_shared/skills/performing-firmware-malware-analysis/SKILL.md create mode 100644 personas/_shared/skills/performing-firmware-malware-analysis/references/api-reference.md create mode 100644 personas/_shared/skills/performing-firmware-malware-analysis/scripts/agent.py create mode 100644 personas/_shared/skills/performing-fuzzing-with-aflplusplus/LICENSE create mode 100644 personas/_shared/skills/performing-fuzzing-with-aflplusplus/SKILL.md create mode 100644 personas/_shared/skills/performing-fuzzing-with-aflplusplus/references/api-reference.md create mode 100644 personas/_shared/skills/performing-fuzzing-with-aflplusplus/scripts/agent.py create mode 100644 personas/_shared/skills/performing-gcp-penetration-testing-with-gcpbucketbrute/LICENSE create mode 100644 personas/_shared/skills/performing-gcp-penetration-testing-with-gcpbucketbrute/SKILL.md create mode 100644 personas/_shared/skills/performing-gcp-penetration-testing-with-gcpbucketbrute/references/api-reference.md create mode 100644 personas/_shared/skills/performing-gcp-penetration-testing-with-gcpbucketbrute/scripts/agent.py create mode 100644 personas/_shared/skills/performing-gcp-security-assessment-with-forseti/LICENSE create mode 100644 personas/_shared/skills/performing-gcp-security-assessment-with-forseti/SKILL.md create mode 100644 personas/_shared/skills/performing-gcp-security-assessment-with-forseti/references/api-reference.md create mode 100644 personas/_shared/skills/performing-gcp-security-assessment-with-forseti/scripts/agent.py create mode 100644 personas/_shared/skills/performing-graphql-depth-limit-attack/LICENSE create mode 100644 personas/_shared/skills/performing-graphql-depth-limit-attack/SKILL.md create mode 100644 personas/_shared/skills/performing-graphql-depth-limit-attack/references/api-reference.md create mode 100644 personas/_shared/skills/performing-graphql-depth-limit-attack/scripts/agent.py create mode 100644 personas/_shared/skills/performing-graphql-introspection-attack/LICENSE create mode 100644 personas/_shared/skills/performing-graphql-introspection-attack/SKILL.md create mode 100644 personas/_shared/skills/performing-graphql-introspection-attack/references/api-reference.md create mode 100644 personas/_shared/skills/performing-graphql-introspection-attack/scripts/agent.py create mode 100644 personas/_shared/skills/performing-graphql-security-assessment/LICENSE create mode 100644 personas/_shared/skills/performing-graphql-security-assessment/SKILL.md create mode 100644 personas/_shared/skills/performing-graphql-security-assessment/references/api-reference.md create mode 100644 personas/_shared/skills/performing-graphql-security-assessment/scripts/agent.py create mode 100644 personas/_shared/skills/performing-hardware-security-module-integration/LICENSE create mode 100644 personas/_shared/skills/performing-hardware-security-module-integration/SKILL.md create mode 100644 personas/_shared/skills/performing-hardware-security-module-integration/references/api-reference.md create mode 100644 personas/_shared/skills/performing-hardware-security-module-integration/scripts/agent.py create mode 100644 personas/_shared/skills/performing-hash-cracking-with-hashcat/LICENSE create mode 100644 personas/_shared/skills/performing-hash-cracking-with-hashcat/SKILL.md create mode 100644 personas/_shared/skills/performing-hash-cracking-with-hashcat/assets/template.md create mode 100644 personas/_shared/skills/performing-hash-cracking-with-hashcat/references/api-reference.md create mode 100644 personas/_shared/skills/performing-hash-cracking-with-hashcat/references/standards.md create mode 100644 personas/_shared/skills/performing-hash-cracking-with-hashcat/references/workflows.md create mode 100644 personas/_shared/skills/performing-hash-cracking-with-hashcat/scripts/agent.py create mode 100644 personas/_shared/skills/performing-hash-cracking-with-hashcat/scripts/process.py create mode 100644 personas/_shared/skills/performing-http-parameter-pollution-attack/LICENSE create mode 100644 personas/_shared/skills/performing-http-parameter-pollution-attack/SKILL.md create mode 100644 personas/_shared/skills/performing-http-parameter-pollution-attack/references/api-reference.md create mode 100644 personas/_shared/skills/performing-http-parameter-pollution-attack/scripts/agent.py create mode 100644 personas/_shared/skills/performing-ics-asset-discovery-with-claroty/LICENSE create mode 100644 personas/_shared/skills/performing-ics-asset-discovery-with-claroty/SKILL.md create mode 100644 personas/_shared/skills/performing-ics-asset-discovery-with-claroty/references/api-reference.md create mode 100644 personas/_shared/skills/performing-ics-asset-discovery-with-claroty/scripts/agent.py create mode 100644 personas/_shared/skills/performing-indicator-lifecycle-management/LICENSE create mode 100644 personas/_shared/skills/performing-indicator-lifecycle-management/SKILL.md create mode 100644 personas/_shared/skills/performing-indicator-lifecycle-management/assets/template.md create mode 100644 personas/_shared/skills/performing-indicator-lifecycle-management/references/api-reference.md create mode 100644 personas/_shared/skills/performing-indicator-lifecycle-management/references/standards.md create mode 100644 personas/_shared/skills/performing-indicator-lifecycle-management/references/workflows.md create mode 100644 personas/_shared/skills/performing-indicator-lifecycle-management/scripts/agent.py create mode 100644 personas/_shared/skills/performing-indicator-lifecycle-management/scripts/process.py create mode 100644 personas/_shared/skills/performing-initial-access-with-evilginx3/LICENSE create mode 100644 personas/_shared/skills/performing-initial-access-with-evilginx3/SKILL.md create mode 100644 personas/_shared/skills/performing-initial-access-with-evilginx3/assets/template.md create mode 100644 personas/_shared/skills/performing-initial-access-with-evilginx3/references/api-reference.md create mode 100644 personas/_shared/skills/performing-initial-access-with-evilginx3/references/standards.md create mode 100644 personas/_shared/skills/performing-initial-access-with-evilginx3/references/workflows.md create mode 100644 personas/_shared/skills/performing-initial-access-with-evilginx3/scripts/agent.py create mode 100644 personas/_shared/skills/performing-initial-access-with-evilginx3/scripts/process.py create mode 100644 personas/_shared/skills/performing-insider-threat-investigation/LICENSE create mode 100644 personas/_shared/skills/performing-insider-threat-investigation/SKILL.md create mode 100644 personas/_shared/skills/performing-insider-threat-investigation/references/api-reference.md create mode 100644 personas/_shared/skills/performing-insider-threat-investigation/scripts/agent.py create mode 100644 personas/_shared/skills/performing-ioc-enrichment-automation/LICENSE create mode 100644 personas/_shared/skills/performing-ioc-enrichment-automation/SKILL.md create mode 100644 personas/_shared/skills/performing-ioc-enrichment-automation/references/api-reference.md create mode 100644 personas/_shared/skills/performing-ioc-enrichment-automation/scripts/agent.py create mode 100644 personas/_shared/skills/performing-ios-app-security-assessment/LICENSE create mode 100644 personas/_shared/skills/performing-ios-app-security-assessment/SKILL.md create mode 100644 personas/_shared/skills/performing-ios-app-security-assessment/references/api-reference.md create mode 100644 personas/_shared/skills/performing-ios-app-security-assessment/scripts/agent.py create mode 100644 personas/_shared/skills/performing-iot-security-assessment/LICENSE create mode 100644 personas/_shared/skills/performing-iot-security-assessment/SKILL.md create mode 100644 personas/_shared/skills/performing-iot-security-assessment/references/api-reference.md create mode 100644 personas/_shared/skills/performing-iot-security-assessment/scripts/agent.py create mode 100644 personas/_shared/skills/performing-ip-reputation-analysis-with-shodan/LICENSE create mode 100644 personas/_shared/skills/performing-ip-reputation-analysis-with-shodan/SKILL.md create mode 100644 personas/_shared/skills/performing-ip-reputation-analysis-with-shodan/references/api-reference.md create mode 100644 personas/_shared/skills/performing-ip-reputation-analysis-with-shodan/scripts/agent.py create mode 100644 personas/_shared/skills/performing-jwt-none-algorithm-attack/LICENSE create mode 100644 personas/_shared/skills/performing-jwt-none-algorithm-attack/SKILL.md create mode 100644 personas/_shared/skills/performing-jwt-none-algorithm-attack/references/api-reference.md create mode 100644 personas/_shared/skills/performing-jwt-none-algorithm-attack/scripts/agent.py create mode 100644 personas/_shared/skills/performing-kerberoasting-attack/LICENSE create mode 100644 personas/_shared/skills/performing-kerberoasting-attack/SKILL.md create mode 100644 personas/_shared/skills/performing-kerberoasting-attack/assets/template.md create mode 100644 personas/_shared/skills/performing-kerberoasting-attack/references/api-reference.md create mode 100644 personas/_shared/skills/performing-kerberoasting-attack/references/standards.md create mode 100644 personas/_shared/skills/performing-kerberoasting-attack/references/workflows.md create mode 100644 personas/_shared/skills/performing-kerberoasting-attack/scripts/agent.py create mode 100644 personas/_shared/skills/performing-kerberoasting-attack/scripts/process.py create mode 100644 personas/_shared/skills/performing-kubernetes-cis-benchmark-with-kube-bench/LICENSE create mode 100644 personas/_shared/skills/performing-kubernetes-cis-benchmark-with-kube-bench/SKILL.md create mode 100644 personas/_shared/skills/performing-kubernetes-cis-benchmark-with-kube-bench/assets/template.md create mode 100644 personas/_shared/skills/performing-kubernetes-cis-benchmark-with-kube-bench/references/api-reference.md create mode 100644 personas/_shared/skills/performing-kubernetes-cis-benchmark-with-kube-bench/references/standards.md create mode 100644 personas/_shared/skills/performing-kubernetes-cis-benchmark-with-kube-bench/references/workflows.md create mode 100644 personas/_shared/skills/performing-kubernetes-cis-benchmark-with-kube-bench/scripts/agent.py create mode 100644 personas/_shared/skills/performing-kubernetes-cis-benchmark-with-kube-bench/scripts/process.py create mode 100644 personas/_shared/skills/performing-kubernetes-etcd-security-assessment/LICENSE create mode 100644 personas/_shared/skills/performing-kubernetes-etcd-security-assessment/SKILL.md create mode 100644 personas/_shared/skills/performing-kubernetes-etcd-security-assessment/assets/template.md create mode 100644 personas/_shared/skills/performing-kubernetes-etcd-security-assessment/references/api-reference.md create mode 100644 personas/_shared/skills/performing-kubernetes-etcd-security-assessment/references/standards.md create mode 100644 personas/_shared/skills/performing-kubernetes-etcd-security-assessment/references/workflows.md create mode 100644 personas/_shared/skills/performing-kubernetes-etcd-security-assessment/scripts/agent.py create mode 100644 personas/_shared/skills/performing-kubernetes-etcd-security-assessment/scripts/process.py create mode 100644 personas/_shared/skills/performing-kubernetes-penetration-testing/LICENSE create mode 100644 personas/_shared/skills/performing-kubernetes-penetration-testing/SKILL.md create mode 100644 personas/_shared/skills/performing-kubernetes-penetration-testing/assets/template.md create mode 100644 personas/_shared/skills/performing-kubernetes-penetration-testing/references/api-reference.md create mode 100644 personas/_shared/skills/performing-kubernetes-penetration-testing/references/standards.md create mode 100644 personas/_shared/skills/performing-kubernetes-penetration-testing/references/workflows.md create mode 100644 personas/_shared/skills/performing-kubernetes-penetration-testing/scripts/agent.py create mode 100644 personas/_shared/skills/performing-kubernetes-penetration-testing/scripts/process.py create mode 100644 personas/_shared/skills/performing-lateral-movement-detection/LICENSE create mode 100644 personas/_shared/skills/performing-lateral-movement-detection/SKILL.md create mode 100644 personas/_shared/skills/performing-lateral-movement-detection/references/api-reference.md create mode 100644 personas/_shared/skills/performing-lateral-movement-detection/scripts/agent.py create mode 100644 personas/_shared/skills/performing-lateral-movement-with-wmiexec/LICENSE create mode 100644 personas/_shared/skills/performing-lateral-movement-with-wmiexec/SKILL.md create mode 100644 personas/_shared/skills/performing-lateral-movement-with-wmiexec/assets/template.md create mode 100644 personas/_shared/skills/performing-lateral-movement-with-wmiexec/references/api-reference.md create mode 100644 personas/_shared/skills/performing-lateral-movement-with-wmiexec/references/standards.md create mode 100644 personas/_shared/skills/performing-lateral-movement-with-wmiexec/references/workflows.md create mode 100644 personas/_shared/skills/performing-lateral-movement-with-wmiexec/scripts/agent.py create mode 100644 personas/_shared/skills/performing-lateral-movement-with-wmiexec/scripts/process.py create mode 100644 personas/_shared/skills/performing-linux-log-forensics-investigation/LICENSE create mode 100644 personas/_shared/skills/performing-linux-log-forensics-investigation/SKILL.md create mode 100644 personas/_shared/skills/performing-linux-log-forensics-investigation/assets/template.md create mode 100644 personas/_shared/skills/performing-linux-log-forensics-investigation/references/api-reference.md create mode 100644 personas/_shared/skills/performing-linux-log-forensics-investigation/references/standards.md create mode 100644 personas/_shared/skills/performing-linux-log-forensics-investigation/references/workflows.md create mode 100644 personas/_shared/skills/performing-linux-log-forensics-investigation/scripts/agent.py create mode 100644 personas/_shared/skills/performing-linux-log-forensics-investigation/scripts/process.py create mode 100644 personas/_shared/skills/performing-log-analysis-for-forensic-investigation/LICENSE create mode 100644 personas/_shared/skills/performing-log-analysis-for-forensic-investigation/SKILL.md create mode 100644 personas/_shared/skills/performing-log-analysis-for-forensic-investigation/references/api-reference.md create mode 100644 personas/_shared/skills/performing-log-analysis-for-forensic-investigation/scripts/agent.py create mode 100644 personas/_shared/skills/performing-log-source-onboarding-in-siem/LICENSE create mode 100644 personas/_shared/skills/performing-log-source-onboarding-in-siem/SKILL.md create mode 100644 personas/_shared/skills/performing-log-source-onboarding-in-siem/assets/template.md create mode 100644 personas/_shared/skills/performing-log-source-onboarding-in-siem/references/api-reference.md create mode 100644 personas/_shared/skills/performing-log-source-onboarding-in-siem/references/standards.md create mode 100644 personas/_shared/skills/performing-log-source-onboarding-in-siem/references/workflows.md create mode 100644 personas/_shared/skills/performing-log-source-onboarding-in-siem/scripts/agent.py create mode 100644 personas/_shared/skills/performing-log-source-onboarding-in-siem/scripts/process.py create mode 100644 personas/_shared/skills/performing-malware-hash-enrichment-with-virustotal/LICENSE create mode 100644 personas/_shared/skills/performing-malware-hash-enrichment-with-virustotal/SKILL.md create mode 100644 personas/_shared/skills/performing-malware-hash-enrichment-with-virustotal/references/api-reference.md create mode 100644 personas/_shared/skills/performing-malware-hash-enrichment-with-virustotal/scripts/agent.py create mode 100644 personas/_shared/skills/performing-malware-ioc-extraction/LICENSE create mode 100644 personas/_shared/skills/performing-malware-ioc-extraction/SKILL.md create mode 100644 personas/_shared/skills/performing-malware-ioc-extraction/assets/template.md create mode 100644 personas/_shared/skills/performing-malware-ioc-extraction/references/api-reference.md create mode 100644 personas/_shared/skills/performing-malware-ioc-extraction/references/standards.md create mode 100644 personas/_shared/skills/performing-malware-ioc-extraction/references/workflows.md create mode 100644 personas/_shared/skills/performing-malware-ioc-extraction/scripts/agent.py create mode 100644 personas/_shared/skills/performing-malware-ioc-extraction/scripts/process.py create mode 100644 personas/_shared/skills/performing-malware-persistence-investigation/LICENSE create mode 100644 personas/_shared/skills/performing-malware-persistence-investigation/SKILL.md create mode 100644 personas/_shared/skills/performing-malware-persistence-investigation/references/api-reference.md create mode 100644 personas/_shared/skills/performing-malware-persistence-investigation/scripts/agent.py create mode 100644 personas/_shared/skills/performing-malware-triage-with-yara/LICENSE create mode 100644 personas/_shared/skills/performing-malware-triage-with-yara/SKILL.md create mode 100644 personas/_shared/skills/performing-malware-triage-with-yara/references/api-reference.md create mode 100644 personas/_shared/skills/performing-malware-triage-with-yara/scripts/agent.py create mode 100644 personas/_shared/skills/performing-memory-forensics-with-volatility3-plugins/LICENSE create mode 100644 personas/_shared/skills/performing-memory-forensics-with-volatility3-plugins/SKILL.md create mode 100644 personas/_shared/skills/performing-memory-forensics-with-volatility3-plugins/assets/template.md create mode 100644 personas/_shared/skills/performing-memory-forensics-with-volatility3-plugins/references/api-reference.md create mode 100644 personas/_shared/skills/performing-memory-forensics-with-volatility3-plugins/references/standards.md create mode 100644 personas/_shared/skills/performing-memory-forensics-with-volatility3-plugins/references/workflows.md create mode 100644 personas/_shared/skills/performing-memory-forensics-with-volatility3-plugins/scripts/agent.py create mode 100644 personas/_shared/skills/performing-memory-forensics-with-volatility3-plugins/scripts/process.py create mode 100644 personas/_shared/skills/performing-memory-forensics-with-volatility3/LICENSE create mode 100644 personas/_shared/skills/performing-memory-forensics-with-volatility3/SKILL.es.md create mode 100644 personas/_shared/skills/performing-memory-forensics-with-volatility3/SKILL.md create mode 100644 personas/_shared/skills/performing-memory-forensics-with-volatility3/references/api-reference.md create mode 100644 personas/_shared/skills/performing-memory-forensics-with-volatility3/scripts/agent.py create mode 100644 personas/_shared/skills/performing-mobile-app-certificate-pinning-bypass/LICENSE create mode 100644 personas/_shared/skills/performing-mobile-app-certificate-pinning-bypass/SKILL.md create mode 100644 personas/_shared/skills/performing-mobile-app-certificate-pinning-bypass/assets/template.md create mode 100644 personas/_shared/skills/performing-mobile-app-certificate-pinning-bypass/references/api-reference.md create mode 100644 personas/_shared/skills/performing-mobile-app-certificate-pinning-bypass/references/standards.md create mode 100644 personas/_shared/skills/performing-mobile-app-certificate-pinning-bypass/references/workflows.md create mode 100644 personas/_shared/skills/performing-mobile-app-certificate-pinning-bypass/scripts/agent.py create mode 100644 personas/_shared/skills/performing-mobile-app-certificate-pinning-bypass/scripts/process.py create mode 100644 personas/_shared/skills/performing-mobile-device-forensics-with-cellebrite/LICENSE create mode 100644 personas/_shared/skills/performing-mobile-device-forensics-with-cellebrite/SKILL.md create mode 100644 personas/_shared/skills/performing-mobile-device-forensics-with-cellebrite/references/api-reference.md create mode 100644 personas/_shared/skills/performing-mobile-device-forensics-with-cellebrite/scripts/agent.py create mode 100644 personas/_shared/skills/performing-network-forensics-with-wireshark/LICENSE create mode 100644 personas/_shared/skills/performing-network-forensics-with-wireshark/SKILL.md create mode 100644 personas/_shared/skills/performing-network-forensics-with-wireshark/references/api-reference.md create mode 100644 personas/_shared/skills/performing-network-forensics-with-wireshark/scripts/agent.py create mode 100644 personas/_shared/skills/performing-network-packet-capture-analysis/LICENSE create mode 100644 personas/_shared/skills/performing-network-packet-capture-analysis/SKILL.md create mode 100644 personas/_shared/skills/performing-network-packet-capture-analysis/assets/template.md create mode 100644 personas/_shared/skills/performing-network-packet-capture-analysis/references/api-reference.md create mode 100644 personas/_shared/skills/performing-network-packet-capture-analysis/references/standards.md create mode 100644 personas/_shared/skills/performing-network-packet-capture-analysis/references/workflows.md create mode 100644 personas/_shared/skills/performing-network-packet-capture-analysis/scripts/agent.py create mode 100644 personas/_shared/skills/performing-network-packet-capture-analysis/scripts/process.py create mode 100644 personas/_shared/skills/performing-network-traffic-analysis-with-tshark/LICENSE create mode 100644 personas/_shared/skills/performing-network-traffic-analysis-with-tshark/SKILL.md create mode 100644 personas/_shared/skills/performing-network-traffic-analysis-with-tshark/references/api-reference.md create mode 100644 personas/_shared/skills/performing-network-traffic-analysis-with-tshark/scripts/agent.py create mode 100644 personas/_shared/skills/performing-network-traffic-analysis-with-zeek/LICENSE create mode 100644 personas/_shared/skills/performing-network-traffic-analysis-with-zeek/SKILL.md create mode 100644 personas/_shared/skills/performing-network-traffic-analysis-with-zeek/references/api-reference.md create mode 100644 personas/_shared/skills/performing-network-traffic-analysis-with-zeek/scripts/agent.py create mode 100644 personas/_shared/skills/performing-nist-csf-maturity-assessment/LICENSE create mode 100644 personas/_shared/skills/performing-nist-csf-maturity-assessment/SKILL.md create mode 100644 personas/_shared/skills/performing-nist-csf-maturity-assessment/assets/template.md create mode 100644 personas/_shared/skills/performing-nist-csf-maturity-assessment/references/api-reference.md create mode 100644 personas/_shared/skills/performing-nist-csf-maturity-assessment/references/standards.md create mode 100644 personas/_shared/skills/performing-nist-csf-maturity-assessment/references/workflows.md create mode 100644 personas/_shared/skills/performing-nist-csf-maturity-assessment/scripts/agent.py create mode 100644 personas/_shared/skills/performing-nist-csf-maturity-assessment/scripts/process.py create mode 100644 personas/_shared/skills/performing-oauth-scope-minimization-review/LICENSE create mode 100644 personas/_shared/skills/performing-oauth-scope-minimization-review/SKILL.md create mode 100644 personas/_shared/skills/performing-oauth-scope-minimization-review/references/api-reference.md create mode 100644 personas/_shared/skills/performing-oauth-scope-minimization-review/scripts/agent.py create mode 100644 personas/_shared/skills/performing-oil-gas-cybersecurity-assessment/LICENSE create mode 100644 personas/_shared/skills/performing-oil-gas-cybersecurity-assessment/SKILL.md create mode 100644 personas/_shared/skills/performing-oil-gas-cybersecurity-assessment/references/api-reference.md create mode 100644 personas/_shared/skills/performing-oil-gas-cybersecurity-assessment/scripts/agent.py create mode 100644 personas/_shared/skills/performing-open-source-intelligence-gathering/LICENSE create mode 100644 personas/_shared/skills/performing-open-source-intelligence-gathering/SKILL.md create mode 100644 personas/_shared/skills/performing-open-source-intelligence-gathering/assets/template.md create mode 100644 personas/_shared/skills/performing-open-source-intelligence-gathering/references/api-reference.md create mode 100644 personas/_shared/skills/performing-open-source-intelligence-gathering/references/standards.md create mode 100644 personas/_shared/skills/performing-open-source-intelligence-gathering/references/workflows.md create mode 100644 personas/_shared/skills/performing-open-source-intelligence-gathering/scripts/agent.py create mode 100644 personas/_shared/skills/performing-open-source-intelligence-gathering/scripts/process.py create mode 100644 personas/_shared/skills/performing-osint-with-spiderfoot/LICENSE create mode 100644 personas/_shared/skills/performing-osint-with-spiderfoot/SKILL.es.md create mode 100644 personas/_shared/skills/performing-osint-with-spiderfoot/SKILL.md create mode 100644 personas/_shared/skills/performing-osint-with-spiderfoot/references/api-reference.md create mode 100644 personas/_shared/skills/performing-osint-with-spiderfoot/scripts/agent.py create mode 100644 personas/_shared/skills/performing-ot-network-security-assessment/LICENSE create mode 100644 personas/_shared/skills/performing-ot-network-security-assessment/SKILL.md create mode 100644 personas/_shared/skills/performing-ot-network-security-assessment/assets/template.md create mode 100644 personas/_shared/skills/performing-ot-network-security-assessment/references/api-reference.md create mode 100644 personas/_shared/skills/performing-ot-network-security-assessment/references/standards.md create mode 100644 personas/_shared/skills/performing-ot-network-security-assessment/references/workflows.md create mode 100644 personas/_shared/skills/performing-ot-network-security-assessment/scripts/agent.py create mode 100644 personas/_shared/skills/performing-ot-network-security-assessment/scripts/process.py create mode 100644 personas/_shared/skills/performing-ot-vulnerability-assessment-with-claroty/LICENSE create mode 100644 personas/_shared/skills/performing-ot-vulnerability-assessment-with-claroty/SKILL.md create mode 100644 personas/_shared/skills/performing-ot-vulnerability-assessment-with-claroty/references/api-reference.md create mode 100644 personas/_shared/skills/performing-ot-vulnerability-assessment-with-claroty/scripts/agent.py create mode 100644 personas/_shared/skills/performing-ot-vulnerability-scanning-safely/LICENSE create mode 100644 personas/_shared/skills/performing-ot-vulnerability-scanning-safely/SKILL.md create mode 100644 personas/_shared/skills/performing-ot-vulnerability-scanning-safely/references/api-reference.md create mode 100644 personas/_shared/skills/performing-ot-vulnerability-scanning-safely/scripts/agent.py create mode 100644 personas/_shared/skills/performing-packet-injection-attack/LICENSE create mode 100644 personas/_shared/skills/performing-packet-injection-attack/SKILL.md create mode 100644 personas/_shared/skills/performing-packet-injection-attack/references/api-reference.md create mode 100644 personas/_shared/skills/performing-packet-injection-attack/scripts/agent.py create mode 100644 personas/_shared/skills/performing-paste-site-monitoring-for-credentials/LICENSE create mode 100644 personas/_shared/skills/performing-paste-site-monitoring-for-credentials/SKILL.md create mode 100644 personas/_shared/skills/performing-paste-site-monitoring-for-credentials/references/api-reference.md create mode 100644 personas/_shared/skills/performing-paste-site-monitoring-for-credentials/scripts/agent.py create mode 100644 personas/_shared/skills/performing-phishing-simulation-with-gophish/LICENSE create mode 100644 personas/_shared/skills/performing-phishing-simulation-with-gophish/SKILL.md create mode 100644 personas/_shared/skills/performing-phishing-simulation-with-gophish/assets/template.md create mode 100644 personas/_shared/skills/performing-phishing-simulation-with-gophish/references/api-reference.md create mode 100644 personas/_shared/skills/performing-phishing-simulation-with-gophish/references/standards.md create mode 100644 personas/_shared/skills/performing-phishing-simulation-with-gophish/references/workflows.md create mode 100644 personas/_shared/skills/performing-phishing-simulation-with-gophish/scripts/agent.py create mode 100644 personas/_shared/skills/performing-phishing-simulation-with-gophish/scripts/process.py create mode 100644 personas/_shared/skills/performing-physical-intrusion-assessment/LICENSE create mode 100644 personas/_shared/skills/performing-physical-intrusion-assessment/SKILL.md create mode 100644 personas/_shared/skills/performing-physical-intrusion-assessment/assets/template.md create mode 100644 personas/_shared/skills/performing-physical-intrusion-assessment/references/api-reference.md create mode 100644 personas/_shared/skills/performing-physical-intrusion-assessment/references/standards.md create mode 100644 personas/_shared/skills/performing-physical-intrusion-assessment/references/workflows.md create mode 100644 personas/_shared/skills/performing-physical-intrusion-assessment/scripts/agent.py create mode 100644 personas/_shared/skills/performing-physical-intrusion-assessment/scripts/process.py create mode 100644 personas/_shared/skills/performing-plc-firmware-security-analysis/LICENSE create mode 100644 personas/_shared/skills/performing-plc-firmware-security-analysis/SKILL.md create mode 100644 personas/_shared/skills/performing-plc-firmware-security-analysis/references/api-reference.md create mode 100644 personas/_shared/skills/performing-plc-firmware-security-analysis/scripts/agent.py create mode 100644 personas/_shared/skills/performing-post-quantum-cryptography-migration/LICENSE create mode 100644 personas/_shared/skills/performing-post-quantum-cryptography-migration/SKILL.md create mode 100644 personas/_shared/skills/performing-post-quantum-cryptography-migration/references/api-reference.md create mode 100644 personas/_shared/skills/performing-post-quantum-cryptography-migration/scripts/agent.py create mode 100644 personas/_shared/skills/performing-power-grid-cybersecurity-assessment/LICENSE create mode 100644 personas/_shared/skills/performing-power-grid-cybersecurity-assessment/SKILL.md create mode 100644 personas/_shared/skills/performing-power-grid-cybersecurity-assessment/references/api-reference.md create mode 100644 personas/_shared/skills/performing-power-grid-cybersecurity-assessment/scripts/agent.py create mode 100644 personas/_shared/skills/performing-privacy-impact-assessment/LICENSE create mode 100644 personas/_shared/skills/performing-privacy-impact-assessment/SKILL.md create mode 100644 personas/_shared/skills/performing-privacy-impact-assessment/references/api-reference.md create mode 100644 personas/_shared/skills/performing-privacy-impact-assessment/scripts/agent.py create mode 100644 personas/_shared/skills/performing-privilege-escalation-assessment/LICENSE create mode 100644 personas/_shared/skills/performing-privilege-escalation-assessment/SKILL.md create mode 100644 personas/_shared/skills/performing-privilege-escalation-assessment/references/api-reference.md create mode 100644 personas/_shared/skills/performing-privilege-escalation-assessment/scripts/agent.py create mode 100644 personas/_shared/skills/performing-privilege-escalation-on-linux/LICENSE create mode 100644 personas/_shared/skills/performing-privilege-escalation-on-linux/SKILL.md create mode 100644 personas/_shared/skills/performing-privilege-escalation-on-linux/references/api-reference.md create mode 100644 personas/_shared/skills/performing-privilege-escalation-on-linux/references/standards.md create mode 100644 personas/_shared/skills/performing-privilege-escalation-on-linux/references/workflows.md create mode 100644 personas/_shared/skills/performing-privilege-escalation-on-linux/scripts/agent.py create mode 100644 personas/_shared/skills/performing-privileged-account-access-review/LICENSE create mode 100644 personas/_shared/skills/performing-privileged-account-access-review/SKILL.md create mode 100644 personas/_shared/skills/performing-privileged-account-access-review/assets/template.md create mode 100644 personas/_shared/skills/performing-privileged-account-access-review/references/api-reference.md create mode 100644 personas/_shared/skills/performing-privileged-account-access-review/references/standards.md create mode 100644 personas/_shared/skills/performing-privileged-account-access-review/references/workflows.md create mode 100644 personas/_shared/skills/performing-privileged-account-access-review/scripts/agent.py create mode 100644 personas/_shared/skills/performing-privileged-account-access-review/scripts/process.py create mode 100644 personas/_shared/skills/performing-privileged-account-discovery/LICENSE create mode 100644 personas/_shared/skills/performing-privileged-account-discovery/SKILL.md create mode 100644 personas/_shared/skills/performing-privileged-account-discovery/references/api-reference.md create mode 100644 personas/_shared/skills/performing-privileged-account-discovery/scripts/agent.py create mode 100644 personas/_shared/skills/performing-purple-team-atomic-testing/LICENSE create mode 100644 personas/_shared/skills/performing-purple-team-atomic-testing/SKILL.md create mode 100644 personas/_shared/skills/performing-purple-team-atomic-testing/references/api-reference.md create mode 100644 personas/_shared/skills/performing-purple-team-atomic-testing/scripts/agent.py create mode 100644 personas/_shared/skills/performing-purple-team-exercise/LICENSE create mode 100644 personas/_shared/skills/performing-purple-team-exercise/SKILL.md create mode 100644 personas/_shared/skills/performing-purple-team-exercise/references/api-reference.md create mode 100644 personas/_shared/skills/performing-purple-team-exercise/scripts/agent.py create mode 100644 personas/_shared/skills/performing-ransomware-response/LICENSE create mode 100644 personas/_shared/skills/performing-ransomware-response/SKILL.md create mode 100644 personas/_shared/skills/performing-ransomware-response/references/api-reference.md create mode 100644 personas/_shared/skills/performing-ransomware-response/scripts/agent.py create mode 100644 personas/_shared/skills/performing-ransomware-tabletop-exercise/LICENSE create mode 100644 personas/_shared/skills/performing-ransomware-tabletop-exercise/SKILL.md create mode 100644 personas/_shared/skills/performing-ransomware-tabletop-exercise/assets/template.md create mode 100644 personas/_shared/skills/performing-ransomware-tabletop-exercise/references/api-reference.md create mode 100644 personas/_shared/skills/performing-ransomware-tabletop-exercise/references/standards.md create mode 100644 personas/_shared/skills/performing-ransomware-tabletop-exercise/references/workflows.md create mode 100644 personas/_shared/skills/performing-ransomware-tabletop-exercise/scripts/agent.py create mode 100644 personas/_shared/skills/performing-ransomware-tabletop-exercise/scripts/process.py create mode 100644 personas/_shared/skills/performing-red-team-phishing-with-gophish/LICENSE create mode 100644 personas/_shared/skills/performing-red-team-phishing-with-gophish/SKILL.md create mode 100644 personas/_shared/skills/performing-red-team-phishing-with-gophish/references/api-reference.md create mode 100644 personas/_shared/skills/performing-red-team-phishing-with-gophish/scripts/agent.py create mode 100644 personas/_shared/skills/performing-red-team-with-covenant/LICENSE create mode 100644 personas/_shared/skills/performing-red-team-with-covenant/SKILL.md create mode 100644 personas/_shared/skills/performing-red-team-with-covenant/references/api-reference.md create mode 100644 personas/_shared/skills/performing-red-team-with-covenant/scripts/agent.py create mode 100644 personas/_shared/skills/performing-s7comm-protocol-security-analysis/LICENSE create mode 100644 personas/_shared/skills/performing-s7comm-protocol-security-analysis/SKILL.md create mode 100644 personas/_shared/skills/performing-s7comm-protocol-security-analysis/references/api-reference.md create mode 100644 personas/_shared/skills/performing-s7comm-protocol-security-analysis/scripts/agent.py create mode 100644 personas/_shared/skills/performing-sca-dependency-scanning-with-snyk/LICENSE create mode 100644 personas/_shared/skills/performing-sca-dependency-scanning-with-snyk/SKILL.md create mode 100644 personas/_shared/skills/performing-sca-dependency-scanning-with-snyk/assets/template.md create mode 100644 personas/_shared/skills/performing-sca-dependency-scanning-with-snyk/references/api-reference.md create mode 100644 personas/_shared/skills/performing-sca-dependency-scanning-with-snyk/references/standards.md create mode 100644 personas/_shared/skills/performing-sca-dependency-scanning-with-snyk/references/workflows.md create mode 100644 personas/_shared/skills/performing-sca-dependency-scanning-with-snyk/scripts/agent.py create mode 100644 personas/_shared/skills/performing-sca-dependency-scanning-with-snyk/scripts/process.py create mode 100644 personas/_shared/skills/performing-scada-hmi-security-assessment/LICENSE create mode 100644 personas/_shared/skills/performing-scada-hmi-security-assessment/SKILL.md create mode 100644 personas/_shared/skills/performing-scada-hmi-security-assessment/references/api-reference.md create mode 100644 personas/_shared/skills/performing-scada-hmi-security-assessment/scripts/agent.py create mode 100644 personas/_shared/skills/performing-second-order-sql-injection/LICENSE create mode 100644 personas/_shared/skills/performing-second-order-sql-injection/SKILL.md create mode 100644 personas/_shared/skills/performing-second-order-sql-injection/references/api-reference.md create mode 100644 personas/_shared/skills/performing-second-order-sql-injection/scripts/agent.py create mode 100644 personas/_shared/skills/performing-security-headers-audit/LICENSE create mode 100644 personas/_shared/skills/performing-security-headers-audit/SKILL.md create mode 100644 personas/_shared/skills/performing-security-headers-audit/references/api-reference.md create mode 100644 personas/_shared/skills/performing-security-headers-audit/scripts/agent.py create mode 100644 personas/_shared/skills/performing-serverless-function-security-review/LICENSE create mode 100644 personas/_shared/skills/performing-serverless-function-security-review/SKILL.md create mode 100644 personas/_shared/skills/performing-serverless-function-security-review/references/api-reference.md create mode 100644 personas/_shared/skills/performing-serverless-function-security-review/scripts/agent.py create mode 100644 personas/_shared/skills/performing-service-account-audit/LICENSE create mode 100644 personas/_shared/skills/performing-service-account-audit/SKILL.md create mode 100644 personas/_shared/skills/performing-service-account-audit/assets/template.md create mode 100644 personas/_shared/skills/performing-service-account-audit/references/api-reference.md create mode 100644 personas/_shared/skills/performing-service-account-audit/references/standards.md create mode 100644 personas/_shared/skills/performing-service-account-audit/references/workflows.md create mode 100644 personas/_shared/skills/performing-service-account-audit/scripts/agent.py create mode 100644 personas/_shared/skills/performing-service-account-audit/scripts/process.py create mode 100644 personas/_shared/skills/performing-service-account-credential-rotation/LICENSE create mode 100644 personas/_shared/skills/performing-service-account-credential-rotation/SKILL.md create mode 100644 personas/_shared/skills/performing-service-account-credential-rotation/assets/template.md create mode 100644 personas/_shared/skills/performing-service-account-credential-rotation/references/api-reference.md create mode 100644 personas/_shared/skills/performing-service-account-credential-rotation/references/standards.md create mode 100644 personas/_shared/skills/performing-service-account-credential-rotation/references/workflows.md create mode 100644 personas/_shared/skills/performing-service-account-credential-rotation/scripts/agent.py create mode 100644 personas/_shared/skills/performing-service-account-credential-rotation/scripts/process.py create mode 100644 personas/_shared/skills/performing-soap-web-service-security-testing/LICENSE create mode 100644 personas/_shared/skills/performing-soap-web-service-security-testing/SKILL.md create mode 100644 personas/_shared/skills/performing-soap-web-service-security-testing/references/api-reference.md create mode 100644 personas/_shared/skills/performing-soap-web-service-security-testing/scripts/agent.py create mode 100644 personas/_shared/skills/performing-soc-tabletop-exercise/LICENSE create mode 100644 personas/_shared/skills/performing-soc-tabletop-exercise/SKILL.md create mode 100644 personas/_shared/skills/performing-soc-tabletop-exercise/references/api-reference.md create mode 100644 personas/_shared/skills/performing-soc-tabletop-exercise/scripts/agent.py create mode 100644 personas/_shared/skills/performing-soc2-type2-audit-preparation/LICENSE create mode 100644 personas/_shared/skills/performing-soc2-type2-audit-preparation/SKILL.md create mode 100644 personas/_shared/skills/performing-soc2-type2-audit-preparation/assets/template.md create mode 100644 personas/_shared/skills/performing-soc2-type2-audit-preparation/references/api-reference.md create mode 100644 personas/_shared/skills/performing-soc2-type2-audit-preparation/references/standards.md create mode 100644 personas/_shared/skills/performing-soc2-type2-audit-preparation/references/workflows.md create mode 100644 personas/_shared/skills/performing-soc2-type2-audit-preparation/scripts/agent.py create mode 100644 personas/_shared/skills/performing-soc2-type2-audit-preparation/scripts/process.py create mode 100644 personas/_shared/skills/performing-sqlite-database-forensics/LICENSE create mode 100644 personas/_shared/skills/performing-sqlite-database-forensics/SKILL.md create mode 100644 personas/_shared/skills/performing-sqlite-database-forensics/assets/template.md create mode 100644 personas/_shared/skills/performing-sqlite-database-forensics/references/api-reference.md create mode 100644 personas/_shared/skills/performing-sqlite-database-forensics/references/standards.md create mode 100644 personas/_shared/skills/performing-sqlite-database-forensics/references/workflows.md create mode 100644 personas/_shared/skills/performing-sqlite-database-forensics/scripts/agent.py create mode 100644 personas/_shared/skills/performing-sqlite-database-forensics/scripts/process.py create mode 100644 personas/_shared/skills/performing-ssl-certificate-lifecycle-management/LICENSE create mode 100644 personas/_shared/skills/performing-ssl-certificate-lifecycle-management/SKILL.md create mode 100644 personas/_shared/skills/performing-ssl-certificate-lifecycle-management/assets/template.md create mode 100644 personas/_shared/skills/performing-ssl-certificate-lifecycle-management/references/api-reference.md create mode 100644 personas/_shared/skills/performing-ssl-certificate-lifecycle-management/references/standards.md create mode 100644 personas/_shared/skills/performing-ssl-certificate-lifecycle-management/references/workflows.md create mode 100644 personas/_shared/skills/performing-ssl-certificate-lifecycle-management/scripts/agent.py create mode 100644 personas/_shared/skills/performing-ssl-certificate-lifecycle-management/scripts/process.py create mode 100644 personas/_shared/skills/performing-ssl-stripping-attack/LICENSE create mode 100644 personas/_shared/skills/performing-ssl-stripping-attack/SKILL.md create mode 100644 personas/_shared/skills/performing-ssl-stripping-attack/references/api-reference.md create mode 100644 personas/_shared/skills/performing-ssl-stripping-attack/scripts/agent.py create mode 100644 personas/_shared/skills/performing-ssl-tls-inspection-configuration/LICENSE create mode 100644 personas/_shared/skills/performing-ssl-tls-inspection-configuration/SKILL.md create mode 100644 personas/_shared/skills/performing-ssl-tls-inspection-configuration/references/api-reference.md create mode 100644 personas/_shared/skills/performing-ssl-tls-inspection-configuration/scripts/agent.py create mode 100644 personas/_shared/skills/performing-ssl-tls-security-assessment/LICENSE create mode 100644 personas/_shared/skills/performing-ssl-tls-security-assessment/SKILL.md create mode 100644 personas/_shared/skills/performing-ssl-tls-security-assessment/references/api-reference.md create mode 100644 personas/_shared/skills/performing-ssl-tls-security-assessment/scripts/agent.py create mode 100644 personas/_shared/skills/performing-ssrf-vulnerability-exploitation/LICENSE create mode 100644 personas/_shared/skills/performing-ssrf-vulnerability-exploitation/SKILL.md create mode 100644 personas/_shared/skills/performing-ssrf-vulnerability-exploitation/references/api-reference.md create mode 100644 personas/_shared/skills/performing-ssrf-vulnerability-exploitation/scripts/agent.py create mode 100644 personas/_shared/skills/performing-static-malware-analysis-with-pe-studio/LICENSE create mode 100644 personas/_shared/skills/performing-static-malware-analysis-with-pe-studio/SKILL.md create mode 100644 personas/_shared/skills/performing-static-malware-analysis-with-pe-studio/references/api-reference.md create mode 100644 personas/_shared/skills/performing-static-malware-analysis-with-pe-studio/scripts/agent.py create mode 100644 personas/_shared/skills/performing-steganography-detection/LICENSE create mode 100644 personas/_shared/skills/performing-steganography-detection/SKILL.md create mode 100644 personas/_shared/skills/performing-steganography-detection/references/api-reference.md create mode 100644 personas/_shared/skills/performing-steganography-detection/scripts/agent.py create mode 100644 personas/_shared/skills/performing-subdomain-enumeration-with-subfinder/LICENSE create mode 100644 personas/_shared/skills/performing-subdomain-enumeration-with-subfinder/SKILL.md create mode 100644 personas/_shared/skills/performing-subdomain-enumeration-with-subfinder/assets/template.md create mode 100644 personas/_shared/skills/performing-subdomain-enumeration-with-subfinder/references/api-reference.md create mode 100644 personas/_shared/skills/performing-subdomain-enumeration-with-subfinder/references/standards.md create mode 100644 personas/_shared/skills/performing-subdomain-enumeration-with-subfinder/references/workflows.md create mode 100644 personas/_shared/skills/performing-subdomain-enumeration-with-subfinder/scripts/agent.py create mode 100644 personas/_shared/skills/performing-subdomain-enumeration-with-subfinder/scripts/process.py create mode 100644 personas/_shared/skills/performing-supply-chain-attack-simulation/LICENSE create mode 100644 personas/_shared/skills/performing-supply-chain-attack-simulation/SKILL.md create mode 100644 personas/_shared/skills/performing-supply-chain-attack-simulation/references/api-reference.md create mode 100644 personas/_shared/skills/performing-supply-chain-attack-simulation/scripts/agent.py create mode 100644 personas/_shared/skills/performing-thick-client-application-penetration-test/LICENSE create mode 100644 personas/_shared/skills/performing-thick-client-application-penetration-test/SKILL.md create mode 100644 personas/_shared/skills/performing-thick-client-application-penetration-test/assets/template.md create mode 100644 personas/_shared/skills/performing-thick-client-application-penetration-test/references/api-reference.md create mode 100644 personas/_shared/skills/performing-thick-client-application-penetration-test/references/standards.md create mode 100644 personas/_shared/skills/performing-thick-client-application-penetration-test/references/workflows.md create mode 100644 personas/_shared/skills/performing-thick-client-application-penetration-test/scripts/agent.py create mode 100644 personas/_shared/skills/performing-thick-client-application-penetration-test/scripts/process.py create mode 100644 personas/_shared/skills/performing-threat-emulation-with-atomic-red-team/LICENSE create mode 100644 personas/_shared/skills/performing-threat-emulation-with-atomic-red-team/SKILL.md create mode 100644 personas/_shared/skills/performing-threat-emulation-with-atomic-red-team/references/api-reference.md create mode 100644 personas/_shared/skills/performing-threat-emulation-with-atomic-red-team/scripts/agent.py create mode 100644 personas/_shared/skills/performing-threat-hunting-with-elastic-siem/LICENSE create mode 100644 personas/_shared/skills/performing-threat-hunting-with-elastic-siem/SKILL.md create mode 100644 personas/_shared/skills/performing-threat-hunting-with-elastic-siem/references/api-reference.md create mode 100644 personas/_shared/skills/performing-threat-hunting-with-elastic-siem/scripts/agent.py create mode 100644 personas/_shared/skills/performing-threat-hunting-with-yara-rules/LICENSE create mode 100644 personas/_shared/skills/performing-threat-hunting-with-yara-rules/SKILL.md create mode 100644 personas/_shared/skills/performing-threat-hunting-with-yara-rules/references/api-reference.md create mode 100644 personas/_shared/skills/performing-threat-hunting-with-yara-rules/scripts/agent.py create mode 100644 personas/_shared/skills/performing-threat-intelligence-sharing-with-misp/LICENSE create mode 100644 personas/_shared/skills/performing-threat-intelligence-sharing-with-misp/SKILL.md create mode 100644 personas/_shared/skills/performing-threat-intelligence-sharing-with-misp/references/api-reference.md create mode 100644 personas/_shared/skills/performing-threat-intelligence-sharing-with-misp/scripts/agent.py create mode 100644 personas/_shared/skills/performing-threat-landscape-assessment-for-sector/LICENSE create mode 100644 personas/_shared/skills/performing-threat-landscape-assessment-for-sector/SKILL.md create mode 100644 personas/_shared/skills/performing-threat-landscape-assessment-for-sector/references/api-reference.md create mode 100644 personas/_shared/skills/performing-threat-landscape-assessment-for-sector/scripts/agent.py create mode 100644 personas/_shared/skills/performing-threat-modeling-with-owasp-threat-dragon/LICENSE create mode 100644 personas/_shared/skills/performing-threat-modeling-with-owasp-threat-dragon/SKILL.md create mode 100644 personas/_shared/skills/performing-threat-modeling-with-owasp-threat-dragon/assets/template.md create mode 100644 personas/_shared/skills/performing-threat-modeling-with-owasp-threat-dragon/references/api-reference.md create mode 100644 personas/_shared/skills/performing-threat-modeling-with-owasp-threat-dragon/references/standards.md create mode 100644 personas/_shared/skills/performing-threat-modeling-with-owasp-threat-dragon/references/workflows.md create mode 100644 personas/_shared/skills/performing-threat-modeling-with-owasp-threat-dragon/scripts/agent.py create mode 100644 personas/_shared/skills/performing-threat-modeling-with-owasp-threat-dragon/scripts/process.py create mode 100644 personas/_shared/skills/performing-timeline-reconstruction-with-plaso/LICENSE create mode 100644 personas/_shared/skills/performing-timeline-reconstruction-with-plaso/SKILL.md create mode 100644 personas/_shared/skills/performing-timeline-reconstruction-with-plaso/references/api-reference.md create mode 100644 personas/_shared/skills/performing-timeline-reconstruction-with-plaso/scripts/agent.py create mode 100644 personas/_shared/skills/performing-user-behavior-analytics/LICENSE create mode 100644 personas/_shared/skills/performing-user-behavior-analytics/SKILL.md create mode 100644 personas/_shared/skills/performing-user-behavior-analytics/references/api-reference.md create mode 100644 personas/_shared/skills/performing-user-behavior-analytics/scripts/agent.py create mode 100644 personas/_shared/skills/performing-vlan-hopping-attack/LICENSE create mode 100644 personas/_shared/skills/performing-vlan-hopping-attack/SKILL.md create mode 100644 personas/_shared/skills/performing-vlan-hopping-attack/references/api-reference.md create mode 100644 personas/_shared/skills/performing-vlan-hopping-attack/scripts/agent.py create mode 100644 personas/_shared/skills/performing-vulnerability-scanning-with-nessus/LICENSE create mode 100644 personas/_shared/skills/performing-vulnerability-scanning-with-nessus/SKILL.md create mode 100644 personas/_shared/skills/performing-vulnerability-scanning-with-nessus/references/api-reference.md create mode 100644 personas/_shared/skills/performing-vulnerability-scanning-with-nessus/scripts/agent.py create mode 100644 personas/_shared/skills/performing-web-application-firewall-bypass/LICENSE create mode 100644 personas/_shared/skills/performing-web-application-firewall-bypass/SKILL.md create mode 100644 personas/_shared/skills/performing-web-application-firewall-bypass/references/api-reference.md create mode 100644 personas/_shared/skills/performing-web-application-firewall-bypass/scripts/agent.py create mode 100644 personas/_shared/skills/performing-web-application-penetration-test/LICENSE create mode 100644 personas/_shared/skills/performing-web-application-penetration-test/SKILL.md create mode 100644 personas/_shared/skills/performing-web-application-penetration-test/references/api-reference.md create mode 100644 personas/_shared/skills/performing-web-application-penetration-test/scripts/agent.py create mode 100644 personas/_shared/skills/performing-web-application-scanning-with-nikto/LICENSE create mode 100644 personas/_shared/skills/performing-web-application-scanning-with-nikto/SKILL.md create mode 100644 personas/_shared/skills/performing-web-application-scanning-with-nikto/assets/template.md create mode 100644 personas/_shared/skills/performing-web-application-scanning-with-nikto/references/api-reference.md create mode 100644 personas/_shared/skills/performing-web-application-scanning-with-nikto/references/standards.md create mode 100644 personas/_shared/skills/performing-web-application-scanning-with-nikto/references/workflows.md create mode 100644 personas/_shared/skills/performing-web-application-scanning-with-nikto/scripts/agent.py create mode 100644 personas/_shared/skills/performing-web-application-scanning-with-nikto/scripts/process.py create mode 100644 personas/_shared/skills/performing-web-application-vulnerability-triage/LICENSE create mode 100644 personas/_shared/skills/performing-web-application-vulnerability-triage/SKILL.md create mode 100644 personas/_shared/skills/performing-web-application-vulnerability-triage/references/api-reference.md create mode 100644 personas/_shared/skills/performing-web-application-vulnerability-triage/references/standards.md create mode 100644 personas/_shared/skills/performing-web-application-vulnerability-triage/references/workflows.md create mode 100644 personas/_shared/skills/performing-web-application-vulnerability-triage/scripts/agent.py create mode 100644 personas/_shared/skills/performing-web-cache-deception-attack/LICENSE create mode 100644 personas/_shared/skills/performing-web-cache-deception-attack/SKILL.md create mode 100644 personas/_shared/skills/performing-web-cache-deception-attack/references/api-reference.md create mode 100644 personas/_shared/skills/performing-web-cache-deception-attack/scripts/agent.py create mode 100644 personas/_shared/skills/performing-web-cache-poisoning-attack/LICENSE create mode 100644 personas/_shared/skills/performing-web-cache-poisoning-attack/SKILL.md create mode 100644 personas/_shared/skills/performing-web-cache-poisoning-attack/references/api-reference.md create mode 100644 personas/_shared/skills/performing-web-cache-poisoning-attack/scripts/agent.py create mode 100644 personas/_shared/skills/performing-wifi-password-cracking-with-aircrack/LICENSE create mode 100644 personas/_shared/skills/performing-wifi-password-cracking-with-aircrack/SKILL.md create mode 100644 personas/_shared/skills/performing-wifi-password-cracking-with-aircrack/references/api-reference.md create mode 100644 personas/_shared/skills/performing-wifi-password-cracking-with-aircrack/scripts/agent.py create mode 100644 personas/_shared/skills/performing-windows-artifact-analysis-with-eric-zimmerman-tools/LICENSE create mode 100644 personas/_shared/skills/performing-windows-artifact-analysis-with-eric-zimmerman-tools/SKILL.md create mode 100644 personas/_shared/skills/performing-windows-artifact-analysis-with-eric-zimmerman-tools/assets/template.md create mode 100644 personas/_shared/skills/performing-windows-artifact-analysis-with-eric-zimmerman-tools/references/api-reference.md create mode 100644 personas/_shared/skills/performing-windows-artifact-analysis-with-eric-zimmerman-tools/references/standards.md create mode 100644 personas/_shared/skills/performing-windows-artifact-analysis-with-eric-zimmerman-tools/references/workflows.md create mode 100644 personas/_shared/skills/performing-windows-artifact-analysis-with-eric-zimmerman-tools/scripts/agent.py create mode 100644 personas/_shared/skills/performing-windows-artifact-analysis-with-eric-zimmerman-tools/scripts/process.py create mode 100644 personas/_shared/skills/performing-wireless-network-penetration-test/LICENSE create mode 100644 personas/_shared/skills/performing-wireless-network-penetration-test/SKILL.md create mode 100644 personas/_shared/skills/performing-wireless-network-penetration-test/assets/template.md create mode 100644 personas/_shared/skills/performing-wireless-network-penetration-test/references/api-reference.md create mode 100644 personas/_shared/skills/performing-wireless-network-penetration-test/references/standards.md create mode 100644 personas/_shared/skills/performing-wireless-network-penetration-test/references/workflows.md create mode 100644 personas/_shared/skills/performing-wireless-network-penetration-test/scripts/agent.py create mode 100644 personas/_shared/skills/performing-wireless-network-penetration-test/scripts/process.py create mode 100644 personas/_shared/skills/performing-wireless-security-assessment-with-kismet/LICENSE create mode 100644 personas/_shared/skills/performing-wireless-security-assessment-with-kismet/SKILL.md create mode 100644 personas/_shared/skills/performing-wireless-security-assessment-with-kismet/references/api-reference.md create mode 100644 personas/_shared/skills/performing-wireless-security-assessment-with-kismet/scripts/agent.py create mode 100644 personas/_shared/skills/performing-yara-rule-development-for-detection/LICENSE create mode 100644 personas/_shared/skills/performing-yara-rule-development-for-detection/SKILL.md create mode 100644 personas/_shared/skills/performing-yara-rule-development-for-detection/assets/template.md create mode 100644 personas/_shared/skills/performing-yara-rule-development-for-detection/references/api-reference.md create mode 100644 personas/_shared/skills/performing-yara-rule-development-for-detection/references/standards.md create mode 100644 personas/_shared/skills/performing-yara-rule-development-for-detection/references/workflows.md create mode 100644 personas/_shared/skills/performing-yara-rule-development-for-detection/scripts/agent.py create mode 100644 personas/_shared/skills/performing-yara-rule-development-for-detection/scripts/process.py create mode 100644 personas/_shared/skills/prioritizing-vulnerabilities-with-cvss-scoring/LICENSE create mode 100644 personas/_shared/skills/prioritizing-vulnerabilities-with-cvss-scoring/SKILL.md create mode 100644 personas/_shared/skills/prioritizing-vulnerabilities-with-cvss-scoring/assets/template.md create mode 100644 personas/_shared/skills/prioritizing-vulnerabilities-with-cvss-scoring/references/api-reference.md create mode 100644 personas/_shared/skills/prioritizing-vulnerabilities-with-cvss-scoring/references/standards.md create mode 100644 personas/_shared/skills/prioritizing-vulnerabilities-with-cvss-scoring/references/workflows.md create mode 100644 personas/_shared/skills/prioritizing-vulnerabilities-with-cvss-scoring/scripts/agent.py create mode 100644 personas/_shared/skills/prioritizing-vulnerabilities-with-cvss-scoring/scripts/process.py create mode 100644 personas/_shared/skills/processing-stix-taxii-feeds/LICENSE create mode 100644 personas/_shared/skills/processing-stix-taxii-feeds/SKILL.md create mode 100644 personas/_shared/skills/processing-stix-taxii-feeds/references/api-reference.md create mode 100644 personas/_shared/skills/processing-stix-taxii-feeds/scripts/agent.py create mode 100644 personas/_shared/skills/profiling-threat-actor-groups/LICENSE create mode 100644 personas/_shared/skills/profiling-threat-actor-groups/SKILL.md create mode 100644 personas/_shared/skills/profiling-threat-actor-groups/references/api-reference.md create mode 100644 personas/_shared/skills/profiling-threat-actor-groups/scripts/agent.py create mode 100644 personas/_shared/skills/recovering-deleted-files-with-photorec/LICENSE create mode 100644 personas/_shared/skills/recovering-deleted-files-with-photorec/SKILL.md create mode 100644 personas/_shared/skills/recovering-deleted-files-with-photorec/references/api-reference.md create mode 100644 personas/_shared/skills/recovering-deleted-files-with-photorec/scripts/agent.py create mode 100644 personas/_shared/skills/recovering-from-ransomware-attack/LICENSE create mode 100644 personas/_shared/skills/recovering-from-ransomware-attack/SKILL.md create mode 100644 personas/_shared/skills/recovering-from-ransomware-attack/references/api-reference.md create mode 100644 personas/_shared/skills/recovering-from-ransomware-attack/references/standards.md create mode 100644 personas/_shared/skills/recovering-from-ransomware-attack/references/workflows.md create mode 100644 personas/_shared/skills/recovering-from-ransomware-attack/scripts/agent.py create mode 100644 personas/_shared/skills/recovering-from-ransomware-attack/scripts/process.py create mode 100644 personas/_shared/skills/remediating-s3-bucket-misconfiguration/LICENSE create mode 100644 personas/_shared/skills/remediating-s3-bucket-misconfiguration/SKILL.md create mode 100644 personas/_shared/skills/remediating-s3-bucket-misconfiguration/references/api-reference.md create mode 100644 personas/_shared/skills/remediating-s3-bucket-misconfiguration/scripts/agent.py create mode 100644 personas/_shared/skills/reverse-engineering-android-malware-with-jadx/LICENSE create mode 100644 personas/_shared/skills/reverse-engineering-android-malware-with-jadx/SKILL.md create mode 100644 personas/_shared/skills/reverse-engineering-android-malware-with-jadx/references/api-reference.md create mode 100644 personas/_shared/skills/reverse-engineering-android-malware-with-jadx/scripts/agent.py create mode 100644 personas/_shared/skills/reverse-engineering-dotnet-malware-with-dnspy/LICENSE create mode 100644 personas/_shared/skills/reverse-engineering-dotnet-malware-with-dnspy/SKILL.md create mode 100644 personas/_shared/skills/reverse-engineering-dotnet-malware-with-dnspy/references/api-reference.md create mode 100644 personas/_shared/skills/reverse-engineering-dotnet-malware-with-dnspy/scripts/agent.py create mode 100644 personas/_shared/skills/reverse-engineering-ios-app-with-frida/LICENSE create mode 100644 personas/_shared/skills/reverse-engineering-ios-app-with-frida/SKILL.md create mode 100644 personas/_shared/skills/reverse-engineering-ios-app-with-frida/assets/template.md create mode 100644 personas/_shared/skills/reverse-engineering-ios-app-with-frida/references/api-reference.md create mode 100644 personas/_shared/skills/reverse-engineering-ios-app-with-frida/references/standards.md create mode 100644 personas/_shared/skills/reverse-engineering-ios-app-with-frida/references/workflows.md create mode 100644 personas/_shared/skills/reverse-engineering-ios-app-with-frida/scripts/agent.py create mode 100644 personas/_shared/skills/reverse-engineering-ios-app-with-frida/scripts/process.py create mode 100644 personas/_shared/skills/reverse-engineering-malware-with-ghidra/LICENSE create mode 100644 personas/_shared/skills/reverse-engineering-malware-with-ghidra/SKILL.md create mode 100644 personas/_shared/skills/reverse-engineering-malware-with-ghidra/references/api-reference.md create mode 100644 personas/_shared/skills/reverse-engineering-malware-with-ghidra/scripts/agent.py create mode 100644 personas/_shared/skills/reverse-engineering-ransomware-encryption-routine/LICENSE create mode 100644 personas/_shared/skills/reverse-engineering-ransomware-encryption-routine/SKILL.md create mode 100644 personas/_shared/skills/reverse-engineering-ransomware-encryption-routine/assets/template.md create mode 100644 personas/_shared/skills/reverse-engineering-ransomware-encryption-routine/references/api-reference.md create mode 100644 personas/_shared/skills/reverse-engineering-ransomware-encryption-routine/references/standards.md create mode 100644 personas/_shared/skills/reverse-engineering-ransomware-encryption-routine/references/workflows.md create mode 100644 personas/_shared/skills/reverse-engineering-ransomware-encryption-routine/scripts/agent.py create mode 100644 personas/_shared/skills/reverse-engineering-ransomware-encryption-routine/scripts/process.py create mode 100644 personas/_shared/skills/reverse-engineering-rust-malware/LICENSE create mode 100644 personas/_shared/skills/reverse-engineering-rust-malware/SKILL.md create mode 100644 personas/_shared/skills/reverse-engineering-rust-malware/assets/template.md create mode 100644 personas/_shared/skills/reverse-engineering-rust-malware/references/api-reference.md create mode 100644 personas/_shared/skills/reverse-engineering-rust-malware/references/standards.md create mode 100644 personas/_shared/skills/reverse-engineering-rust-malware/references/workflows.md create mode 100644 personas/_shared/skills/reverse-engineering-rust-malware/scripts/agent.py create mode 100644 personas/_shared/skills/scanning-container-images-with-grype/LICENSE create mode 100644 personas/_shared/skills/scanning-container-images-with-grype/SKILL.md create mode 100644 personas/_shared/skills/scanning-container-images-with-grype/assets/template.md create mode 100644 personas/_shared/skills/scanning-container-images-with-grype/references/api-reference.md create mode 100644 personas/_shared/skills/scanning-container-images-with-grype/references/standards.md create mode 100644 personas/_shared/skills/scanning-container-images-with-grype/references/workflows.md create mode 100644 personas/_shared/skills/scanning-container-images-with-grype/scripts/agent.py create mode 100644 personas/_shared/skills/scanning-container-images-with-grype/scripts/process.py create mode 100644 personas/_shared/skills/scanning-containers-with-trivy-in-cicd/LICENSE create mode 100644 personas/_shared/skills/scanning-containers-with-trivy-in-cicd/SKILL.md create mode 100644 personas/_shared/skills/scanning-containers-with-trivy-in-cicd/assets/template.md create mode 100644 personas/_shared/skills/scanning-containers-with-trivy-in-cicd/references/api-reference.md create mode 100644 personas/_shared/skills/scanning-containers-with-trivy-in-cicd/references/standards.md create mode 100644 personas/_shared/skills/scanning-containers-with-trivy-in-cicd/references/workflows.md create mode 100644 personas/_shared/skills/scanning-containers-with-trivy-in-cicd/scripts/agent.py create mode 100644 personas/_shared/skills/scanning-containers-with-trivy-in-cicd/scripts/process.py create mode 100644 personas/_shared/skills/scanning-docker-images-with-trivy/LICENSE create mode 100644 personas/_shared/skills/scanning-docker-images-with-trivy/SKILL.md create mode 100644 personas/_shared/skills/scanning-docker-images-with-trivy/assets/template.md create mode 100644 personas/_shared/skills/scanning-docker-images-with-trivy/references/api-reference.md create mode 100644 personas/_shared/skills/scanning-docker-images-with-trivy/references/standards.md create mode 100644 personas/_shared/skills/scanning-docker-images-with-trivy/references/workflows.md create mode 100644 personas/_shared/skills/scanning-docker-images-with-trivy/scripts/agent.py create mode 100644 personas/_shared/skills/scanning-docker-images-with-trivy/scripts/process.py create mode 100644 personas/_shared/skills/scanning-infrastructure-with-nessus/LICENSE create mode 100644 personas/_shared/skills/scanning-infrastructure-with-nessus/SKILL.md create mode 100644 personas/_shared/skills/scanning-infrastructure-with-nessus/assets/template.md create mode 100644 personas/_shared/skills/scanning-infrastructure-with-nessus/references/api-reference.md create mode 100644 personas/_shared/skills/scanning-infrastructure-with-nessus/references/standards.md create mode 100644 personas/_shared/skills/scanning-infrastructure-with-nessus/references/workflows.md create mode 100644 personas/_shared/skills/scanning-infrastructure-with-nessus/scripts/agent.py create mode 100644 personas/_shared/skills/scanning-infrastructure-with-nessus/scripts/process.py create mode 100644 personas/_shared/skills/scanning-kubernetes-manifests-with-kubesec/LICENSE create mode 100644 personas/_shared/skills/scanning-kubernetes-manifests-with-kubesec/SKILL.md create mode 100644 personas/_shared/skills/scanning-kubernetes-manifests-with-kubesec/assets/template.md create mode 100644 personas/_shared/skills/scanning-kubernetes-manifests-with-kubesec/references/api-reference.md create mode 100644 personas/_shared/skills/scanning-kubernetes-manifests-with-kubesec/references/standards.md create mode 100644 personas/_shared/skills/scanning-kubernetes-manifests-with-kubesec/references/workflows.md create mode 100644 personas/_shared/skills/scanning-kubernetes-manifests-with-kubesec/scripts/agent.py create mode 100644 personas/_shared/skills/scanning-kubernetes-manifests-with-kubesec/scripts/process.py create mode 100644 personas/_shared/skills/scanning-network-with-nmap-advanced/LICENSE create mode 100644 personas/_shared/skills/scanning-network-with-nmap-advanced/SKILL.md create mode 100644 personas/_shared/skills/scanning-network-with-nmap-advanced/references/api-reference.md create mode 100644 personas/_shared/skills/scanning-network-with-nmap-advanced/scripts/agent.py create mode 100644 personas/_shared/skills/securing-api-gateway-with-aws-waf/LICENSE create mode 100644 personas/_shared/skills/securing-api-gateway-with-aws-waf/SKILL.md create mode 100644 personas/_shared/skills/securing-api-gateway-with-aws-waf/references/api-reference.md create mode 100644 personas/_shared/skills/securing-api-gateway-with-aws-waf/scripts/agent.py create mode 100644 personas/_shared/skills/securing-aws-iam-permissions/LICENSE create mode 100644 personas/_shared/skills/securing-aws-iam-permissions/SKILL.md create mode 100644 personas/_shared/skills/securing-aws-iam-permissions/references/api-reference.md create mode 100644 personas/_shared/skills/securing-aws-iam-permissions/scripts/agent.py create mode 100644 personas/_shared/skills/securing-aws-lambda-execution-roles/LICENSE create mode 100644 personas/_shared/skills/securing-aws-lambda-execution-roles/SKILL.md create mode 100644 personas/_shared/skills/securing-aws-lambda-execution-roles/references/api-reference.md create mode 100644 personas/_shared/skills/securing-aws-lambda-execution-roles/scripts/agent.py create mode 100644 personas/_shared/skills/securing-azure-with-microsoft-defender/LICENSE create mode 100644 personas/_shared/skills/securing-azure-with-microsoft-defender/SKILL.md create mode 100644 personas/_shared/skills/securing-azure-with-microsoft-defender/references/api-reference.md create mode 100644 personas/_shared/skills/securing-azure-with-microsoft-defender/scripts/agent.py create mode 100644 personas/_shared/skills/securing-container-registry-images/LICENSE create mode 100644 personas/_shared/skills/securing-container-registry-images/SKILL.md create mode 100644 personas/_shared/skills/securing-container-registry-images/references/api-reference.md create mode 100644 personas/_shared/skills/securing-container-registry-images/scripts/agent.py create mode 100644 personas/_shared/skills/securing-container-registry-with-harbor/LICENSE create mode 100644 personas/_shared/skills/securing-container-registry-with-harbor/SKILL.md create mode 100644 personas/_shared/skills/securing-container-registry-with-harbor/assets/template.md create mode 100644 personas/_shared/skills/securing-container-registry-with-harbor/references/api-reference.md create mode 100644 personas/_shared/skills/securing-container-registry-with-harbor/references/standards.md create mode 100644 personas/_shared/skills/securing-container-registry-with-harbor/references/workflows.md create mode 100644 personas/_shared/skills/securing-container-registry-with-harbor/scripts/agent.py create mode 100644 personas/_shared/skills/securing-container-registry-with-harbor/scripts/process.py create mode 100644 personas/_shared/skills/securing-github-actions-workflows/LICENSE create mode 100644 personas/_shared/skills/securing-github-actions-workflows/SKILL.md create mode 100644 personas/_shared/skills/securing-github-actions-workflows/assets/template.md create mode 100644 personas/_shared/skills/securing-github-actions-workflows/references/api-reference.md create mode 100644 personas/_shared/skills/securing-github-actions-workflows/references/standards.md create mode 100644 personas/_shared/skills/securing-github-actions-workflows/references/workflows.md create mode 100644 personas/_shared/skills/securing-github-actions-workflows/scripts/agent.py create mode 100644 personas/_shared/skills/securing-github-actions-workflows/scripts/process.py create mode 100644 personas/_shared/skills/securing-helm-chart-deployments/LICENSE create mode 100644 personas/_shared/skills/securing-helm-chart-deployments/SKILL.md create mode 100644 personas/_shared/skills/securing-helm-chart-deployments/assets/template.md create mode 100644 personas/_shared/skills/securing-helm-chart-deployments/references/api-reference.md create mode 100644 personas/_shared/skills/securing-helm-chart-deployments/references/standards.md create mode 100644 personas/_shared/skills/securing-helm-chart-deployments/references/workflows.md create mode 100644 personas/_shared/skills/securing-helm-chart-deployments/scripts/agent.py create mode 100644 personas/_shared/skills/securing-helm-chart-deployments/scripts/process.py create mode 100644 personas/_shared/skills/securing-historian-server-in-ot-environment/LICENSE create mode 100644 personas/_shared/skills/securing-historian-server-in-ot-environment/SKILL.md create mode 100644 personas/_shared/skills/securing-historian-server-in-ot-environment/references/api-reference.md create mode 100644 personas/_shared/skills/securing-historian-server-in-ot-environment/scripts/agent.py create mode 100644 personas/_shared/skills/securing-kubernetes-on-cloud/LICENSE create mode 100644 personas/_shared/skills/securing-kubernetes-on-cloud/SKILL.md create mode 100644 personas/_shared/skills/securing-kubernetes-on-cloud/references/api-reference.md create mode 100644 personas/_shared/skills/securing-kubernetes-on-cloud/scripts/agent.py create mode 100644 personas/_shared/skills/securing-remote-access-to-ot-environment/LICENSE create mode 100644 personas/_shared/skills/securing-remote-access-to-ot-environment/SKILL.md create mode 100644 personas/_shared/skills/securing-remote-access-to-ot-environment/references/api-reference.md create mode 100644 personas/_shared/skills/securing-remote-access-to-ot-environment/scripts/agent.py create mode 100644 personas/_shared/skills/securing-serverless-functions/LICENSE create mode 100644 personas/_shared/skills/securing-serverless-functions/SKILL.md create mode 100644 personas/_shared/skills/securing-serverless-functions/references/api-reference.md create mode 100644 personas/_shared/skills/securing-serverless-functions/scripts/agent.py create mode 100644 personas/_shared/skills/testing-android-intents-for-vulnerabilities/LICENSE create mode 100644 personas/_shared/skills/testing-android-intents-for-vulnerabilities/SKILL.md create mode 100644 personas/_shared/skills/testing-android-intents-for-vulnerabilities/assets/template.md create mode 100644 personas/_shared/skills/testing-android-intents-for-vulnerabilities/references/api-reference.md create mode 100644 personas/_shared/skills/testing-android-intents-for-vulnerabilities/references/standards.md create mode 100644 personas/_shared/skills/testing-android-intents-for-vulnerabilities/references/workflows.md create mode 100644 personas/_shared/skills/testing-android-intents-for-vulnerabilities/scripts/agent.py create mode 100644 personas/_shared/skills/testing-android-intents-for-vulnerabilities/scripts/process.py create mode 100644 personas/_shared/skills/testing-api-authentication-weaknesses/LICENSE create mode 100644 personas/_shared/skills/testing-api-authentication-weaknesses/SKILL.md create mode 100644 personas/_shared/skills/testing-api-authentication-weaknesses/references/api-reference.md create mode 100644 personas/_shared/skills/testing-api-authentication-weaknesses/scripts/agent.py create mode 100644 personas/_shared/skills/testing-api-for-broken-object-level-authorization/LICENSE create mode 100644 personas/_shared/skills/testing-api-for-broken-object-level-authorization/SKILL.md create mode 100644 personas/_shared/skills/testing-api-for-broken-object-level-authorization/references/api-reference.md create mode 100644 personas/_shared/skills/testing-api-for-broken-object-level-authorization/scripts/agent.py create mode 100644 personas/_shared/skills/testing-api-for-mass-assignment-vulnerability/LICENSE create mode 100644 personas/_shared/skills/testing-api-for-mass-assignment-vulnerability/SKILL.md create mode 100644 personas/_shared/skills/testing-api-for-mass-assignment-vulnerability/references/api-reference.md create mode 100644 personas/_shared/skills/testing-api-for-mass-assignment-vulnerability/scripts/agent.py create mode 100644 personas/_shared/skills/testing-api-security-with-owasp-top-10/LICENSE create mode 100644 personas/_shared/skills/testing-api-security-with-owasp-top-10/SKILL.md create mode 100644 personas/_shared/skills/testing-api-security-with-owasp-top-10/references/api-reference.md create mode 100644 personas/_shared/skills/testing-api-security-with-owasp-top-10/scripts/agent.py create mode 100644 personas/_shared/skills/testing-cors-misconfiguration/LICENSE create mode 100644 personas/_shared/skills/testing-cors-misconfiguration/SKILL.md create mode 100644 personas/_shared/skills/testing-cors-misconfiguration/references/api-reference.md create mode 100644 personas/_shared/skills/testing-cors-misconfiguration/scripts/agent.py create mode 100644 personas/_shared/skills/testing-for-broken-access-control/LICENSE create mode 100644 personas/_shared/skills/testing-for-broken-access-control/SKILL.md create mode 100644 personas/_shared/skills/testing-for-broken-access-control/references/api-reference.md create mode 100644 personas/_shared/skills/testing-for-broken-access-control/scripts/agent.py create mode 100644 personas/_shared/skills/testing-for-business-logic-vulnerabilities/LICENSE create mode 100644 personas/_shared/skills/testing-for-business-logic-vulnerabilities/SKILL.md create mode 100644 personas/_shared/skills/testing-for-business-logic-vulnerabilities/references/api-reference.md create mode 100644 personas/_shared/skills/testing-for-business-logic-vulnerabilities/scripts/agent.py create mode 100644 personas/_shared/skills/testing-for-email-header-injection/LICENSE create mode 100644 personas/_shared/skills/testing-for-email-header-injection/SKILL.md create mode 100644 personas/_shared/skills/testing-for-email-header-injection/references/api-reference.md create mode 100644 personas/_shared/skills/testing-for-email-header-injection/scripts/agent.py create mode 100644 personas/_shared/skills/testing-for-host-header-injection/LICENSE create mode 100644 personas/_shared/skills/testing-for-host-header-injection/SKILL.md create mode 100644 personas/_shared/skills/testing-for-host-header-injection/references/api-reference.md create mode 100644 personas/_shared/skills/testing-for-host-header-injection/scripts/agent.py create mode 100644 personas/_shared/skills/testing-for-json-web-token-vulnerabilities/LICENSE create mode 100644 personas/_shared/skills/testing-for-json-web-token-vulnerabilities/SKILL.md create mode 100644 personas/_shared/skills/testing-for-json-web-token-vulnerabilities/references/api-reference.md create mode 100644 personas/_shared/skills/testing-for-json-web-token-vulnerabilities/scripts/agent.py create mode 100644 personas/_shared/skills/testing-for-open-redirect-vulnerabilities/LICENSE create mode 100644 personas/_shared/skills/testing-for-open-redirect-vulnerabilities/SKILL.md create mode 100644 personas/_shared/skills/testing-for-open-redirect-vulnerabilities/references/api-reference.md create mode 100644 personas/_shared/skills/testing-for-open-redirect-vulnerabilities/scripts/agent.py create mode 100644 personas/_shared/skills/testing-for-sensitive-data-exposure/LICENSE create mode 100644 personas/_shared/skills/testing-for-sensitive-data-exposure/SKILL.md create mode 100644 personas/_shared/skills/testing-for-sensitive-data-exposure/references/api-reference.md create mode 100644 personas/_shared/skills/testing-for-sensitive-data-exposure/scripts/agent.py create mode 100644 personas/_shared/skills/testing-for-xml-injection-vulnerabilities/LICENSE create mode 100644 personas/_shared/skills/testing-for-xml-injection-vulnerabilities/SKILL.md create mode 100644 personas/_shared/skills/testing-for-xml-injection-vulnerabilities/references/api-reference.md create mode 100644 personas/_shared/skills/testing-for-xml-injection-vulnerabilities/scripts/agent.py create mode 100644 personas/_shared/skills/testing-for-xss-vulnerabilities-with-burpsuite/LICENSE create mode 100644 personas/_shared/skills/testing-for-xss-vulnerabilities-with-burpsuite/SKILL.md create mode 100644 personas/_shared/skills/testing-for-xss-vulnerabilities-with-burpsuite/references/api-reference.md create mode 100644 personas/_shared/skills/testing-for-xss-vulnerabilities-with-burpsuite/scripts/agent.py create mode 100644 personas/_shared/skills/testing-for-xss-vulnerabilities/LICENSE create mode 100644 personas/_shared/skills/testing-for-xss-vulnerabilities/SKILL.md create mode 100644 personas/_shared/skills/testing-for-xss-vulnerabilities/references/api-reference.md create mode 100644 personas/_shared/skills/testing-for-xss-vulnerabilities/scripts/agent.py create mode 100644 personas/_shared/skills/testing-for-xxe-injection-vulnerabilities/LICENSE create mode 100644 personas/_shared/skills/testing-for-xxe-injection-vulnerabilities/SKILL.md create mode 100644 personas/_shared/skills/testing-for-xxe-injection-vulnerabilities/references/api-reference.md create mode 100644 personas/_shared/skills/testing-for-xxe-injection-vulnerabilities/scripts/agent.py create mode 100644 personas/_shared/skills/testing-jwt-token-security/LICENSE create mode 100644 personas/_shared/skills/testing-jwt-token-security/SKILL.md create mode 100644 personas/_shared/skills/testing-jwt-token-security/references/api-reference.md create mode 100644 personas/_shared/skills/testing-jwt-token-security/scripts/agent.py create mode 100644 personas/_shared/skills/testing-mobile-api-authentication/LICENSE create mode 100644 personas/_shared/skills/testing-mobile-api-authentication/SKILL.md create mode 100644 personas/_shared/skills/testing-mobile-api-authentication/assets/template.md create mode 100644 personas/_shared/skills/testing-mobile-api-authentication/references/api-reference.md create mode 100644 personas/_shared/skills/testing-mobile-api-authentication/references/standards.md create mode 100644 personas/_shared/skills/testing-mobile-api-authentication/references/workflows.md create mode 100644 personas/_shared/skills/testing-mobile-api-authentication/scripts/agent.py create mode 100644 personas/_shared/skills/testing-mobile-api-authentication/scripts/process.py create mode 100644 personas/_shared/skills/testing-oauth2-implementation-flaws/LICENSE create mode 100644 personas/_shared/skills/testing-oauth2-implementation-flaws/SKILL.md create mode 100644 personas/_shared/skills/testing-oauth2-implementation-flaws/references/api-reference.md create mode 100644 personas/_shared/skills/testing-oauth2-implementation-flaws/scripts/agent.py create mode 100644 personas/_shared/skills/testing-ransomware-recovery-procedures/LICENSE create mode 100644 personas/_shared/skills/testing-ransomware-recovery-procedures/SKILL.md create mode 100644 personas/_shared/skills/testing-ransomware-recovery-procedures/references/api-reference.md create mode 100644 personas/_shared/skills/testing-ransomware-recovery-procedures/scripts/agent.py create mode 100644 personas/_shared/skills/testing-websocket-api-security/LICENSE create mode 100644 personas/_shared/skills/testing-websocket-api-security/SKILL.md create mode 100644 personas/_shared/skills/testing-websocket-api-security/references/api-reference.md create mode 100644 personas/_shared/skills/testing-websocket-api-security/scripts/agent.py create mode 100644 personas/_shared/skills/tracking-threat-actor-infrastructure/LICENSE create mode 100644 personas/_shared/skills/tracking-threat-actor-infrastructure/SKILL.md create mode 100644 personas/_shared/skills/tracking-threat-actor-infrastructure/assets/template.md create mode 100644 personas/_shared/skills/tracking-threat-actor-infrastructure/references/api-reference.md create mode 100644 personas/_shared/skills/tracking-threat-actor-infrastructure/references/standards.md create mode 100644 personas/_shared/skills/tracking-threat-actor-infrastructure/references/workflows.md create mode 100644 personas/_shared/skills/tracking-threat-actor-infrastructure/scripts/agent.py create mode 100644 personas/_shared/skills/tracking-threat-actor-infrastructure/scripts/process.py create mode 100644 personas/_shared/skills/triaging-security-alerts-in-splunk/LICENSE create mode 100644 personas/_shared/skills/triaging-security-alerts-in-splunk/SKILL.md create mode 100644 personas/_shared/skills/triaging-security-alerts-in-splunk/references/api-reference.md create mode 100644 personas/_shared/skills/triaging-security-alerts-in-splunk/scripts/agent.py create mode 100644 personas/_shared/skills/triaging-security-incident-with-ir-playbook/LICENSE create mode 100644 personas/_shared/skills/triaging-security-incident-with-ir-playbook/SKILL.md create mode 100644 personas/_shared/skills/triaging-security-incident-with-ir-playbook/assets/template.md create mode 100644 personas/_shared/skills/triaging-security-incident-with-ir-playbook/references/api-reference.md create mode 100644 personas/_shared/skills/triaging-security-incident-with-ir-playbook/references/standards.md create mode 100644 personas/_shared/skills/triaging-security-incident-with-ir-playbook/references/workflows.md create mode 100644 personas/_shared/skills/triaging-security-incident-with-ir-playbook/scripts/agent.py create mode 100644 personas/_shared/skills/triaging-security-incident-with-ir-playbook/scripts/process.py create mode 100644 personas/_shared/skills/triaging-security-incident/LICENSE create mode 100644 personas/_shared/skills/triaging-security-incident/SKILL.md create mode 100644 personas/_shared/skills/triaging-security-incident/references/api-reference.md create mode 100644 personas/_shared/skills/triaging-security-incident/scripts/agent.py create mode 100644 personas/_shared/skills/triaging-vulnerabilities-with-ssvc-framework/LICENSE create mode 100644 personas/_shared/skills/triaging-vulnerabilities-with-ssvc-framework/SKILL.md create mode 100644 personas/_shared/skills/triaging-vulnerabilities-with-ssvc-framework/references/api-reference.md create mode 100644 personas/_shared/skills/triaging-vulnerabilities-with-ssvc-framework/references/standards.md create mode 100644 personas/_shared/skills/triaging-vulnerabilities-with-ssvc-framework/references/workflows.md create mode 100644 personas/_shared/skills/triaging-vulnerabilities-with-ssvc-framework/scripts/agent.py create mode 100644 personas/_shared/skills/triaging-vulnerabilities-with-ssvc-framework/scripts/process.py create mode 100644 personas/_shared/skills/validating-backup-integrity-for-recovery/LICENSE create mode 100644 personas/_shared/skills/validating-backup-integrity-for-recovery/SKILL.md create mode 100644 personas/_shared/skills/validating-backup-integrity-for-recovery/references/api-reference.md create mode 100644 personas/_shared/skills/validating-backup-integrity-for-recovery/scripts/agent.py diff --git a/.gitignore b/.gitignore index 29e0284..d4c81ed 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,6 @@ generated/ +sources/* +!sources/README.md config.yaml __pycache__/ *.pyc diff --git a/README.md b/README.md index 2934c3e..1b51e92 100644 --- a/README.md +++ b/README.md @@ -205,6 +205,20 @@ build.py # Build: .md → .yaml + .json + .prompt.md generated/ # Build output (gitignored) ``` +## Source Mirrors + +External source repositories used during integration are kept under `sources/`: + +```text +sources/ +├── Anthropic-Cybersecurity-Skills/ +├── paperclip-docs-main/ +└── temp-cyber-skills/ +``` + +- `build.py` prefers `personas/_shared` as canonical input. +- If `personas/_shared` is missing, `build.py` falls back to known mirrors under `sources/`. + ### Variant Types | Type | Purpose | Example | diff --git a/build.py b/build.py index efa910a..87a1eee 100755 --- a/build.py +++ b/build.py @@ -8,6 +8,7 @@ New users: copy config.example.yaml → config.yaml and customize. import json import re import sys +import unicodedata from pathlib import Path try: @@ -27,10 +28,48 @@ def load_config(root: Path) -> dict: example_path = root / "config.example.yaml" if example_path.exists(): - print("WARN: No config.yaml found. Using defaults. Copy config.example.yaml → config.yaml to customize.") + print( + "WARN: No config.yaml found. Using defaults. Copy config.example.yaml → config.yaml to customize." + ) return {} +def resolve_shared_dir(root: Path, personas_dir: Path) -> Path | None: + """Resolve canonical shared library path. + + Primary location is personas/_shared. If that is missing, fall back to + known source mirrors under sources/. + """ + primary = personas_dir / "_shared" + if primary.exists(): + return primary + + sources_dir = root / "sources" + fallbacks = [ + sources_dir / "temp-cyber-skills" / "personas" / "_shared", + sources_dir / "paperclip-docs-main" / "_shared", + ] + for candidate in fallbacks: + if candidate.exists(): + return candidate + + return None + + +def discover_sources(root: Path) -> list[str]: + """List known source mirrors under root/sources.""" + sources_dir = root / "sources" + if not sources_dir.exists(): + return [] + + known = [ + "Anthropic-Cybersecurity-Skills", + "paperclip-docs-main", + "temp-cyber-skills", + ] + return [name for name in known if (sources_dir / name).exists()] + + def flatten_config(config: dict, prefix: str = "") -> dict: """Flatten nested config dict for template substitution. @@ -44,7 +83,9 @@ def flatten_config(config: dict, prefix: str = "") -> dict: elif isinstance(value, list): flat[full_key] = value flat[f"{full_key}.count"] = len(value) - flat[f"{full_key}.csv"] = ", ".join(str(v) for v in value if not isinstance(v, dict)) + flat[f"{full_key}.csv"] = ", ".join( + str(v) for v in value if not isinstance(v, dict) + ) else: flat[full_key] = value return flat @@ -52,6 +93,7 @@ def flatten_config(config: dict, prefix: str = "") -> dict: def inject_config(content: str, flat_config: dict) -> str: """Replace {{config.key}} placeholders with config values.""" + def replacer(match): key = match.group(1).strip() value = flat_config.get(key, match.group(0)) # keep original if not found @@ -66,6 +108,7 @@ def inject_config(content: str, flat_config: dict) -> str: def check_conditionals(content: str, flat_config: dict) -> str: """Process {{#if key}}...{{/if}} and {{#unless key}}...{{/unless}} blocks.""" + # Handle {{#if key}}content{{/if}} def if_replacer(match): key = match.group(1).strip() @@ -75,7 +118,9 @@ def check_conditionals(content: str, flat_config: dict) -> str: return body return "" - content = re.sub(r"\{\{#if (.+?)\}\}(.*?)\{\{/if\}\}", if_replacer, content, flags=re.DOTALL) + content = re.sub( + r"\{\{#if (.+?)\}\}(.*?)\{\{/if\}\}", if_replacer, content, flags=re.DOTALL + ) # Handle {{#unless key}}content{{/unless}} def unless_replacer(match): @@ -86,7 +131,12 @@ def check_conditionals(content: str, flat_config: dict) -> str: return body return "" - content = re.sub(r"\{\{#unless (.+?)\}\}(.*?)\{\{/unless\}\}", unless_replacer, content, flags=re.DOTALL) + content = re.sub( + r"\{\{#unless (.+?)\}\}(.*?)\{\{/unless\}\}", + unless_replacer, + content, + flags=re.DOTALL, + ) return content @@ -118,7 +168,9 @@ def parse_persona_md(filepath: Path, flat_config: dict) -> dict: if line.startswith("## "): if current_section: sections[current_section] = "\n".join(current_content).strip() - current_section = line[3:].strip().lower().replace(" ", "_").replace("&", "and") + current_section = ( + line[3:].strip().lower().replace(" ", "_").replace("&", "and") + ) current_content = [] else: current_content.append(line) @@ -133,7 +185,14 @@ def parse_persona_md(filepath: Path, flat_config: dict) -> dict: } -def build_persona(persona_dir: Path, output_dir: Path, flat_config: dict, config: dict, escalation_graph: dict = None, skills_index: dict = None): +def build_persona( + persona_dir: Path, + output_dir: Path, + flat_config: dict, + config: dict, + escalation_graph: dict = None, + skills_index: dict = None, +): """Build all variants for a persona directory.""" md_files = sorted(persona_dir.glob("*.md")) if not md_files: @@ -168,14 +227,27 @@ def build_persona(persona_dir: Path, output_dir: Path, flat_config: dict, config continue # Build output object - output = {**meta, **parsed["metadata"], "variant": variant, "sections": parsed["sections"]} + output = { + **meta, + **parsed["metadata"], + "variant": variant, + "sections": parsed["sections"], + } # Inject config metadata if config: output["_config"] = { "user": config.get("user", {}).get("name", "unknown"), - "tools": {k: v for k, v in config.get("infrastructure", {}).get("tools", {}).items() if v is True}, - "frameworks": {k: v for k, v in config.get("frameworks", {}).items() if v is True}, + "tools": { + k: v + for k, v in config.get("infrastructure", {}) + .get("tools", {}) + .items() + if v is True + }, + "frameworks": { + k: v for k, v in config.get("frameworks", {}).items() if v is True + }, "regional_focus": config.get("regional_focus", {}), } @@ -207,13 +279,17 @@ def build_persona(persona_dir: Path, output_dir: Path, flat_config: dict, config # Write YAML yaml_out = out_path / f"{variant}.yaml" yaml_out.write_text( - yaml.dump(output, allow_unicode=True, default_flow_style=False, sort_keys=False), + yaml.dump( + output, allow_unicode=True, default_flow_style=False, sort_keys=False + ), encoding="utf-8", ) # Write JSON json_out = out_path / f"{variant}.json" - json_out.write_text(json.dumps(output, ensure_ascii=False, indent=2), encoding="utf-8") + json_out.write_text( + json.dumps(output, ensure_ascii=False, indent=2), encoding="utf-8" + ) # Write plain system prompt (just the body, no config metadata) prompt_out = out_path / f"{variant}.prompt.md" @@ -227,37 +303,192 @@ def build_persona(persona_dir: Path, output_dir: Path, flat_config: dict, config DEFAULT_SKILL_PERSONA_MAP = { # Cybersecurity skills → personas - "pentest": ["neo"], "nmap-recon": ["neo", "vortex"], "security-scanner": ["neo", "phantom"], - "sql-injection-testing": ["neo", "phantom"], "stealth-browser": ["neo", "oracle"], - "security-audit-toolkit": ["neo", "forge"], "pwnclaw-security-scan": ["neo"], - "senior-secops": ["bastion"], "clawsec": ["neo", "vortex"], - "pcap-analyzer": ["vortex", "bastion"], "sys-guard-linux-remediator": ["bastion"], - "ctf-writeup-generator": ["neo"], "dns-networking": ["vortex", "architect"], - "network-scanner": ["neo", "vortex"], "security-skill-scanner": ["neo"], - "pentest-active-directory": ["neo"], "pentest-api-attacker": ["neo", "phantom"], - "pentest-auth-bypass": ["neo", "phantom"], "pentest-c2-operator": ["neo", "sentinel"], + "pentest": ["neo"], + "nmap-recon": ["neo", "vortex"], + "security-scanner": ["neo", "phantom"], + "sql-injection-testing": ["neo", "phantom"], + "stealth-browser": ["neo", "oracle"], + "security-audit-toolkit": ["neo", "forge"], + "pwnclaw-security-scan": ["neo"], + "senior-secops": ["bastion"], + "clawsec": ["neo", "vortex"], + "pcap-analyzer": ["vortex", "bastion"], + "sys-guard-linux-remediator": ["bastion"], + "ctf-writeup-generator": ["neo"], + "dns-networking": ["vortex", "architect"], + "network-scanner": ["neo", "vortex"], + "security-skill-scanner": ["neo"], + "pentest-active-directory": ["neo"], + "pentest-api-attacker": ["neo", "phantom"], + "pentest-auth-bypass": ["neo", "phantom"], + "pentest-c2-operator": ["neo", "sentinel"], "gov-cybersecurity": ["sentinel", "bastion"], # Intelligence skills → personas - "osint-investigator": ["oracle"], "seithar-intel": ["sentinel", "frodo"], - "freshrss": ["frodo", "oracle"], "freshrss-reader": ["frodo", "oracle"], - "war-intel-monitor": ["frodo", "marshal"], "news-crawler": ["frodo", "herald"], - "dellight-intelligence-ops": ["frodo", "echo"], "dellight-strategic-intelligence": ["frodo"], - "agent-intelligence-network-scan": ["oracle"], "social-trust-manipulation-detector": ["ghost"], + "osint-investigator": ["oracle"], + "seithar-intel": ["sentinel", "frodo"], + "freshrss": ["frodo", "oracle"], + "freshrss-reader": ["frodo", "oracle"], + "war-intel-monitor": ["frodo", "marshal"], + "news-crawler": ["frodo", "herald"], + "dellight-intelligence-ops": ["frodo", "echo"], + "dellight-strategic-intelligence": ["frodo"], + "agent-intelligence-network-scan": ["oracle"], + "social-trust-manipulation-detector": ["ghost"], # Infrastructure skills → personas - "docker-essentials": ["architect"], "session-logs": ["architect"], + "docker-essentials": ["architect"], + "session-logs": ["architect"], # Document processing → personas - "image-ocr": ["oracle", "scribe"], "mistral-ocr": ["oracle", "scribe"], - "pdf-text-extractor": ["scribe", "scholar"], "youtube-transcript": ["herald", "scholar"], + "image-ocr": ["oracle", "scribe"], + "mistral-ocr": ["oracle", "scribe"], + "pdf-text-extractor": ["scribe", "scholar"], + "youtube-transcript": ["herald", "scholar"], # Web scraping → personas - "deep-scraper": ["oracle"], "crawl-for-ai": ["oracle", "herald"], + "deep-scraper": ["oracle"], + "crawl-for-ai": ["oracle", "herald"], } +VALID_PERSONAS = { + "arbiter", + "architect", + "bastion", + "centurion", + "chronos", + "cipher", + "corsair", + "echo", + "forge", + "frodo", + "gambit", + "ghost", + "herald", + "ledger", + "marshal", + "medic", + "neo", + "oracle", + "phantom", + "polyglot", + "sage", + "scholar", + "scribe", + "sentinel", + "specter", + "tribune", + "vortex", + "warden", + "wraith", +} + + +def parse_skill_frontmatter(skill_md: Path) -> dict: + """Parse YAML frontmatter from SKILL.md; return empty dict if absent/invalid.""" + content = skill_md.read_text(encoding="utf-8") + fm_match = re.match(r"^---\n(.*?)\n---\n", content, re.DOTALL) + if not fm_match: + return {} + parsed = yaml.safe_load(fm_match.group(1)) + return parsed if isinstance(parsed, dict) else {} + + +def infer_personas_from_skill_metadata(skill_name: str, metadata: dict) -> list: + """Infer likely persona mappings using skill frontmatter metadata and naming.""" + name = (skill_name or "").lower() + domain = str(metadata.get("domain", "")).lower() + subdomain = str(metadata.get("subdomain", "")).lower() + description = str(metadata.get("description", "")).lower() + tags = [str(t).lower() for t in metadata.get("tags", []) if t is not None] + blob = " ".join([name, domain, subdomain, description] + tags) + + personas = set() + + # Subdomain affinity + subdomain_map = { + "penetration-testing": ["neo", "phantom", "vortex"], + "application-security": ["phantom", "neo"], + "api-security": ["phantom", "neo"], + "web-security": ["phantom", "neo"], + "malware-analysis": ["specter", "bastion", "sentinel"], + "memory-forensics": ["specter", "bastion"], + "forensics": ["specter", "bastion"], + "threat-intelligence": ["sentinel", "frodo", "oracle"], + "incident-response": ["bastion", "sentinel", "medic"], + "soc-operations": ["bastion", "sentinel"], + "threat-hunting": ["sentinel", "bastion", "vortex"], + "network-security": ["vortex", "bastion"], + "network-forensics": ["vortex", "specter", "bastion"], + "cloud-security": ["architect", "bastion", "sentinel"], + "identity-security": ["cipher", "neo", "bastion"], + "active-directory": ["cipher", "neo", "bastion"], + "vulnerability-management": ["bastion", "forge"], + "compliance": ["ledger", "arbiter", "bastion"], + "ot-security": ["centurion", "bastion", "sentinel"], + } + personas.update(subdomain_map.get(subdomain, [])) + + # Keyword affinity fallback + keyword_map = { + "apt": ["sentinel", "frodo"], + "threat intel": ["sentinel", "oracle", "frodo"], + "ioc": ["sentinel", "bastion"], + "misp": ["sentinel", "oracle"], + "siem": ["bastion", "sentinel"], + "splunk": ["bastion", "sentinel"], + "soc": ["bastion", "sentinel"], + "incident response": ["bastion", "medic", "sentinel"], + "phishing": ["bastion", "oracle", "sentinel"], + "malware": ["specter", "bastion", "sentinel"], + "ransomware": ["specter", "bastion", "sentinel"], + "forensic": ["specter", "bastion"], + "volatility": ["specter", "bastion"], + "yara": ["specter", "bastion"], + "memory": ["specter", "bastion"], + "network": ["vortex", "bastion"], + "zeek": ["vortex", "bastion", "sentinel"], + "wireshark": ["vortex", "bastion"], + "nmap": ["neo", "vortex"], + "pentest": ["neo", "phantom", "vortex"], + "red team": ["neo", "phantom", "specter"], + "web": ["phantom", "neo"], + "xss": ["phantom", "neo"], + "sql injection": ["phantom", "neo"], + "api": ["phantom", "neo"], + "kubernetes": ["architect", "bastion", "sentinel"], + "docker": ["architect", "bastion"], + "aws": ["architect", "bastion", "sentinel"], + "azure": ["architect", "bastion", "sentinel"], + "gcp": ["architect", "bastion", "sentinel"], + "iam": ["cipher", "architect", "bastion"], + "active directory": ["cipher", "neo", "bastion"], + "kerberos": ["cipher", "neo", "bastion"], + "compliance": ["ledger", "arbiter", "bastion"], + "nist": ["ledger", "bastion", "sentinel"], + "ot": ["centurion", "bastion", "sentinel"], + "scada": ["centurion", "bastion", "sentinel"], + "ics": ["centurion", "bastion", "sentinel"], + } + for keyword, mapped_personas in keyword_map.items(): + if keyword in blob: + personas.update(mapped_personas) + + # Conservative fallback for unmapped cybersecurity skills + if not personas and "cyber" in domain: + personas.update(["bastion"]) + + # Keep only valid personas and deterministic order + return sorted([p for p in personas if p in VALID_PERSONAS]) + + def load_skill_persona_map(config: dict) -> dict: """Load skill→persona mapping from config.yaml or use defaults.""" custom = config.get("skill_persona_map", {}) - merged = dict(DEFAULT_SKILL_PERSONA_MAP) - merged.update(custom) + merged = { + k: [p for p in v if p in VALID_PERSONAS] + for k, v in DEFAULT_SKILL_PERSONA_MAP.items() + } + for skill, personas in custom.items(): + if isinstance(personas, list): + merged[skill] = [p for p in personas if p in VALID_PERSONAS] return merged @@ -289,7 +520,9 @@ def search_skills(shared_dir: Path, query: str): desc = "" for line in content.split("\n"): line = line.strip() - if line and not line.startswith(("---", "#", "name:", "description:")): + if line and not line.startswith( + ("---", "#", "name:", "description:") + ): desc = line[:100] break results.append((score, name, skills_subdir, desc)) @@ -297,7 +530,7 @@ def search_skills(shared_dir: Path, query: str): results.sort(key=lambda x: -x[0]) print(f"\n Search: '{query}' — {len(results)} results\n") for i, (score, name, source, desc) in enumerate(results[:20]): - print(f" {i+1:2}. [{score:3}] {name} ({source})") + print(f" {i + 1:2}. [{score:3}] {name} ({source})") if desc: print(f" {desc}") if len(results) > 20: @@ -351,20 +584,26 @@ def run_tests(personas_dir: Path, target: str = None): # Check must_include keywords exist in persona definition for keyword in expect.get("must_include", []): if keyword.lower() not in prompt_content: - warnings.append(f" {persona_name}/{test_name}: '{keyword}' not in persona prompt") + warnings.append( + f" {persona_name}/{test_name}: '{keyword}' not in persona prompt" + ) test_passed = False # Check escalation targets are defined if expect.get("escalation"): target_persona = expect["escalation"].lower() if target_persona not in prompt_content: - warnings.append(f" {persona_name}/{test_name}: escalation to '{target_persona}' not defined in boundaries") + warnings.append( + f" {persona_name}/{test_name}: escalation to '{target_persona}' not defined in boundaries" + ) test_passed = False # Check confidence language for intel personas if expect.get("confidence"): if "confidence" not in prompt_content and "high" not in prompt_content: - warnings.append(f" {persona_name}/{test_name}: confidence levels not defined in persona") + warnings.append( + f" {persona_name}/{test_name}: confidence levels not defined in persona" + ) test_passed = False if test_passed: @@ -384,9 +623,16 @@ def run_tests(personas_dir: Path, target: str = None): def build_skills_index(shared_dir: Path, config: dict = None) -> dict: - """Index all shared skills from _shared/skills/ and _shared/paperclip-skills/.""" + """Index all shared skills from _shared/{skills,paperclip-skills,community-skills}/.""" skill_map = load_skill_persona_map(config or {}) - index = {"skills": {}, "paperclip_skills": {}, "design_brands": [], "ui_ux_styles": 0, "_skill_persona_map": skill_map} + index = { + "skills": {}, + "paperclip_skills": {}, + "community_skills": {}, + "design_brands": [], + "ui_ux_styles": 0, + "_skill_persona_map": skill_map, + } # Index shared-skills skills_dir = shared_dir / "skills" @@ -396,16 +642,33 @@ def build_skills_index(shared_dir: Path, config: dict = None) -> dict: continue skill_md = skill_dir / "SKILL.md" if skill_md.exists(): + skill_meta = parse_skill_frontmatter(skill_md) + inferred_personas = infer_personas_from_skill_metadata( + skill_dir.name, skill_meta + ) + configured_personas = skill_map.get(skill_dir.name, []) + merged_personas = sorted( + set(configured_personas).union(inferred_personas) + ) content = skill_md.read_text(encoding="utf-8") first_line = "" for line in content.split("\n"): line = line.strip() - if line and not line.startswith(("---", "#", "name:", "description:")): + if line and not line.startswith( + ("---", "#", "name:", "description:") + ): first_line = line[:120] break index["skills"][skill_dir.name] = { - "personas": skill_map.get(skill_dir.name, []), + "personas": merged_personas, "summary": first_line, + "domain": str(skill_meta.get("domain", "")), + "subdomain": str(skill_meta.get("subdomain", "")), + "tags": skill_meta.get("tags", []), + "mapped_by": { + "explicit": configured_personas, + "inferred": inferred_personas, + }, "has_references": (skill_dir / "references").is_dir(), } @@ -419,10 +682,22 @@ def build_skills_index(shared_dir: Path, config: dict = None) -> dict: if skill_md.exists(): index["paperclip_skills"][skill_dir.name] = True + # Index community-skills + cskills_dir = shared_dir / "community-skills" + if cskills_dir.exists(): + for skill_dir in sorted(cskills_dir.iterdir()): + if not skill_dir.is_dir(): + continue + skill_md = skill_dir / "SKILL.md" + if skill_md.exists(): + index["community_skills"][skill_dir.name] = True + # Index design brands design_dir = shared_dir / "design-md" if design_dir.exists(): - index["design_brands"] = sorted([d.name for d in design_dir.iterdir() if d.is_dir()]) + index["design_brands"] = sorted( + [d.name for d in design_dir.iterdir() if d.is_dir()] + ) # Count UI/UX data uiux_dir = shared_dir / "ui-ux-pro-max" / "data" @@ -477,7 +752,9 @@ def validate_persona(persona_name: str, parsed: dict) -> list: if section not in parsed.get("sections", {}): warnings.append(f"Missing section: {section}") elif len(parsed["sections"][section].split()) < 30: - warnings.append(f"Thin section ({len(parsed['sections'][section].split())} words): {section}") + warnings.append( + f"Thin section ({len(parsed['sections'][section].split())} words): {section}" + ) fm = parsed.get("metadata", {}) for field in ["codename", "name", "domain", "address_to", "tone"]: @@ -487,7 +764,13 @@ def validate_persona(persona_name: str, parsed: dict) -> list: return warnings -def build_catalog(personas_dir: Path, output_dir: Path, config: dict, flat_config: dict): +def build_catalog( + personas_dir: Path, + output_dir: Path, + config: dict, + flat_config: dict, + shared_dir: Path | None, +): """Generate CATALOG.md with stats, escalation paths, and trigger index.""" addresses = config.get("persona_defaults", {}).get("custom_addresses", {}) @@ -515,7 +798,11 @@ def build_catalog(personas_dir: Path, output_dir: Path, config: dict, flat_confi meta = yaml.safe_load(meta_file.read_text(encoding="utf-8")) or {} codename = meta.get("codename", persona_dir.name) address = addresses.get(persona_dir.name, meta.get("address_to", "N/A")) - variants = [f.stem for f in sorted(persona_dir.glob("*.md")) if not f.name.startswith("_")] + variants = [ + f.stem + for f in sorted(persona_dir.glob("*.md")) + if not f.name.startswith("_") + ] # Parse general.md for stats general = persona_dir / "general.md" @@ -540,7 +827,9 @@ def build_catalog(personas_dir: Path, output_dir: Path, config: dict, flat_confi catalog_lines.append(f"- **Domain:** {meta.get('domain', 'N/A')}") catalog_lines.append(f"- **Hitap:** {address}") catalog_lines.append(f"- **Variants:** {', '.join(variants)}") - catalog_lines.append(f"- **Depth:** {word_count:,} words, {section_count} sections") + catalog_lines.append( + f"- **Depth:** {word_count:,} words, {section_count} sections" + ) if escalates_to: catalog_lines.append(f"- **Escalates to:** {', '.join(escalates_to)}") catalog_lines.append("") @@ -559,7 +848,9 @@ def build_catalog(personas_dir: Path, output_dir: Path, config: dict, flat_confi catalog_lines.append("## Build Statistics\n") catalog_lines.append(f"- Total prompt content: {total_words:,} words") catalog_lines.append(f"- Total sections: {total_sections}") - catalog_lines.append(f"- Escalation connections: {sum(len(v) for v in escalation_graph.values())}") + catalog_lines.append( + f"- Escalation connections: {sum(len(v) for v in escalation_graph.values())}" + ) catalog_lines.append(f"- Unique triggers: {len(trigger_index)}") catalog_lines.append("") @@ -580,13 +871,18 @@ def build_catalog(personas_dir: Path, output_dir: Path, config: dict, flat_confi print(f" Index: {index_path}/escalation_graph.json, trigger_index.json") # Write skills index if shared dir exists - shared_dir = personas_dir / "_shared" - if shared_dir.exists(): - si = build_skills_index(shared_dir) + if shared_dir and shared_dir.exists(): + si = build_skills_index(shared_dir, config) (index_path / "skills_index.json").write_text( json.dumps(si, indent=2, ensure_ascii=False), encoding="utf-8" ) - print(f" Skills: {len(si.get('skills', {}))} shared + {len(si.get('paperclip_skills', {}))} paperclip + {len(si.get('design_brands', []))} design brands + {si.get('ui_ux_styles', 0)} UI/UX data files") + print( + f" Skills: {len(si.get('skills', {}))} shared + " + f"{len(si.get('paperclip_skills', {}))} paperclip + " + f"{len(si.get('community_skills', {}))} community + " + f"{len(si.get('design_brands', []))} design brands + " + f"{si.get('ui_ux_styles', 0)} UI/UX data files" + ) # Print validation warnings if all_warnings: @@ -597,7 +893,9 @@ def build_catalog(personas_dir: Path, output_dir: Path, config: dict, flat_confi return total_words -def print_summary(config: dict, total_personas: int, total_variants: int, total_words: int = 0): +def print_summary( + config: dict, total_personas: int, total_variants: int, total_words: int = 0 +): """Print build summary with config status.""" print("\n" + "=" * 50) print(f"BUILD COMPLETE") @@ -609,8 +907,14 @@ def print_summary(config: dict, total_personas: int, total_variants: int, total_ if config: user = config.get("user", {}).get("name", "?") - tools_on = sum(1 for v in config.get("infrastructure", {}).get("tools", {}).values() if v is True) - frameworks_on = sum(1 for v in config.get("frameworks", {}).values() if v is True) + tools_on = sum( + 1 + for v in config.get("infrastructure", {}).get("tools", {}).values() + if v is True + ) + frameworks_on = sum( + 1 for v in config.get("frameworks", {}).values() if v is True + ) regions = config.get("regional_focus", {}).get("primary", []) print(f"\n Config: {user}") print(f" Tools: {tools_on} enabled") @@ -641,7 +945,11 @@ def install_claude(output_dir: Path): for prompt_file in persona_dir.glob("*.prompt.md"): variant = prompt_file.stem codename = persona_dir.name - cmd_name = f"persona-{codename}" if variant == "general" else f"persona-{codename}-{variant}" + cmd_name = ( + f"persona-{codename}" + if variant == "general" + else f"persona-{codename}-{variant}" + ) dest = commands_dir / f"{cmd_name}.md" content = prompt_file.read_text(encoding="utf-8") command_content = f"{content}\n\n---\nUser query: $ARGUMENTS\n" @@ -683,10 +991,24 @@ def install_claude(output_dir: Path): "name": codename, "description": f"{name} ({address_to}) — {role}. {domain}.", "instructions": instructions, - "allowedTools": ["Read(*)", "Edit(*)", "Write(*)", "Bash(*)", "Glob(*)", "Grep(*)", "WebFetch(*)", "WebSearch(*)"], + "allowedTools": [ + "Read(*)", + "Edit(*)", + "Write(*)", + "Bash(*)", + "Glob(*)", + "Grep(*)", + "WebFetch(*)", + "WebSearch(*)", + ], } agent_file = agents_dir / f"{codename}.yml" - agent_file.write_text(yaml.dump(agent, allow_unicode=True, default_flow_style=False, sort_keys=False), encoding="utf-8") + agent_file.write_text( + yaml.dump( + agent, allow_unicode=True, default_flow_style=False, sort_keys=False + ), + encoding="utf-8", + ) agent_count += 1 print(f" Claude: {cmd_count} commands + {agent_count} agents installed") @@ -730,10 +1052,13 @@ def install_gemini(output_dir: Path): gem = { "name": f"{name} — {variant}" if variant != "general" else name, "description": f"{data.get('role', '')} | {data.get('domain', '')}", - "system_instruction": data.get("sections", {}).get("soul", "") + "\n\n" + - data.get("sections", {}).get("expertise", "") + "\n\n" + - data.get("sections", {}).get("methodology", "") + "\n\n" + - data.get("sections", {}).get("behavior_rules", ""), + "system_instruction": data.get("sections", {}).get("soul", "") + + "\n\n" + + data.get("sections", {}).get("expertise", "") + + "\n\n" + + data.get("sections", {}).get("methodology", "") + + "\n\n" + + data.get("sections", {}).get("behavior_rules", ""), "metadata": { "codename": codename, "variant": variant, @@ -744,17 +1069,26 @@ def install_gemini(output_dir: Path): }, } dest = gems_dir / f"{codename}-{variant}.json" - dest.write_text(json.dumps(gem, ensure_ascii=False, indent=2), encoding="utf-8") + dest.write_text( + json.dumps(gem, ensure_ascii=False, indent=2), encoding="utf-8" + ) count += 1 print(f" Gemini: {count} gems generated to {gems_dir}") return count -def install_paperclip(output_dir: Path, personas_dir: Path): +def install_paperclip(output_dir: Path, personas_dir: Path, shared_dir: Path | None): """Install personas as Paperclip agents (SOUL.md + hermes-config.yaml + AGENTS.md per agent).""" pc_dir = output_dir / "_paperclip" agents_dir = pc_dir / "agents" skills_dir = pc_dir / "skills" + + # Recreate output for deterministic full migration. + if pc_dir.exists(): + import shutil + + shutil.rmtree(pc_dir) + agents_dir.mkdir(parents=True, exist_ok=True) skills_dir.mkdir(parents=True, exist_ok=True) @@ -860,11 +1194,13 @@ def install_paperclip(output_dir: Path, personas_dir: Path): agents_md_lines.append(f"- → {target}") agents_md_lines.append("") - (agent_dir / "AGENTS.md").write_text("\n".join(agents_md_lines), encoding="utf-8") + (agent_dir / "AGENTS.md").write_text( + "\n".join(agents_md_lines), encoding="utf-8" + ) agent_count += 1 # Copy shared skills as Paperclip skills (SKILL.md format already compatible) - shared_skills = personas_dir / "_shared" / "skills" + shared_skills = shared_dir / "skills" if shared_dir else Path("__missing__") if shared_skills.exists(): for skill_dir in sorted(shared_skills.iterdir()): if not skill_dir.is_dir(): @@ -873,15 +1209,18 @@ def install_paperclip(output_dir: Path, personas_dir: Path): if skill_md.exists(): dest = skills_dir / skill_dir.name dest.mkdir(parents=True, exist_ok=True) - (dest / "SKILL.md").write_text(skill_md.read_text(encoding="utf-8"), encoding="utf-8") + (dest / "SKILL.md").write_text( + skill_md.read_text(encoding="utf-8"), encoding="utf-8" + ) refs = skill_dir / "references" if refs.is_dir(): import shutil + shutil.copytree(refs, dest / "references", dirs_exist_ok=True) skill_count += 1 # Copy paperclip-specific skills - pc_skills = personas_dir / "_shared" / "paperclip-skills" + pc_skills = shared_dir / "paperclip-skills" if shared_dir else Path("__missing__") if pc_skills.exists(): for skill_dir in sorted(pc_skills.iterdir()): if not skill_dir.is_dir(): @@ -890,25 +1229,54 @@ def install_paperclip(output_dir: Path, personas_dir: Path): if skill_md.exists() and not (skills_dir / skill_dir.name).exists(): dest = skills_dir / skill_dir.name dest.mkdir(parents=True, exist_ok=True) - (dest / "SKILL.md").write_text(skill_md.read_text(encoding="utf-8"), encoding="utf-8") + (dest / "SKILL.md").write_text( + skill_md.read_text(encoding="utf-8"), encoding="utf-8" + ) refs = skill_dir / "references" if refs.is_dir(): import shutil + shutil.copytree(refs, dest / "references", dirs_exist_ok=True) scripts = skill_dir / "scripts" if scripts.is_dir(): import shutil + shutil.copytree(scripts, dest / "scripts", dirs_exist_ok=True) skill_count += 1 # Deploy original Paperclip company agents from _shared/paperclip-agents/ - pc_agents_src = personas_dir / "_shared" / "paperclip-agents" + pc_agents_src = ( + shared_dir / "paperclip-agents" if shared_dir else Path("__missing__") + ) pc_agent_count = 0 + + def normalize_agent_name(name: str) -> str: + """Normalize escaped/unicode-heavy names to stable ASCII directory names.""" + decoded = re.sub( + r"#U([0-9A-Fa-f]{4})", + lambda m: chr(int(m.group(1), 16)), + name, + ) + ascii_name = ( + unicodedata.normalize("NFKD", decoded) + .encode("ascii", "ignore") + .decode("ascii") + ) + # Keep names filesystem-safe and deterministic. + slug = re.sub(r"[^a-zA-Z0-9]+", "-", ascii_name).strip("-").lower() + return slug or decoded + if pc_agents_src.exists(): + seen_company_agents = set() + collision_count = 0 for agent_src in sorted(pc_agents_src.iterdir()): if not agent_src.is_dir(): continue - agent_name = agent_src.name + agent_name = normalize_agent_name(agent_src.name) + if agent_name in seen_company_agents: + collision_count += 1 + continue + seen_company_agents.add(agent_name) # Skip if persona-based agent already exists with same name if (agents_dir / agent_name).exists(): continue @@ -916,11 +1284,19 @@ def install_paperclip(output_dir: Path, personas_dir: Path): dest.mkdir(parents=True, exist_ok=True) for f in agent_src.iterdir(): if f.is_file(): - (dest / f.name).write_text(f.read_text(encoding="utf-8"), encoding="utf-8") + (dest / f.name).write_text( + f.read_text(encoding="utf-8"), encoding="utf-8" + ) pc_agent_count += 1 + if collision_count: + print( + f" Note: skipped {collision_count} duplicate company agent source dirs after name normalization" + ) total_agents = agent_count + pc_agent_count - print(f" Paperclip: {agent_count} persona agents + {pc_agent_count} company agents + {skill_count} skills to {pc_dir}") + print( + f" Paperclip: {agent_count} persona agents + {pc_agent_count} company agents + {skill_count} skills to {pc_dir}" + ) return total_agents @@ -955,13 +1331,28 @@ def install_openclaw(output_dir: Path): def main(): import argparse - parser = argparse.ArgumentParser(description="Build persona library and optionally install to platforms.") - parser.add_argument("--install", choices=["claude", "antigravity", "gemini", "openclaw", "paperclip", "all"], - help="Install generated personas to a target platform") - parser.add_argument("--search", type=str, metavar="QUERY", - help="Search across all shared skills (e.g. --search 'pentest AD')") - parser.add_argument("--test", nargs="?", const="__all__", metavar="PERSONA", - help="Run persona test suite (optionally specify persona name)") + + parser = argparse.ArgumentParser( + description="Build persona library and optionally install to platforms." + ) + parser.add_argument( + "--install", + choices=["claude", "antigravity", "gemini", "openclaw", "paperclip", "all"], + help="Install generated personas to a target platform", + ) + parser.add_argument( + "--search", + type=str, + metavar="QUERY", + help="Search across all shared skills (e.g. --search 'pentest AD')", + ) + parser.add_argument( + "--test", + nargs="?", + const="__all__", + metavar="PERSONA", + help="Run persona test suite (optionally specify persona name)", + ) args = parser.parse_args() root = Path(__file__).parent @@ -979,17 +1370,28 @@ def main(): # Find all persona directories persona_dirs = [ - d for d in sorted(personas_dir.iterdir()) if d.is_dir() and not d.name.startswith((".", "_")) + d + for d in sorted(personas_dir.iterdir()) + if d.is_dir() and not d.name.startswith((".", "_")) ] if not persona_dirs: print("No persona directories found.") sys.exit(1) - shared_dir = personas_dir / "_shared" + shared_dir = resolve_shared_dir(root, personas_dir) + source_mirrors = discover_sources(root) + + if source_mirrors: + print(f"Detected source mirrors: {', '.join(source_mirrors)}") + else: + print("Detected source mirrors: none") # Handle search-only mode if args.search: + if not shared_dir: + print("No shared skill library found.") + return search_skills(shared_dir, args.search) return @@ -1004,18 +1406,26 @@ def main(): # Pre-build escalation graph and skills index escalation_graph = build_escalation_graph(personas_dir, flat_config) - skills_index = build_skills_index(shared_dir, config) if shared_dir.exists() else {} + skills_index = build_skills_index(shared_dir, config) if shared_dir else {} total_variants = 0 for pdir in persona_dirs: - total_variants += build_persona(pdir, output_dir, flat_config, config, escalation_graph, skills_index) + total_variants += build_persona( + pdir, output_dir, flat_config, config, escalation_graph, skills_index + ) - total_words = build_catalog(personas_dir, output_dir, config, flat_config) + total_words = build_catalog( + personas_dir, output_dir, config, flat_config, shared_dir + ) # Platform installation if args.install: print(f"\n--- Installing to: {args.install} ---\n") - targets = ["claude", "antigravity", "gemini", "openclaw", "paperclip"] if args.install == "all" else [args.install] + targets = ( + ["claude", "antigravity", "gemini", "openclaw", "paperclip"] + if args.install == "all" + else [args.install] + ) for target in targets: if target == "claude": install_claude(output_dir) @@ -1026,7 +1436,7 @@ def main(): elif target == "openclaw": install_openclaw(output_dir) elif target == "paperclip": - install_paperclip(output_dir, personas_dir) + install_paperclip(output_dir, personas_dir, shared_dir) print_summary(config, len(persona_dirs), total_variants, total_words) diff --git a/personas/_shared/anthropic-cybersecurity-skills/.claude-plugin/marketplace.json b/personas/_shared/anthropic-cybersecurity-skills/.claude-plugin/marketplace.json new file mode 100644 index 0000000..58cd50e --- /dev/null +++ b/personas/_shared/anthropic-cybersecurity-skills/.claude-plugin/marketplace.json @@ -0,0 +1,37 @@ +{ + "name": "anthropic-cybersecurity-skills", + "owner": { + "name": "mukul975", + "email": "mukuljangra5@gmail.com" + }, + "metadata": { + "description": "754 cybersecurity skills for AI agents mapped to 5 frameworks: MITRE ATT&CK, NIST CSF 2.0, MITRE ATLAS, D3FEND, and NIST AI RMF.", + "version": "1.2.0" + }, + "plugins": [ + { + "name": "cybersecurity-skills", + "source": "./", + "description": "754 cybersecurity skills covering web security, pentesting, DFIR, threat intelligence, cloud security, malware analysis, and more. Mapped to 5 frameworks.", + "version": "1.2.0", + "author": { + "name": "mukul975" + }, + "license": "Apache-2.0", + "keywords": [ + "cybersecurity", + "pentesting", + "forensics", + "threat-intelligence", + "cloud-security", + "malware-analysis", + "incident-response", + "zero-trust", + "devsecops" + ], + "category": "security", + "homepage": "https://github.com/mukul975/Anthropic-Cybersecurity-Skills", + "repository": "https://github.com/mukul975/Anthropic-Cybersecurity-Skills" + } + ] +} \ No newline at end of file diff --git a/personas/_shared/anthropic-cybersecurity-skills/.claude-plugin/plugin.json b/personas/_shared/anthropic-cybersecurity-skills/.claude-plugin/plugin.json new file mode 100644 index 0000000..ff4afc1 --- /dev/null +++ b/personas/_shared/anthropic-cybersecurity-skills/.claude-plugin/plugin.json @@ -0,0 +1,5 @@ +{ + "name": "cybersecurity-skills", + "description": "753 cybersecurity skills covering web security, pentesting, DFIR, threat intelligence, cloud security, malware analysis, and more.", + "version": "1.0.0" +} diff --git a/personas/_shared/anthropic-cybersecurity-skills/.github/FUNDING.yml b/personas/_shared/anthropic-cybersecurity-skills/.github/FUNDING.yml new file mode 100644 index 0000000..1c344b7 --- /dev/null +++ b/personas/_shared/anthropic-cybersecurity-skills/.github/FUNDING.yml @@ -0,0 +1,2 @@ +github: mukul975 +custom: ["https://paypal.me/mahipaljangra"] diff --git a/personas/_shared/anthropic-cybersecurity-skills/.github/ISSUE_TEMPLATE/bug-report.yml b/personas/_shared/anthropic-cybersecurity-skills/.github/ISSUE_TEMPLATE/bug-report.yml new file mode 100644 index 0000000..ae36bd5 --- /dev/null +++ b/personas/_shared/anthropic-cybersecurity-skills/.github/ISSUE_TEMPLATE/bug-report.yml @@ -0,0 +1,50 @@ +name: Bug Report +description: Report a SKILL.md validation error, broken script, or incorrect content +title: "[Bug]: " +labels: ["bug", "needs-triage"] +body: + - type: markdown + attributes: + value: | + Thanks for taking the time to report a bug! + - type: input + id: skill-name + attributes: + label: Skill Name + description: Which skill has the issue? + placeholder: e.g., analyzing-disk-image-with-autopsy + validations: + required: true + - type: dropdown + id: bug-type + attributes: + label: Bug Type + options: + - SKILL.md validation error + - Broken/incorrect script + - Wrong instructions or commands + - Missing required files + - Incorrect metadata/frontmatter + - Other + validations: + required: true + - type: textarea + id: description + attributes: + label: Description + description: What is the issue? + validations: + required: true + - type: textarea + id: expected + attributes: + label: Expected Behavior + description: What should happen instead? + validations: + required: true + - type: input + id: ai-agent + attributes: + label: AI Agent Used + description: Which AI agent were you using? + placeholder: e.g., Claude Code, GitHub Copilot, Codex CLI diff --git a/personas/_shared/anthropic-cybersecurity-skills/.github/ISSUE_TEMPLATE/config.yml b/personas/_shared/anthropic-cybersecurity-skills/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..d2b10ec --- /dev/null +++ b/personas/_shared/anthropic-cybersecurity-skills/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,8 @@ +blank_issues_enabled: false +contact_links: + - name: Security Vulnerability + url: https://github.com/mukul975/Anthropic-Cybersecurity-Skills/security/advisories/new + about: Report a security vulnerability in this repository + - name: Discussion + url: https://github.com/mukul975/Anthropic-Cybersecurity-Skills/discussions + about: Ask questions or discuss ideas diff --git a/personas/_shared/anthropic-cybersecurity-skills/.github/ISSUE_TEMPLATE/improve-skill.md b/personas/_shared/anthropic-cybersecurity-skills/.github/ISSUE_TEMPLATE/improve-skill.md new file mode 100644 index 0000000..14de01f --- /dev/null +++ b/personas/_shared/anthropic-cybersecurity-skills/.github/ISSUE_TEMPLATE/improve-skill.md @@ -0,0 +1,19 @@ +--- +name: Improve existing skill +about: Suggest improvements to an existing skill +title: '[IMPROVE] skill-name-here' +labels: 'enhancement' +assignees: '' +--- + +## Skill to improve + + +## What needs improvement? +- [ ] agent.py has errors or placeholders +- [ ] api-reference.md is incomplete +- [ ] SKILL.md frontmatter is missing fields +- [ ] ATT&CK mapping is incorrect +- [ ] Other: + +## Suggested improvement diff --git a/personas/_shared/anthropic-cybersecurity-skills/.github/ISSUE_TEMPLATE/new-skill-request.yml b/personas/_shared/anthropic-cybersecurity-skills/.github/ISSUE_TEMPLATE/new-skill-request.yml new file mode 100644 index 0000000..ac19d3f --- /dev/null +++ b/personas/_shared/anthropic-cybersecurity-skills/.github/ISSUE_TEMPLATE/new-skill-request.yml @@ -0,0 +1,58 @@ +name: New Skill Request +description: Request a new cybersecurity skill to be added to the database +title: "[Skill Request]: " +labels: ["enhancement", "new-skill", "help wanted"] +body: + - type: markdown + attributes: + value: | + Request a new cybersecurity skill. The more detail you provide, the faster we can add it! + - type: input + id: skill-name + attributes: + label: Proposed Skill Name + description: Kebab-case gerund form (e.g., analyzing-memory-dump-with-volatility) + placeholder: performing-task-name + validations: + required: true + - type: dropdown + id: category + attributes: + label: Category + options: + - Threat Detection + - Incident Response + - Penetration Testing + - Digital Forensics + - Compliance & Governance + - Network Security + - Cloud Security + - Application Security + - Malware Analysis + - OSINT + - Zero Trust Architecture + - OT/ICS Security + - DevSecOps + - Ransomware Defense + - Threat Intelligence + - Other + validations: + required: true + - type: textarea + id: description + attributes: + label: Skill Description + description: What should this skill teach an AI agent to do? + validations: + required: true + - type: input + id: mitre-attack + attributes: + label: MITRE ATT&CK Technique(s) + description: Optional - relevant technique IDs + placeholder: e.g., T1059, T1078 + - type: textarea + id: tools + attributes: + label: Key Tools + description: What tools/commands should this skill cover? diff --git a/personas/_shared/anthropic-cybersecurity-skills/.github/ISSUE_TEMPLATE/new-skill.md b/personas/_shared/anthropic-cybersecurity-skills/.github/ISSUE_TEMPLATE/new-skill.md new file mode 100644 index 0000000..33d3130 --- /dev/null +++ b/personas/_shared/anthropic-cybersecurity-skills/.github/ISSUE_TEMPLATE/new-skill.md @@ -0,0 +1,25 @@ +--- +name: Add new skill +about: Propose a new cybersecurity skill for the database +title: '[NEW SKILL] skill-name-here' +labels: 'new-skill, good first issue' +assignees: '' +--- + +## Skill name (kebab-case) + + +## Domain / Subdomain + + +## Description + + +## MITRE ATT&CK techniques + + +## NIST CSF function + + +## Why is this skill needed? + diff --git a/personas/_shared/anthropic-cybersecurity-skills/.github/ISSUE_TEMPLATE/skill-improvement.yml b/personas/_shared/anthropic-cybersecurity-skills/.github/ISSUE_TEMPLATE/skill-improvement.yml new file mode 100644 index 0000000..96afb5f --- /dev/null +++ b/personas/_shared/anthropic-cybersecurity-skills/.github/ISSUE_TEMPLATE/skill-improvement.yml @@ -0,0 +1,41 @@ +name: Skill Improvement +description: Suggest improvements to an existing skill +title: "[Improvement]: " +labels: ["enhancement", "skill-improvement"] +body: + - type: input + id: skill-name + attributes: + label: Skill Name + placeholder: e.g., analyzing-network-traffic-with-wireshark + validations: + required: true + - type: dropdown + id: improvement-type + attributes: + label: Type of Improvement + options: + - More accurate/updated instructions + - Better workflow steps + - Add missing tools or commands + - Improve description for agent discovery + - Add MITRE ATT&CK mapping + - Add NIST CSF alignment + - Improve scripts/assets + - Fix outdated content + validations: + required: true + - type: textarea + id: current-issue + attributes: + label: Current Issue + description: What is wrong or missing? + validations: + required: true + - type: textarea + id: suggested-improvement + attributes: + label: Suggested Improvement + description: What should be changed or added? + validations: + required: true diff --git a/personas/_shared/anthropic-cybersecurity-skills/.github/workflows/sync-marketplace-version.yml b/personas/_shared/anthropic-cybersecurity-skills/.github/workflows/sync-marketplace-version.yml new file mode 100644 index 0000000..281a9ff --- /dev/null +++ b/personas/_shared/anthropic-cybersecurity-skills/.github/workflows/sync-marketplace-version.yml @@ -0,0 +1,39 @@ +name: Sync Marketplace Version on Release + +on: + release: + types: [published] + +jobs: + sync-version: + runs-on: ubuntu-latest + permissions: + contents: write + + steps: + - uses: actions/checkout@v4 + with: + token: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract version from tag + id: version + run: | + VERSION=${GITHUB_REF_NAME#v} + echo "version=$VERSION" >> $GITHUB_OUTPUT + echo "tag=$GITHUB_REF_NAME" >> $GITHUB_OUTPUT + + - name: Update marketplace.json version + env: + VERSION: ${{ steps.version.outputs.version }} + run: | + jq --arg v "$VERSION" '.metadata.version = $v | .plugins[].version = $v' .claude-plugin/marketplace.json > tmp.json + mv tmp.json .claude-plugin/marketplace.json + echo "Updated marketplace.json to version $VERSION" + + - name: Commit and push + run: | + git config user.name "mukul975" + git config user.email "mukuljangra5@gmail.com" + git add .claude-plugin/marketplace.json + git diff --staged --quiet || git commit -m "chore: bump marketplace version to ${{ steps.version.outputs.tag }}" + git push diff --git a/personas/_shared/anthropic-cybersecurity-skills/.github/workflows/update-index.yml b/personas/_shared/anthropic-cybersecurity-skills/.github/workflows/update-index.yml new file mode 100644 index 0000000..2910b02 --- /dev/null +++ b/personas/_shared/anthropic-cybersecurity-skills/.github/workflows/update-index.yml @@ -0,0 +1,70 @@ +name: Update marketplace index + +on: + push: + branches: [main] + paths: + - 'skills/**' + - '.github/workflows/update-index.yml' + workflow_dispatch: + +jobs: + update-index: + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - uses: actions/checkout@v4 + with: + token: ${{ secrets.GITHUB_TOKEN }} + + - name: Regenerate index.json + run: | + python3 << 'EOF' + import os, json, re + from datetime import datetime, timezone + + skills_dir = "skills" + skills = [] + + for skill_name in sorted(os.listdir(skills_dir)): + skill_md = os.path.join(skills_dir, skill_name, "SKILL.md") + if not os.path.isfile(skill_md): + continue + with open(skill_md, "r", encoding="utf-8") as f: + content = f.read() + fm_match = re.match(r"^---\n(.*?)\n---", content, re.DOTALL) + description = "" + if fm_match: + m = re.search(r"^description:\s*(.+)$", fm_match.group(1), re.MULTILINE) + if m: + description = m.group(1).strip().strip('"') + skills.append({ + "name": skill_name, + "description": description, + "domain": "cybersecurity", + "path": f"skills/{skill_name}" + }) + + index = { + "version": "1.1.0", + "generated_at": datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ"), + "repository": "https://github.com/mukul975/Anthropic-Cybersecurity-Skills", + "domain": "cybersecurity", + "total_skills": len(skills), + "skills": skills + } + + with open("index.json", "w", encoding="utf-8") as f: + json.dump(index, f, separators=(',', ':')) + + print(f"Updated index.json: {len(skills)} skills") + EOF + + - name: Commit updated index + run: | + git config user.name "mukul975" + git config user.email "mukuljangra5@gmail.com" + git add index.json + git diff --staged --quiet || git commit -m "chore: auto-update index.json" + git push diff --git a/personas/_shared/anthropic-cybersecurity-skills/.github/workflows/validate-skills.yml b/personas/_shared/anthropic-cybersecurity-skills/.github/workflows/validate-skills.yml new file mode 100644 index 0000000..5497aef --- /dev/null +++ b/personas/_shared/anthropic-cybersecurity-skills/.github/workflows/validate-skills.yml @@ -0,0 +1,128 @@ +name: Validate SKILL.md files + +on: + push: + paths: + - 'skills/**' + pull_request: + paths: + - 'skills/**' + +jobs: + validate: + runs-on: ubuntu-latest + name: Validate SKILL.md frontmatter + steps: + - uses: actions/checkout@v4 + + - name: Validate SKILL.md frontmatter with Python + run: | + python3 << 'EOF' + import os + import re + import sys + + REQUIRED_FIELDS = ['name', 'description', 'domain', 'subdomain', 'tags', 'version', 'author', 'license'] + errors = [] + checked = 0 + + for root, dirs, files in os.walk('skills'): + for file in files: + if file == 'SKILL.md': + path = os.path.join(root, file) + checked += 1 + with open(path, 'r', encoding='utf-8') as f: + content = f.read() + + # Check frontmatter exists + fm_match = re.match(r'^---\n(.*?)\n---', content, re.DOTALL) + if not fm_match: + errors.append(f"{path}: Missing YAML frontmatter") + continue + + fm = fm_match.group(1) + + # Check required fields + for field in REQUIRED_FIELDS: + if not re.search(rf'^{field}:', fm, re.MULTILINE): + errors.append(f"{path}: Missing required field '{field}'") + + # Check name format (kebab-case) + name_match = re.search(r'^name:\s*(.+)$', fm, re.MULTILINE) + if name_match: + name = name_match.group(1).strip().strip('"') + if not re.match(r'^[a-z0-9-]+$', name): + errors.append(f"{path}: Name '{name}' must be kebab-case") + if len(name) > 64: + errors.append(f"{path}: Name '{name}' exceeds 64 characters") + + print(f"Checked {checked} SKILL.md files") + + if errors: + print(f"\n{len(errors)} validation error(s):") + for e in errors: + print(f" ❌ {e}") + sys.exit(1) + else: + print(f"✅ All {checked} skills valid") + EOF + + - name: Check for duplicate skill names + run: | + python3 << 'EOF' + import os + import re + from collections import Counter + + names = [] + for root, dirs, files in os.walk('skills'): + for file in files: + if file == 'SKILL.md': + path = os.path.join(root, file) + with open(path, 'r', encoding='utf-8') as f: + content = f.read() + fm_match = re.match(r'^---\n(.*?)\n---', content, re.DOTALL) + if fm_match: + name_match = re.search(r'^name:\s*(.+)$', fm_match.group(1), re.MULTILINE) + if name_match: + names.append(name_match.group(1).strip().strip('"')) + + duplicates = [name for name, count in Counter(names).items() if count > 1] + if duplicates: + print(f"❌ Duplicate skill names found: {duplicates}") + exit(1) + print(f"✅ No duplicate names in {len(names)} skills") + EOF + + - name: Report skill counts + if: always() + run: | + echo "## Skill Database Stats" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + python3 << 'EOF' + import os + import re + from collections import Counter + + subdomain_counts = Counter() + total = 0 + for root, dirs, files in os.walk('skills'): + for file in files: + if file == 'SKILL.md': + total += 1 + path = os.path.join(root, file) + with open(path, 'r', encoding='utf-8') as f: + content = f.read() + fm_match = re.match(r'^---\n(.*?)\n---', content, re.DOTALL) + if fm_match: + sd_match = re.search(r'^subdomain:\s*(.+)$', fm_match.group(1), re.MULTILINE) + if sd_match: + subdomain_counts[sd_match.group(1).strip()] += 1 + + print(f"**Total Skills: {total}**") + print("") + print("| Subdomain | Count |") + print("|-----------|-------|") + for sd, count in sorted(subdomain_counts.items(), key=lambda x: -x[1]): + print(f"| {sd} | {count} |") + EOF diff --git a/personas/_shared/anthropic-cybersecurity-skills/ATTACK_COVERAGE.md b/personas/_shared/anthropic-cybersecurity-skills/ATTACK_COVERAGE.md new file mode 100644 index 0000000..4fcc2bd --- /dev/null +++ b/personas/_shared/anthropic-cybersecurity-skills/ATTACK_COVERAGE.md @@ -0,0 +1,509 @@ +# MITRE ATT&CK Coverage Map + +

+ MITRE ATT&CK + Techniques + Tactics +

+ +This document maps all **291 unique MITRE ATT&CK techniques** (across **149 parent techniques**) referenced in our **753+ cybersecurity skills** to the 14 Enterprise ATT&CK tactics. Use this to identify coverage gaps, plan detection engineering priorities, or validate your security program against the ATT&CK framework. + +> **How to read this:** Each technique links to its official ATT&CK page. Skills listed under each technique are the ones in this repository that teach detection, hunting, exploitation, or response for that technique. + +--- + +## Coverage Summary + +| Tactic | Techniques | Coverage | +|:-------|:---------:|:---------| +| 🔎 **Reconnaissance** | **12** | `████████████░░░░░░░░░░░░░░░░░░` | +| 🏗️ **Resource Development** | **7** | `███████░░░░░░░░░░░░░░░░░░░░░░░` | +| 🚪 **Initial Access** | **18** | `██████████████████░░░░░░░░░░░░` | +| ⚡ **Execution** | **18** | `██████████████████░░░░░░░░░░░░` | +| 🔩 **Persistence** | **36** | `██████████████████████████████` | +| ⬆️ **Privilege Escalation** | **11** | `███████████░░░░░░░░░░░░░░░░░░░` | +| 🥷 **Defense Evasion** | **48** | `██████████████████████████████` | +| 🔑 **Credential Access** | **27** | `███████████████████████████░░░` | +| 🗺️ **Discovery** | **20** | `████████████████████░░░░░░░░░░` | +| ↔️ **Lateral Movement** | **9** | `█████████░░░░░░░░░░░░░░░░░░░░░` | +| 📦 **Collection** | **13** | `█████████████░░░░░░░░░░░░░░░░░` | +| 📡 **Command and Control** | **20** | `████████████████████░░░░░░░░░░` | +| 📤 **Exfiltration** | **12** | `████████████░░░░░░░░░░░░░░░░░░` | +| 💥 **Impact** | **6** | `██████░░░░░░░░░░░░░░░░░░░░░░░░` | +| 🔧 **Other/Cross-tactic** | **34** | | +| | **291** | **Total unique techniques** | + +--- + +## 🔎 Reconnaissance + +**12 techniques covered** + +| Technique | Skills | +|:----------|:-------| +| [T1589](https://attack.mitre.org/techniques/T1589/) | `conducting-full-scope-red-team-engagement`, `conducting-social-engineering-pretext-call`, `performing-open-source-intelligence-gathering` | +| [T1590](https://attack.mitre.org/techniques/T1590/) | `performing-open-source-intelligence-gathering` | +| [T1591](https://attack.mitre.org/techniques/T1591/) | `collecting-open-source-intelligence`, `conducting-social-engineering-pretext-call`, `performing-open-source-intelligence-gathering` | +| [T1592](https://attack.mitre.org/techniques/T1592/) | `performing-open-source-intelligence-gathering` | +| [T1593](https://attack.mitre.org/techniques/T1593/) | `conducting-full-scope-red-team-engagement`, `performing-open-source-intelligence-gathering` | +| [T1594](https://attack.mitre.org/techniques/T1594/) | `performing-open-source-intelligence-gathering` | +| [T1595](https://attack.mitre.org/techniques/T1595/) | `executing-red-team-engagement-planning`, `triaging-security-incident` | +| [T1595.001](https://attack.mitre.org/techniques/T1595/001/) | `performing-open-source-intelligence-gathering` | +| [T1595.002](https://attack.mitre.org/techniques/T1595/002/) | `performing-open-source-intelligence-gathering` | +| [T1596](https://attack.mitre.org/techniques/T1596/) | `performing-open-source-intelligence-gathering` | +| [T1598](https://attack.mitre.org/techniques/T1598/) | `conducting-social-engineering-pretext-call` | +| [T1598.003](https://attack.mitre.org/techniques/T1598/003/) | `conducting-social-engineering-pretext-call`, `conducting-spearphishing-simulation-campaign` | + +--- + +## 🏗️ Resource Development + +**7 techniques covered** + +| Technique | Skills | +|:----------|:-------| +| [T1583.001](https://attack.mitre.org/techniques/T1583/001/) | `building-red-team-c2-infrastructure-with-havoc`, `conducting-full-scope-red-team-engagement`, `conducting-spearphishing-simulation-campaign`, `implementing-mitre-attack-coverage-mapping` | +| [T1583.003](https://attack.mitre.org/techniques/T1583/003/) | `building-red-team-c2-infrastructure-with-havoc` | +| [T1584.001](https://attack.mitre.org/techniques/T1584/001/) | `hunting-for-dns-based-persistence` | +| [T1585.002](https://attack.mitre.org/techniques/T1585/002/) | `conducting-spearphishing-simulation-campaign` | +| [T1587.001](https://attack.mitre.org/techniques/T1587/001/) | `building-red-team-c2-infrastructure-with-havoc`, `conducting-full-scope-red-team-engagement` | +| [T1608.001](https://attack.mitre.org/techniques/T1608/001/) | `conducting-spearphishing-simulation-campaign` | +| [T1608.005](https://attack.mitre.org/techniques/T1608/005/) | `conducting-spearphishing-simulation-campaign` | + +--- + +## 🚪 Initial Access + +**18 techniques covered** + +| Technique | Skills | +|:----------|:-------| +| [T1078](https://attack.mitre.org/techniques/T1078/) | `analyzing-apt-group-with-mitre-navigator`, `analyzing-powershell-script-block-logging`, `analyzing-windows-event-logs-in-splunk`, `building-threat-hunt-hypothesis-framework`, `conducting-full-scope-red-team-engagement` +13 more | +| [T1078.001](https://attack.mitre.org/techniques/T1078/001/) | `detecting-service-account-abuse` | +| [T1078.002](https://attack.mitre.org/techniques/T1078/002/) | `conducting-domain-persistence-with-dcsync`, `detecting-service-account-abuse`, `exploiting-active-directory-certificate-services-esc1`, `exploiting-constrained-delegation-abuse`, `exploiting-nopac-cve-2021-42278-42287` +1 more | +| [T1078.003](https://attack.mitre.org/techniques/T1078/003/) | `performing-privilege-escalation-assessment` | +| [T1078.004](https://attack.mitre.org/techniques/T1078/004/) | `detecting-azure-lateral-movement`, `detecting-azure-service-principal-abuse`, `implementing-mitre-attack-coverage-mapping`, `implementing-threat-modeling-with-mitre-attack` | +| [T1091](https://attack.mitre.org/techniques/T1091/) | `executing-red-team-engagement-planning`, `performing-physical-intrusion-assessment` | +| [T1133](https://attack.mitre.org/techniques/T1133/) | `executing-red-team-engagement-planning`, `performing-threat-landscape-assessment-for-sector` | +| [T1190](https://attack.mitre.org/techniques/T1190/) | `conducting-full-scope-red-team-engagement`, `executing-red-team-engagement-planning`, `exploiting-ms17-010-eternalblue-vulnerability`, `hunting-for-webshell-activity`, `performing-threat-landscape-assessment-for-sector` +1 more | +| [T1195](https://attack.mitre.org/techniques/T1195/) | `analyzing-supply-chain-malware-artifacts`, `performing-threat-landscape-assessment-for-sector` | +| [T1195.001](https://attack.mitre.org/techniques/T1195/001/) | `hunting-for-supply-chain-compromise` | +| [T1195.002](https://attack.mitre.org/techniques/T1195/002/) | `hunting-for-supply-chain-compromise` | +| [T1199](https://attack.mitre.org/techniques/T1199/) | `hunting-for-supply-chain-compromise`, `performing-physical-intrusion-assessment` | +| [T1200](https://attack.mitre.org/techniques/T1200/) | `executing-red-team-engagement-planning`, `performing-physical-intrusion-assessment` | +| [T1566](https://attack.mitre.org/techniques/T1566/) | `analyzing-apt-group-with-mitre-navigator`, `analyzing-threat-actor-ttps-with-mitre-attack`, `analyzing-threat-landscape-with-misp`, `building-attack-pattern-library-from-cti-reports`, `hunting-advanced-persistent-threats` +3 more | +| [T1566.001](https://attack.mitre.org/techniques/T1566/001/) | `analyzing-apt-group-with-mitre-navigator`, `analyzing-campaign-attribution-evidence`, `analyzing-macro-malware-in-office-documents`, `analyzing-threat-actor-ttps-with-mitre-navigator`, `building-attack-pattern-library-from-cti-reports` +13 more | +| [T1566.002](https://attack.mitre.org/techniques/T1566/002/) | `building-attack-pattern-library-from-cti-reports`, `conducting-spearphishing-simulation-campaign`, `hunting-for-spearphishing-indicators`, `implementing-continuous-security-validation-with-bas`, `implementing-mitre-attack-coverage-mapping` +1 more | +| [T1566.003](https://attack.mitre.org/techniques/T1566/003/) | `conducting-spearphishing-simulation-campaign`, `hunting-for-spearphishing-indicators`, `implementing-continuous-security-validation-with-bas` | +| [T1566.004](https://attack.mitre.org/techniques/T1566/004/) | `conducting-social-engineering-pretext-call` | + +--- + +## ⚡ Execution + +**18 techniques covered** + +| Technique | Skills | +|:----------|:-------| +| [T1047](https://attack.mitre.org/techniques/T1047/) | `conducting-full-scope-red-team-engagement`, `detecting-fileless-attacks-on-endpoints`, `detecting-lateral-movement-with-splunk`, `detecting-living-off-the-land-attacks`, `detecting-living-off-the-land-with-lolbas` +8 more | +| [T1053](https://attack.mitre.org/techniques/T1053/) | `analyzing-apt-group-with-mitre-navigator`, `analyzing-persistence-mechanisms-in-linux`, `hunting-advanced-persistent-threats`, `hunting-for-persistence-mechanisms-in-windows`, `implementing-mitre-attack-coverage-mapping` +4 more | +| [T1053.002](https://attack.mitre.org/techniques/T1053/002/) | `hunting-for-scheduled-task-persistence` | +| [T1053.003](https://attack.mitre.org/techniques/T1053/003/) | `analyzing-persistence-mechanisms-in-linux`, `hunting-for-scheduled-task-persistence`, `performing-privilege-escalation-assessment`, `performing-privilege-escalation-on-linux` | +| [T1053.005](https://attack.mitre.org/techniques/T1053/005/) | `analyzing-apt-group-with-mitre-navigator`, `analyzing-campaign-attribution-evidence`, `analyzing-windows-event-logs-in-splunk`, `building-attack-pattern-library-from-cti-reports`, `building-detection-rule-with-splunk-spl` +17 more | +| [T1059](https://attack.mitre.org/techniques/T1059/) | `analyzing-apt-group-with-mitre-navigator`, `analyzing-threat-actor-ttps-with-mitre-attack`, `analyzing-windows-event-logs-in-splunk`, `building-incident-timeline-with-timesketch`, `deobfuscating-powershell-obfuscated-malware` +7 more | +| [T1059.001](https://attack.mitre.org/techniques/T1059/001/) | `analyzing-apt-group-with-mitre-navigator`, `analyzing-campaign-attribution-evidence`, `analyzing-macro-malware-in-office-documents`, `analyzing-powershell-empire-artifacts`, `analyzing-powershell-script-block-logging` +29 more | +| [T1059.003](https://attack.mitre.org/techniques/T1059/003/) | `building-attack-pattern-library-from-cti-reports`, `building-detection-rule-with-splunk-spl`, `detecting-suspicious-powershell-execution`, `mapping-mitre-attack-techniques`, `performing-purple-team-atomic-testing` | +| [T1059.004](https://attack.mitre.org/techniques/T1059/004/) | `performing-purple-team-atomic-testing` | +| [T1059.005](https://attack.mitre.org/techniques/T1059/005/) | `analyzing-macro-malware-in-office-documents`, `detecting-living-off-the-land-attacks`, `executing-red-team-exercise`, `hunting-for-living-off-the-land-binaries`, `hunting-for-lolbins-execution-in-endpoint-logs` +2 more | +| [T1059.006](https://attack.mitre.org/techniques/T1059/006/) | `performing-purple-team-atomic-testing` | +| [T1059.007](https://attack.mitre.org/techniques/T1059/007/) | `performing-purple-team-atomic-testing` | +| [T1129](https://attack.mitre.org/techniques/T1129/) | `performing-purple-team-atomic-testing` | +| [T1203](https://attack.mitre.org/techniques/T1203/) | `performing-purple-team-atomic-testing` | +| [T1204.001](https://attack.mitre.org/techniques/T1204/001/) | `conducting-spearphishing-simulation-campaign` | +| [T1204.002](https://attack.mitre.org/techniques/T1204/002/) | `analyzing-macro-malware-in-office-documents`, `conducting-full-scope-red-team-engagement`, `conducting-spearphishing-simulation-campaign`, `detecting-living-off-the-land-attacks`, `executing-red-team-engagement-planning` +4 more | +| [T1569](https://attack.mitre.org/techniques/T1569/) | `performing-purple-team-atomic-testing` | +| [T1569.002](https://attack.mitre.org/techniques/T1569/002/) | `detecting-lateral-movement-in-network`, `detecting-lateral-movement-with-splunk`, `exploiting-ms17-010-eternalblue-vulnerability`, `performing-purple-team-atomic-testing` | + +--- + +## 🔩 Persistence + +**36 techniques covered** + +| Technique | Skills | +|:----------|:-------| +| [T1098](https://attack.mitre.org/techniques/T1098/) | `analyzing-windows-event-logs-in-splunk`, `conducting-domain-persistence-with-dcsync`, `hunting-for-t1098-account-manipulation`, `implementing-mitre-attack-coverage-mapping`, `implementing-siem-use-cases-for-detection` +1 more | +| [T1098.001](https://attack.mitre.org/techniques/T1098/001/) | `conducting-cloud-penetration-testing`, `detecting-azure-lateral-movement`, `detecting-azure-service-principal-abuse`, `hunting-for-t1098-account-manipulation`, `implementing-mitre-attack-coverage-mapping` | +| [T1098.002](https://attack.mitre.org/techniques/T1098/002/) | `detecting-azure-lateral-movement`, `detecting-email-forwarding-rules-attack` | +| [T1098.004](https://attack.mitre.org/techniques/T1098/004/) | `analyzing-persistence-mechanisms-in-linux`, `implementing-security-monitoring-with-datadog` | +| [T1136](https://attack.mitre.org/techniques/T1136/) | `detecting-privilege-escalation-in-kubernetes-pods`, `implementing-mitre-attack-coverage-mapping`, `performing-purple-team-atomic-testing` | +| [T1136.001](https://attack.mitre.org/techniques/T1136/001/) | `analyzing-windows-event-logs-in-splunk`, `performing-purple-team-atomic-testing` | +| [T1136.002](https://attack.mitre.org/techniques/T1136/002/) | `exploiting-nopac-cve-2021-42278-42287` | +| [T1197](https://attack.mitre.org/techniques/T1197/) | `detecting-living-off-the-land-attacks`, `detecting-living-off-the-land-with-lolbas`, `hunting-for-living-off-the-land-binaries`, `hunting-for-lolbins-execution-in-endpoint-logs`, `performing-purple-team-atomic-testing` | +| [T1505](https://attack.mitre.org/techniques/T1505/) | `performing-purple-team-atomic-testing` | +| [T1505.003](https://attack.mitre.org/techniques/T1505/003/) | `building-attack-pattern-library-from-cti-reports`, `hunting-for-webshell-activity`, `performing-purple-team-atomic-testing` | +| [T1542.001](https://attack.mitre.org/techniques/T1542/001/) | `analyzing-uefi-bootkit-persistence` | +| [T1542.003](https://attack.mitre.org/techniques/T1542/003/) | `analyzing-uefi-bootkit-persistence` | +| [T1543](https://attack.mitre.org/techniques/T1543/) | `analyzing-persistence-mechanisms-in-linux`, `hunting-for-persistence-mechanisms-in-windows`, `performing-purple-team-atomic-testing` | +| [T1543.002](https://attack.mitre.org/techniques/T1543/002/) | `analyzing-persistence-mechanisms-in-linux`, `performing-privilege-escalation-on-linux` | +| [T1543.003](https://attack.mitre.org/techniques/T1543/003/) | `detecting-lateral-movement-with-splunk`, `detecting-living-off-the-land-attacks`, `detecting-privilege-escalation-attempts`, `hunting-for-persistence-mechanisms-in-windows`, `hunting-for-unusual-service-installations` +2 more | +| [T1546](https://attack.mitre.org/techniques/T1546/) | `analyzing-persistence-mechanisms-in-linux`, `performing-purple-team-atomic-testing` | +| [T1546.001](https://attack.mitre.org/techniques/T1546/001/) | `performing-purple-team-atomic-testing` | +| [T1546.003](https://attack.mitre.org/techniques/T1546/003/) | `analyzing-windows-event-logs-in-splunk`, `detecting-fileless-attacks-on-endpoints`, `detecting-fileless-malware-techniques`, `detecting-wmi-persistence`, `hunting-for-lateral-movement-via-wmi` +3 more | +| [T1546.004](https://attack.mitre.org/techniques/T1546/004/) | `analyzing-persistence-mechanisms-in-linux` | +| [T1546.010](https://attack.mitre.org/techniques/T1546/010/) | `hunting-for-persistence-mechanisms-in-windows` | +| [T1546.012](https://attack.mitre.org/techniques/T1546/012/) | `hunting-for-persistence-mechanisms-in-windows`, `hunting-for-registry-persistence-mechanisms` | +| [T1546.015](https://attack.mitre.org/techniques/T1546/015/) | `hunting-for-persistence-mechanisms-in-windows`, `hunting-for-registry-persistence-mechanisms` | +| [T1547](https://attack.mitre.org/techniques/T1547/) | `analyzing-apt-group-with-mitre-navigator`, `analyzing-malware-persistence-with-autoruns`, `hunting-advanced-persistent-threats`, `hunting-for-persistence-mechanisms-in-windows`, `implementing-siem-use-cases-for-detection` +3 more | +| [T1547.001](https://attack.mitre.org/techniques/T1547/001/) | `analyzing-apt-group-with-mitre-navigator`, `analyzing-windows-event-logs-in-splunk`, `building-attack-pattern-library-from-cti-reports`, `conducting-full-scope-red-team-engagement`, `detecting-fileless-attacks-on-endpoints` +10 more | +| [T1547.004](https://attack.mitre.org/techniques/T1547/004/) | `hunting-for-persistence-mechanisms-in-windows`, `hunting-for-registry-persistence-mechanisms`, `performing-purple-team-atomic-testing` | +| [T1547.005](https://attack.mitre.org/techniques/T1547/005/) | `hunting-for-persistence-mechanisms-in-windows` | +| [T1547.009](https://attack.mitre.org/techniques/T1547/009/) | `performing-purple-team-atomic-testing` | +| [T1556](https://attack.mitre.org/techniques/T1556/) | `performing-initial-access-with-evilginx3` | +| [T1556.007](https://attack.mitre.org/techniques/T1556/007/) | `detecting-azure-lateral-movement` | +| [T1574](https://attack.mitre.org/techniques/T1574/) | `analyzing-persistence-mechanisms-in-linux`, `performing-purple-team-atomic-testing` | +| [T1574.001](https://attack.mitre.org/techniques/T1574/001/) | `detecting-dll-sideloading-attacks`, `hunting-for-persistence-mechanisms-in-windows`, `performing-purple-team-atomic-testing` | +| [T1574.002](https://attack.mitre.org/techniques/T1574/002/) | `analyzing-windows-event-logs-in-splunk`, `building-attack-pattern-library-from-cti-reports`, `detecting-dll-sideloading-attacks`, `implementing-siem-use-cases-for-detection`, `performing-purple-team-atomic-testing` | +| [T1574.006](https://attack.mitre.org/techniques/T1574/006/) | `analyzing-persistence-mechanisms-in-linux`, `detecting-dll-sideloading-attacks`, `performing-privilege-escalation-on-linux` | +| [T1574.008](https://attack.mitre.org/techniques/T1574/008/) | `detecting-dll-sideloading-attacks` | +| [T1574.009](https://attack.mitre.org/techniques/T1574/009/) | `detecting-privilege-escalation-attempts` | +| [T1574.011](https://attack.mitre.org/techniques/T1574/011/) | `detecting-privilege-escalation-attempts` | + +--- + +## ⬆️ Privilege Escalation + +**11 techniques covered** + +| Technique | Skills | +|:----------|:-------| +| [T1068](https://attack.mitre.org/techniques/T1068/) | `conducting-full-scope-red-team-engagement`, `detecting-container-escape-attempts`, `detecting-privilege-escalation-attempts`, `detecting-privilege-escalation-in-kubernetes-pods`, `executing-red-team-engagement-planning` +5 more | +| [T1134](https://attack.mitre.org/techniques/T1134/) | `analyzing-windows-event-logs-in-splunk`, `detecting-privilege-escalation-attempts` | +| [T1134.001](https://attack.mitre.org/techniques/T1134/001/) | `detecting-privilege-escalation-attempts`, `exploiting-constrained-delegation-abuse`, `performing-purple-team-atomic-testing` | +| [T1134.005](https://attack.mitre.org/techniques/T1134/005/) | `hunting-for-t1098-account-manipulation`, `performing-active-directory-compromise-investigation` | +| [T1484](https://attack.mitre.org/techniques/T1484/) | `exploiting-active-directory-certificate-services-esc1`, `performing-active-directory-vulnerability-assessment` | +| [T1484.001](https://attack.mitre.org/techniques/T1484/001/) | `deploying-active-directory-honeytokens`, `performing-active-directory-compromise-investigation` | +| [T1548](https://attack.mitre.org/techniques/T1548/) | `detecting-container-escape-attempts`, `detecting-privilege-escalation-in-kubernetes-pods`, `detecting-t1548-abuse-elevation-control-mechanism`, `performing-privilege-escalation-assessment` | +| [T1548.001](https://attack.mitre.org/techniques/T1548/001/) | `detecting-privilege-escalation-attempts`, `detecting-privilege-escalation-in-kubernetes-pods`, `detecting-t1548-abuse-elevation-control-mechanism`, `performing-privilege-escalation-assessment`, `performing-privilege-escalation-on-linux` | +| [T1548.002](https://attack.mitre.org/techniques/T1548/002/) | `conducting-full-scope-red-team-engagement`, `detecting-privilege-escalation-attempts`, `detecting-t1548-abuse-elevation-control-mechanism`, `performing-purple-team-atomic-testing` | +| [T1548.003](https://attack.mitre.org/techniques/T1548/003/) | `detecting-privilege-escalation-attempts`, `detecting-t1548-abuse-elevation-control-mechanism`, `performing-privilege-escalation-assessment`, `performing-privilege-escalation-on-linux` | +| [T1548.004](https://attack.mitre.org/techniques/T1548/004/) | `detecting-t1548-abuse-elevation-control-mechanism` | + +--- + +## 🥷 Defense Evasion + +**48 techniques covered** + +| Technique | Skills | +|:----------|:-------| +| [T1027](https://attack.mitre.org/techniques/T1027/) | `analyzing-apt-group-with-mitre-navigator`, `analyzing-powershell-empire-artifacts`, `analyzing-powershell-script-block-logging`, `building-attack-pattern-library-from-cti-reports`, `conducting-full-scope-red-team-engagement` +3 more | +| [T1036](https://attack.mitre.org/techniques/T1036/) | `detecting-evasion-techniques-in-endpoint-logs`, `implementing-mitre-attack-coverage-mapping`, `implementing-siem-use-cases-for-detection`, `performing-purple-team-atomic-testing` | +| [T1036.005](https://attack.mitre.org/techniques/T1036/005/) | `detecting-process-injection-techniques`, `performing-purple-team-atomic-testing` | +| [T1055](https://attack.mitre.org/techniques/T1055/) | `building-attack-pattern-library-from-cti-reports`, `building-red-team-c2-infrastructure-with-havoc`, `conducting-full-scope-red-team-engagement`, `detecting-evasion-techniques-in-endpoint-logs`, `detecting-fileless-attacks-on-endpoints` +13 more | +| [T1055.001](https://attack.mitre.org/techniques/T1055/001/) | `detecting-process-hollowing-technique`, `detecting-process-injection-techniques`, `detecting-t1055-process-injection-with-sysmon`, `hunting-for-process-injection-techniques`, `performing-purple-team-atomic-testing` +1 more | +| [T1055.002](https://attack.mitre.org/techniques/T1055/002/) | `detecting-process-injection-techniques`, `detecting-t1055-process-injection-with-sysmon` | +| [T1055.003](https://attack.mitre.org/techniques/T1055/003/) | `detecting-process-hollowing-technique`, `detecting-process-injection-techniques`, `detecting-t1055-process-injection-with-sysmon`, `performing-purple-team-atomic-testing` | +| [T1055.004](https://attack.mitre.org/techniques/T1055/004/) | `detecting-process-hollowing-technique`, `detecting-process-injection-techniques`, `detecting-t1055-process-injection-with-sysmon`, `hunting-for-process-injection-techniques` | +| [T1055.005](https://attack.mitre.org/techniques/T1055/005/) | `detecting-process-injection-techniques`, `detecting-t1055-process-injection-with-sysmon` | +| [T1055.008](https://attack.mitre.org/techniques/T1055/008/) | `detecting-process-injection-techniques` | +| [T1055.009](https://attack.mitre.org/techniques/T1055/009/) | `detecting-process-injection-techniques` | +| [T1055.011](https://attack.mitre.org/techniques/T1055/011/) | `detecting-process-injection-techniques` | +| [T1055.012](https://attack.mitre.org/techniques/T1055/012/) | `conducting-malware-incident-response`, `detecting-fileless-malware-techniques`, `detecting-process-hollowing-technique`, `detecting-process-injection-techniques`, `detecting-t1055-process-injection-with-sysmon` +2 more | +| [T1055.013](https://attack.mitre.org/techniques/T1055/013/) | `detecting-process-hollowing-technique`, `detecting-process-injection-techniques`, `detecting-t1055-process-injection-with-sysmon` | +| [T1055.014](https://attack.mitre.org/techniques/T1055/014/) | `detecting-process-injection-techniques` | +| [T1055.015](https://attack.mitre.org/techniques/T1055/015/) | `detecting-process-injection-techniques`, `detecting-t1055-process-injection-with-sysmon` | +| [T1070](https://attack.mitre.org/techniques/T1070/) | `detecting-evasion-techniques-in-endpoint-logs`, `implementing-siem-use-cases-for-detection`, `implementing-velociraptor-for-ir-collection`, `performing-purple-team-atomic-testing` | +| [T1070.001](https://attack.mitre.org/techniques/T1070/001/) | `detecting-evasion-techniques-in-endpoint-logs`, `implementing-mitre-attack-coverage-mapping`, `performing-purple-team-atomic-testing`, `performing-purple-team-exercise` | +| [T1070.004](https://attack.mitre.org/techniques/T1070/004/) | `implementing-threat-modeling-with-mitre-attack`, `performing-purple-team-atomic-testing` | +| [T1070.006](https://attack.mitre.org/techniques/T1070/006/) | `detecting-evasion-techniques-in-endpoint-logs`, `hunting-for-defense-evasion-via-timestomping` | +| [T1112](https://attack.mitre.org/techniques/T1112/) | `detecting-fileless-malware-techniques`, `performing-purple-team-atomic-testing` | +| [T1127](https://attack.mitre.org/techniques/T1127/) | `detecting-evasion-techniques-in-endpoint-logs`, `detecting-living-off-the-land-with-lolbas`, `hunting-for-lolbins-execution-in-endpoint-logs` | +| [T1127.001](https://attack.mitre.org/techniques/T1127/001/) | `detecting-living-off-the-land-attacks`, `detecting-living-off-the-land-with-lolbas`, `hunting-for-lolbins-execution-in-endpoint-logs` | +| [T1140](https://attack.mitre.org/techniques/T1140/) | `analyzing-powershell-script-block-logging`, `detecting-fileless-attacks-on-endpoints`, `detecting-living-off-the-land-with-lolbas`, `hunting-for-living-off-the-land-binaries`, `hunting-for-lolbins-execution-in-endpoint-logs` +1 more | +| [T1202](https://attack.mitre.org/techniques/T1202/) | `hunting-for-living-off-the-land-binaries`, `hunting-for-lolbins-execution-in-endpoint-logs` | +| [T1218](https://attack.mitre.org/techniques/T1218/) | `detecting-evasion-techniques-in-endpoint-logs`, `detecting-living-off-the-land-attacks`, `detecting-living-off-the-land-with-lolbas`, `hunting-advanced-persistent-threats`, `hunting-for-living-off-the-land-binaries` +3 more | +| [T1218.001](https://attack.mitre.org/techniques/T1218/001/) | `hunting-for-living-off-the-land-binaries`, `hunting-for-lolbins-execution-in-endpoint-logs`, `performing-purple-team-atomic-testing` | +| [T1218.002](https://attack.mitre.org/techniques/T1218/002/) | `hunting-for-living-off-the-land-binaries` | +| [T1218.003](https://attack.mitre.org/techniques/T1218/003/) | `detecting-living-off-the-land-attacks`, `detecting-living-off-the-land-with-lolbas`, `hunting-for-living-off-the-land-binaries`, `hunting-for-lolbins-execution-in-endpoint-logs`, `performing-purple-team-atomic-testing` | +| [T1218.004](https://attack.mitre.org/techniques/T1218/004/) | `detecting-living-off-the-land-attacks`, `hunting-for-lolbins-execution-in-endpoint-logs` | +| [T1218.005](https://attack.mitre.org/techniques/T1218/005/) | `detecting-fileless-malware-techniques`, `detecting-living-off-the-land-attacks`, `detecting-living-off-the-land-with-lolbas`, `hunting-for-living-off-the-land-binaries`, `hunting-for-lolbins-execution-in-endpoint-logs` +1 more | +| [T1218.007](https://attack.mitre.org/techniques/T1218/007/) | `hunting-for-living-off-the-land-binaries`, `hunting-for-lolbins-execution-in-endpoint-logs` | +| [T1218.010](https://attack.mitre.org/techniques/T1218/010/) | `detecting-living-off-the-land-attacks`, `detecting-living-off-the-land-with-lolbas`, `hunting-for-living-off-the-land-binaries`, `hunting-for-lolbins-execution-in-endpoint-logs`, `performing-purple-team-atomic-testing` | +| [T1218.011](https://attack.mitre.org/techniques/T1218/011/) | `detecting-living-off-the-land-attacks`, `detecting-living-off-the-land-with-lolbas`, `hunting-for-living-off-the-land-binaries`, `hunting-for-lolbins-execution-in-endpoint-logs`, `performing-dynamic-analysis-with-any-run` +1 more | +| [T1218.013](https://attack.mitre.org/techniques/T1218/013/) | `detecting-living-off-the-land-attacks` | +| [T1222.001](https://attack.mitre.org/techniques/T1222/001/) | `conducting-domain-persistence-with-dcsync` | +| [T1497](https://attack.mitre.org/techniques/T1497/) | `analyzing-malware-sandbox-evasion-techniques` | +| [T1497.001](https://attack.mitre.org/techniques/T1497/001/) | `analyzing-malware-sandbox-evasion-techniques` | +| [T1497.002](https://attack.mitre.org/techniques/T1497/002/) | `analyzing-malware-sandbox-evasion-techniques` | +| [T1497.003](https://attack.mitre.org/techniques/T1497/003/) | `analyzing-malware-sandbox-evasion-techniques` | +| [T1550](https://attack.mitre.org/techniques/T1550/) | `performing-lateral-movement-detection` | +| [T1550.001](https://attack.mitre.org/techniques/T1550/001/) | `detecting-azure-lateral-movement` | +| [T1550.002](https://attack.mitre.org/techniques/T1550/002/) | `analyzing-windows-event-logs-in-splunk`, `building-attack-pattern-library-from-cti-reports`, `conducting-full-scope-red-team-engagement`, `detecting-lateral-movement-in-network`, `detecting-lateral-movement-with-splunk` +6 more | +| [T1550.003](https://attack.mitre.org/techniques/T1550/003/) | `conducting-pass-the-ticket-attack`, `detecting-pass-the-hash-attacks`, `detecting-pass-the-ticket-attacks`, `exploiting-constrained-delegation-abuse` | +| [T1550.004](https://attack.mitre.org/techniques/T1550/004/) | `performing-initial-access-with-evilginx3` | +| [T1562](https://attack.mitre.org/techniques/T1562/) | `detecting-evasion-techniques-in-endpoint-logs`, `performing-purple-team-atomic-testing` | +| [T1562.001](https://attack.mitre.org/techniques/T1562/001/) | `analyzing-powershell-script-block-logging`, `building-attack-pattern-library-from-cti-reports`, `detecting-evasion-techniques-in-endpoint-logs`, `detecting-fileless-attacks-on-endpoints`, `detecting-suspicious-powershell-execution` +1 more | +| [T1610](https://attack.mitre.org/techniques/T1610/) | `detecting-container-escape-attempts`, `detecting-container-escape-with-falco-rules` | + +--- + +## 🔑 Credential Access + +**27 techniques covered** + +| Technique | Skills | +|:----------|:-------| +| [T1003](https://attack.mitre.org/techniques/T1003/) | `analyzing-powershell-script-block-logging`, `building-attack-pattern-library-from-cti-reports`, `building-detection-rules-with-sigma`, `detecting-container-escape-with-falco-rules`, `detecting-credential-dumping-techniques` +10 more | +| [T1003.001](https://attack.mitre.org/techniques/T1003/001/) | `analyzing-campaign-attribution-evidence`, `analyzing-powershell-script-block-logging`, `analyzing-windows-event-logs-in-splunk`, `building-attack-pattern-library-from-cti-reports`, `building-detection-rule-with-splunk-spl` +13 more | +| [T1003.002](https://attack.mitre.org/techniques/T1003/002/) | `detecting-credential-dumping-techniques`, `detecting-t1003-credential-dumping-with-edr`, `performing-purple-team-atomic-testing` | +| [T1003.003](https://attack.mitre.org/techniques/T1003/003/) | `detecting-credential-dumping-techniques`, `detecting-t1003-credential-dumping-with-edr`, `performing-purple-team-atomic-testing` | +| [T1003.004](https://attack.mitre.org/techniques/T1003/004/) | `detecting-t1003-credential-dumping-with-edr`, `performing-credential-access-with-lazagne`, `performing-purple-team-atomic-testing` | +| [T1003.005](https://attack.mitre.org/techniques/T1003/005/) | `detecting-t1003-credential-dumping-with-edr`, `performing-purple-team-atomic-testing` | +| [T1003.006](https://attack.mitre.org/techniques/T1003/006/) | `analyzing-windows-event-logs-in-splunk`, `conducting-domain-persistence-with-dcsync`, `conducting-full-scope-red-team-engagement`, `conducting-internal-network-penetration-test`, `detecting-dcsync-attack-in-active-directory` +8 more | +| [T1110](https://attack.mitre.org/techniques/T1110/) | `analyzing-windows-event-logs-in-splunk`, `building-detection-rule-with-splunk-spl`, `conducting-internal-network-penetration-test`, `implementing-mitre-attack-coverage-mapping`, `implementing-siem-use-cases-for-detection` +3 more | +| [T1110.001](https://attack.mitre.org/techniques/T1110/001/) | `analyzing-windows-event-logs-in-splunk`, `building-detection-rule-with-splunk-spl`, `implementing-siem-use-cases-for-detection`, `performing-false-positive-reduction-in-siem`, `performing-purple-team-atomic-testing` | +| [T1110.002](https://attack.mitre.org/techniques/T1110/002/) | `exploiting-kerberoasting-with-impacket` | +| [T1110.003](https://attack.mitre.org/techniques/T1110/003/) | `detecting-pass-the-ticket-attacks`, `implementing-siem-use-cases-for-detection`, `performing-purple-team-atomic-testing` | +| [T1187](https://attack.mitre.org/techniques/T1187/) | `detecting-ntlm-relay-with-event-correlation` | +| [T1528](https://attack.mitre.org/techniques/T1528/) | `detecting-azure-lateral-movement`, `detecting-azure-service-principal-abuse` | +| [T1539](https://attack.mitre.org/techniques/T1539/) | `performing-credential-access-with-lazagne`, `performing-initial-access-with-evilginx3` | +| [T1552](https://attack.mitre.org/techniques/T1552/) | `performing-cloud-incident-containment-procedures`, `performing-purple-team-atomic-testing` | +| [T1552.001](https://attack.mitre.org/techniques/T1552/001/) | `performing-credential-access-with-lazagne`, `performing-purple-team-atomic-testing` | +| [T1552.002](https://attack.mitre.org/techniques/T1552/002/) | `performing-credential-access-with-lazagne` | +| [T1552.005](https://attack.mitre.org/techniques/T1552/005/) | `conducting-cloud-penetration-testing` | +| [T1552.006](https://attack.mitre.org/techniques/T1552/006/) | `deploying-active-directory-honeytokens` | +| [T1557](https://attack.mitre.org/techniques/T1557/) | `performing-initial-access-with-evilginx3` | +| [T1557.001](https://attack.mitre.org/techniques/T1557/001/) | `conducting-internal-network-penetration-test`, `detecting-ntlm-relay-with-event-correlation`, `hunting-for-ntlm-relay-attacks` | +| [T1558](https://attack.mitre.org/techniques/T1558/) | `analyzing-windows-event-logs-in-splunk`, `conducting-pass-the-ticket-attack`, `exploiting-kerberoasting-with-impacket`, `exploiting-nopac-cve-2021-42278-42287`, `performing-lateral-movement-detection` +1 more | +| [T1558.001](https://attack.mitre.org/techniques/T1558/001/) | `analyzing-windows-event-logs-in-splunk`, `conducting-domain-persistence-with-dcsync`, `detecting-golden-ticket-attacks-in-kerberos-logs`, `detecting-golden-ticket-forgery`, `detecting-kerberoasting-attacks` +3 more | +| [T1558.002](https://attack.mitre.org/techniques/T1558/002/) | `performing-active-directory-compromise-investigation` | +| [T1558.003](https://attack.mitre.org/techniques/T1558/003/) | `analyzing-windows-event-logs-in-splunk`, `building-attack-pattern-library-from-cti-reports`, `conducting-full-scope-red-team-engagement`, `conducting-internal-network-penetration-test`, `deploying-active-directory-honeytokens` +12 more | +| [T1558.004](https://attack.mitre.org/techniques/T1558/004/) | `detecting-kerberoasting-attacks` | +| [T1649](https://attack.mitre.org/techniques/T1649/) | `exploiting-active-directory-certificate-services-esc1` | + +--- + +## 🗺️ Discovery + +**20 techniques covered** + +| Technique | Skills | +|:----------|:-------| +| [T1016](https://attack.mitre.org/techniques/T1016/) | `conducting-full-scope-red-team-engagement`, `conducting-internal-reconnaissance-with-bloodhound-ce`, `exploiting-active-directory-with-bloodhound`, `performing-purple-team-atomic-testing` | +| [T1018](https://attack.mitre.org/techniques/T1018/) | `conducting-full-scope-red-team-engagement`, `conducting-internal-reconnaissance-with-bloodhound-ce`, `detecting-network-scanning-with-ids-signatures`, `exploiting-active-directory-with-bloodhound`, `performing-active-directory-bloodhound-analysis` | +| [T1033](https://attack.mitre.org/techniques/T1033/) | `conducting-internal-reconnaissance-with-bloodhound-ce`, `detecting-privilege-escalation-attempts`, `exploiting-active-directory-with-bloodhound`, `performing-purple-team-atomic-testing` | +| [T1040](https://attack.mitre.org/techniques/T1040/) | `implementing-continuous-security-validation-with-bas` | +| [T1046](https://attack.mitre.org/techniques/T1046/) | `detecting-network-scanning-with-ids-signatures`, `detecting-privilege-escalation-attempts`, `performing-packet-injection-attack`, `triaging-security-incident` | +| [T1049](https://attack.mitre.org/techniques/T1049/) | `performing-purple-team-atomic-testing` | +| [T1057](https://attack.mitre.org/techniques/T1057/) | `performing-purple-team-atomic-testing` | +| [T1069](https://attack.mitre.org/techniques/T1069/) | `performing-purple-team-atomic-testing` | +| [T1069.001](https://attack.mitre.org/techniques/T1069/001/) | `performing-active-directory-bloodhound-analysis`, `performing-purple-team-atomic-testing` | +| [T1069.002](https://attack.mitre.org/techniques/T1069/002/) | `conducting-internal-reconnaissance-with-bloodhound-ce`, `exploiting-active-directory-with-bloodhound`, `performing-active-directory-bloodhound-analysis`, `performing-kerberoasting-attack`, `performing-purple-team-atomic-testing` | +| [T1082](https://attack.mitre.org/techniques/T1082/) | `conducting-full-scope-red-team-engagement`, `performing-purple-team-atomic-testing` | +| [T1083](https://attack.mitre.org/techniques/T1083/) | `implementing-canary-tokens-for-network-intrusion`, `performing-purple-team-atomic-testing` | +| [T1087](https://attack.mitre.org/techniques/T1087/) | `conducting-full-scope-red-team-engagement`, `executing-red-team-engagement-planning`, `implementing-continuous-security-validation-with-bas`, `performing-purple-team-atomic-testing` | +| [T1087.001](https://attack.mitre.org/techniques/T1087/001/) | `performing-purple-team-atomic-testing` | +| [T1087.002](https://attack.mitre.org/techniques/T1087/002/) | `conducting-internal-reconnaissance-with-bloodhound-ce`, `deploying-active-directory-honeytokens`, `exploiting-active-directory-certificate-services-esc1`, `exploiting-active-directory-with-bloodhound`, `exploiting-kerberoasting-with-impacket` +3 more | +| [T1087.004](https://attack.mitre.org/techniques/T1087/004/) | `detecting-azure-service-principal-abuse`, `implementing-mitre-attack-coverage-mapping` | +| [T1482](https://attack.mitre.org/techniques/T1482/) | `conducting-internal-reconnaissance-with-bloodhound-ce`, `exploiting-active-directory-with-bloodhound`, `performing-active-directory-bloodhound-analysis` | +| [T1518](https://attack.mitre.org/techniques/T1518/) | `performing-purple-team-atomic-testing` | +| [T1518.001](https://attack.mitre.org/techniques/T1518/001/) | `performing-purple-team-atomic-testing` | +| [T1580](https://attack.mitre.org/techniques/T1580/) | `implementing-mitre-attack-coverage-mapping` | + +--- + +## ↔️ Lateral Movement + +**9 techniques covered** + +| Technique | Skills | +|:----------|:-------| +| [T1021](https://attack.mitre.org/techniques/T1021/) | `detecting-lateral-movement-in-network`, `detecting-lateral-movement-with-splunk`, `detecting-service-account-abuse`, `executing-red-team-engagement-planning`, `exploiting-constrained-delegation-abuse` +10 more | +| [T1021.001](https://attack.mitre.org/techniques/T1021/001/) | `analyzing-campaign-attribution-evidence`, `analyzing-windows-event-logs-in-splunk`, `building-attack-pattern-library-from-cti-reports`, `building-detection-rule-with-splunk-spl`, `building-threat-hunt-hypothesis-framework` +8 more | +| [T1021.002](https://attack.mitre.org/techniques/T1021/002/) | `analyzing-windows-event-logs-in-splunk`, `building-attack-pattern-library-from-cti-reports`, `building-detection-rule-with-splunk-spl`, `conducting-full-scope-red-team-engagement`, `conducting-internal-network-penetration-test` +10 more | +| [T1021.003](https://attack.mitre.org/techniques/T1021/003/) | `detecting-lateral-movement-with-splunk`, `hunting-for-dcom-lateral-movement`, `performing-lateral-movement-detection`, `performing-lateral-movement-with-wmiexec`, `performing-purple-team-atomic-testing` | +| [T1021.004](https://attack.mitre.org/techniques/T1021/004/) | `detecting-lateral-movement-with-splunk`, `performing-purple-team-atomic-testing` | +| [T1021.006](https://attack.mitre.org/techniques/T1021/006/) | `building-attack-pattern-library-from-cti-reports`, `detecting-lateral-movement-with-splunk`, `performing-lateral-movement-detection`, `performing-purple-team-atomic-testing` | +| [T1210](https://attack.mitre.org/techniques/T1210/) | `exploiting-ms17-010-eternalblue-vulnerability`, `exploiting-zerologon-vulnerability-cve-2020-1472` | +| [T1534](https://attack.mitre.org/techniques/T1534/) | `implementing-mitre-attack-coverage-mapping` | +| [T1570](https://attack.mitre.org/techniques/T1570/) | `detecting-lateral-movement-in-network`, `detecting-lateral-movement-with-splunk`, `performing-lateral-movement-with-wmiexec`, `performing-purple-team-atomic-testing` | + +--- + +## 📦 Collection + +**13 techniques covered** + +| Technique | Skills | +|:----------|:-------| +| [T1005](https://attack.mitre.org/techniques/T1005/) | `conducting-malware-incident-response`, `detecting-container-escape-with-falco-rules`, `performing-purple-team-atomic-testing` | +| [T1039](https://attack.mitre.org/techniques/T1039/) | `performing-purple-team-atomic-testing` | +| [T1074](https://attack.mitre.org/techniques/T1074/) | `building-attack-pattern-library-from-cti-reports`, `executing-red-team-exercise`, `hunting-for-data-staging-before-exfiltration` | +| [T1074.001](https://attack.mitre.org/techniques/T1074/001/) | `hunting-for-data-staging-before-exfiltration`, `performing-purple-team-atomic-testing` | +| [T1074.002](https://attack.mitre.org/techniques/T1074/002/) | `hunting-for-data-staging-before-exfiltration` | +| [T1113](https://attack.mitre.org/techniques/T1113/) | `performing-purple-team-atomic-testing` | +| [T1114.002](https://attack.mitre.org/techniques/T1114/002/) | `detecting-email-forwarding-rules-attack` | +| [T1114.003](https://attack.mitre.org/techniques/T1114/003/) | `detecting-business-email-compromise`, `detecting-email-forwarding-rules-attack` | +| [T1115](https://attack.mitre.org/techniques/T1115/) | `performing-purple-team-atomic-testing` | +| [T1213](https://attack.mitre.org/techniques/T1213/) | `conducting-full-scope-red-team-engagement` | +| [T1530](https://attack.mitre.org/techniques/T1530/) | `detecting-insider-threat-behaviors`, `implementing-mitre-attack-coverage-mapping`, `performing-cloud-incident-containment-procedures` | +| [T1560](https://attack.mitre.org/techniques/T1560/) | `conducting-full-scope-red-team-engagement`, `hunting-for-data-staging-before-exfiltration` | +| [T1560.001](https://attack.mitre.org/techniques/T1560/001/) | `hunting-for-data-staging-before-exfiltration`, `performing-purple-team-atomic-testing` | + +--- + +## 📡 Command and Control + +**20 techniques covered** + +| Technique | Skills | +|:----------|:-------| +| [T1071](https://attack.mitre.org/techniques/T1071/) | `analyzing-apt-group-with-mitre-navigator`, `analyzing-network-covert-channels-in-malware`, `analyzing-ransomware-network-indicators`, `analyzing-threat-actor-ttps-with-mitre-attack`, `hunting-advanced-persistent-threats` +6 more | +| [T1071.001](https://attack.mitre.org/techniques/T1071/001/) | `analyzing-apt-group-with-mitre-navigator`, `analyzing-campaign-attribution-evidence`, `analyzing-powershell-empire-artifacts`, `analyzing-powershell-script-block-logging`, `building-attack-pattern-library-from-cti-reports` +13 more | +| [T1071.004](https://attack.mitre.org/techniques/T1071/004/) | `building-attack-pattern-library-from-cti-reports`, `building-c2-infrastructure-with-sliver-framework`, `hunting-for-beaconing-with-frequency-analysis`, `hunting-for-command-and-control-beaconing`, `hunting-for-dns-tunneling-with-zeek` +3 more | +| [T1090](https://attack.mitre.org/techniques/T1090/) | `implementing-mitre-attack-coverage-mapping`, `performing-purple-team-atomic-testing` | +| [T1090.001](https://attack.mitre.org/techniques/T1090/001/) | `performing-purple-team-atomic-testing` | +| [T1090.002](https://attack.mitre.org/techniques/T1090/002/) | `building-c2-infrastructure-with-sliver-framework`, `building-red-team-c2-infrastructure-with-havoc` | +| [T1090.004](https://attack.mitre.org/techniques/T1090/004/) | `hunting-for-domain-fronting-c2-traffic` | +| [T1095](https://attack.mitre.org/techniques/T1095/) | `hunting-for-command-and-control-beaconing`, `hunting-for-unusual-network-connections` | +| [T1102](https://attack.mitre.org/techniques/T1102/) | `hunting-for-living-off-the-cloud-techniques` | +| [T1105](https://attack.mitre.org/techniques/T1105/) | `analyzing-powershell-script-block-logging`, `building-attack-pattern-library-from-cti-reports`, `building-c2-infrastructure-with-sliver-framework`, `building-red-team-c2-infrastructure-with-havoc`, `detecting-fileless-attacks-on-endpoints` +7 more | +| [T1132](https://attack.mitre.org/techniques/T1132/) | `hunting-for-command-and-control-beaconing`, `performing-purple-team-atomic-testing` | +| [T1132.001](https://attack.mitre.org/techniques/T1132/001/) | `building-c2-infrastructure-with-sliver-framework`, `performing-purple-team-atomic-testing` | +| [T1219](https://attack.mitre.org/techniques/T1219/) | `performing-purple-team-atomic-testing` | +| [T1568](https://attack.mitre.org/techniques/T1568/) | `hunting-for-command-and-control-beaconing`, `implementing-mitre-attack-coverage-mapping` | +| [T1568.002](https://attack.mitre.org/techniques/T1568/002/) | `hunting-for-beaconing-with-frequency-analysis` | +| [T1571](https://attack.mitre.org/techniques/T1571/) | `hunting-for-unusual-network-connections`, `implementing-mitre-attack-coverage-mapping` | +| [T1572](https://attack.mitre.org/techniques/T1572/) | `building-c2-infrastructure-with-sliver-framework`, `hunting-for-command-and-control-beaconing`, `hunting-for-dns-tunneling-with-zeek`, `implementing-mitre-attack-coverage-mapping` | +| [T1573](https://attack.mitre.org/techniques/T1573/) | `analyzing-ransomware-network-indicators`, `hunting-for-beaconing-with-frequency-analysis`, `hunting-for-command-and-control-beaconing`, `implementing-mitre-attack-coverage-mapping`, `performing-purple-team-atomic-testing` | +| [T1573.001](https://attack.mitre.org/techniques/T1573/001/) | `performing-purple-team-atomic-testing` | +| [T1573.002](https://attack.mitre.org/techniques/T1573/002/) | `building-c2-infrastructure-with-sliver-framework`, `building-red-team-c2-infrastructure-with-havoc` | + +--- + +## 📤 Exfiltration + +**12 techniques covered** + +| Technique | Skills | +|:----------|:-------| +| [T1020](https://attack.mitre.org/techniques/T1020/) | `hunting-for-data-exfiltration-indicators` | +| [T1029](https://attack.mitre.org/techniques/T1029/) | `hunting-for-data-exfiltration-indicators` | +| [T1030](https://attack.mitre.org/techniques/T1030/) | `hunting-for-data-exfiltration-indicators` | +| [T1041](https://attack.mitre.org/techniques/T1041/) | `analyzing-campaign-attribution-evidence`, `analyzing-ransomware-network-indicators`, `building-attack-pattern-library-from-cti-reports`, `conducting-full-scope-red-team-engagement`, `conducting-malware-incident-response` +6 more | +| [T1048](https://attack.mitre.org/techniques/T1048/) | `building-attack-pattern-library-from-cti-reports`, `building-detection-rule-with-splunk-spl`, `conducting-full-scope-red-team-engagement`, `hunting-for-data-exfiltration-indicators`, `implementing-continuous-security-validation-with-bas` +2 more | +| [T1048.001](https://attack.mitre.org/techniques/T1048/001/) | `hunting-for-data-exfiltration-indicators` | +| [T1048.002](https://attack.mitre.org/techniques/T1048/002/) | `hunting-for-data-exfiltration-indicators` | +| [T1048.003](https://attack.mitre.org/techniques/T1048/003/) | `conducting-full-scope-red-team-engagement`, `hunting-for-data-exfiltration-indicators`, `hunting-for-dns-tunneling-with-zeek`, `implementing-continuous-security-validation-with-bas`, `implementing-mitre-attack-coverage-mapping` +2 more | +| [T1052](https://attack.mitre.org/techniques/T1052/) | `hunting-for-data-exfiltration-indicators` | +| [T1537](https://attack.mitre.org/techniques/T1537/) | `hunting-for-data-exfiltration-indicators`, `hunting-for-living-off-the-cloud-techniques`, `implementing-mitre-attack-coverage-mapping`, `implementing-threat-modeling-with-mitre-attack`, `performing-cloud-incident-containment-procedures` | +| [T1567](https://attack.mitre.org/techniques/T1567/) | `detecting-insider-threat-behaviors`, `hunting-for-data-exfiltration-indicators`, `hunting-for-living-off-the-cloud-techniques`, `implementing-continuous-security-validation-with-bas`, `performing-purple-team-atomic-testing` | +| [T1567.002](https://attack.mitre.org/techniques/T1567/002/) | `hunting-for-data-exfiltration-indicators`, `performing-purple-team-atomic-testing` | + +--- + +## 💥 Impact + +**6 techniques covered** + +| Technique | Skills | +|:----------|:-------| +| [T1485](https://attack.mitre.org/techniques/T1485/) | `hunting-for-shadow-copy-deletion`, `performing-purple-team-atomic-testing` | +| [T1486](https://attack.mitre.org/techniques/T1486/) | `analyzing-ransomware-network-indicators`, `building-attack-pattern-library-from-cti-reports`, `building-threat-hunt-hypothesis-framework`, `conducting-full-scope-red-team-engagement`, `hunting-for-shadow-copy-deletion` +7 more | +| [T1489](https://attack.mitre.org/techniques/T1489/) | `conducting-full-scope-red-team-engagement`, `performing-purple-team-atomic-testing` | +| [T1490](https://attack.mitre.org/techniques/T1490/) | `building-soc-playbook-for-ransomware`, `hunting-for-shadow-copy-deletion`, `performing-purple-team-atomic-testing`, `performing-purple-team-exercise` | +| [T1491](https://attack.mitre.org/techniques/T1491/) | `performing-purple-team-atomic-testing` | +| [T1491.002](https://attack.mitre.org/techniques/T1491/002/) | `performing-purple-team-atomic-testing` | + +--- + +## 🔧 Other / Cross-Tactic Techniques + +| Technique | Skills | +|:----------|:-------| +| T0157 | `exploiting-kerberoasting-with-impacket` | +| T0200 | `building-vulnerability-scanning-workflow`, `performing-authenticated-scan-with-openvas` | +| T0802 | `detecting-attacks-on-historian-servers` | +| T0809 | `detecting-attacks-on-historian-servers` | +| T0814 | `detecting-modbus-command-injection-attacks` | +| T0816 | `detecting-dnp3-protocol-anomalies` | +| T0830 | `detecting-modbus-protocol-anomalies` | +| T0831 | `detecting-modbus-protocol-anomalies` | +| T0832 | `detecting-attacks-on-historian-servers` | +| T0833 | `detecting-stuxnet-style-attacks` | +| T0836 | `detecting-modbus-command-injection-attacks`, `detecting-modbus-protocol-anomalies`, `detecting-stuxnet-style-attacks` | +| T0839 | `detecting-dnp3-protocol-anomalies`, `detecting-stuxnet-style-attacks` | +| T0843 | `detecting-modbus-command-injection-attacks`, `performing-s7comm-protocol-security-analysis` | +| T0847 | `detecting-stuxnet-style-attacks` | +| T0855 | `detecting-dnp3-protocol-anomalies`, `detecting-modbus-command-injection-attacks`, `detecting-modbus-protocol-anomalies` | +| T0856 | `detecting-stuxnet-style-attacks` | +| T0862 | `detecting-stuxnet-style-attacks` | +| T0866 | `detecting-stuxnet-style-attacks` | +| T0869 | `detecting-dnp3-protocol-anomalies` | +| T0881 | `performing-s7comm-protocol-security-analysis` | +| T0886 | `detecting-modbus-protocol-anomalies` | +| T1404 | `analyzing-android-malware-with-apktool` | +| T1417 | `analyzing-android-malware-with-apktool` | +| T1418 | `analyzing-android-malware-with-apktool` | +| T1553.006 | `analyzing-uefi-bootkit-persistence` | +| T1555 | `performing-credential-access-with-lazagne`, `performing-purple-team-atomic-testing` | +| T1555.003 | `performing-credential-access-with-lazagne`, `performing-purple-team-atomic-testing` | +| T1555.004 | `performing-credential-access-with-lazagne` | +| T1578 | `performing-cloud-incident-containment-procedures` | +| T1582 | `analyzing-android-malware-with-apktool` | +| T1611 | `detecting-container-escape-attempts`, `detecting-container-escape-with-falco-rules` | +| T1615 | `conducting-internal-reconnaissance-with-bloodhound-ce`, `exploiting-active-directory-with-bloodhound`, `performing-active-directory-bloodhound-analysis` | +| T1620 | `detecting-fileless-attacks-on-endpoints` | +| T5577 | `performing-physical-intrusion-assessment` | + +--- + +## How This Was Generated + +This coverage map was automatically generated by scanning all 753+ SKILL.md and agent.py files for MITRE ATT&CK technique IDs (pattern: `T####` and `T####.###`). Each technique was mapped to its parent tactic using the [MITRE ATT&CK Enterprise Matrix v16](https://attack.mitre.org/matrices/enterprise/). + +To regenerate: `python3 extract_attack.py` + +--- + +## MITRE ATLAS Coverage (v5.5.0) + +81 skills mapped to ATLAS adversarial ML techniques. + +Key techniques applied: +- AML.T0051 — LLM Prompt Injection (Execution) +- AML.T0054 — LLM Jailbreak (Privilege Escalation) +- AML.T0088 — Generate Deepfakes (AI Attack Staging) +- AML.T0010 — AI Supply Chain Compromise (Initial Access) +- AML.T0020 — Poison Training Data (Resource Development) +- AML.T0070 — RAG Poisoning (Persistence) +- AML.T0080 — AI Agent Context Poisoning (Persistence) +- AML.T0056 — Extract LLM System Prompt (Exfiltration) + +## MITRE D3FEND Coverage (v1.3) + +11 skills mapped to D3FEND defensive countermeasures. + +Countermeasures applied span D3FEND tactical categories: +Harden, Detect, Isolate, Deceive, Evict, Restore. +Each skill's d3fend_techniques field lists the top 5 most relevant +defensive countermeasures derived from the skill's ATT&CK technique tags. + +## NIST AI RMF Coverage (AI 100-1) + +85 skills mapped to NIST AI Risk Management Framework subcategories. + +Core functions covered: +- GOVERN: Organizational accountability for AI risk (GOVERN-1.1, GOVERN-6.1, GOVERN-6.2) +- MAP: AI risk identification and context (MAP-5.1, MAP-5.2, MAP-1.6) +- MEASURE: AI risk analysis and evaluation (MEASURE-2.5, MEASURE-2.7, MEASURE-2.8, MEASURE-2.11) +- MANAGE: AI risk response and recovery (MANAGE-2.4, MANAGE-3.1) + +GenAI-specific subcategories applied: GOVERN-6.1, GOVERN-6.2 (responsible deployment policies). + +--- + +

+ Part of Anthropic Cybersecurity Skills — 753+ open-source cybersecurity skills for AI agents +

\ No newline at end of file diff --git a/personas/_shared/anthropic-cybersecurity-skills/CITATION.cff b/personas/_shared/anthropic-cybersecurity-skills/CITATION.cff new file mode 100644 index 0000000..807b051 --- /dev/null +++ b/personas/_shared/anthropic-cybersecurity-skills/CITATION.cff @@ -0,0 +1,32 @@ +cff-version: 1.2.0 +message: "If you use this repository in your research, tools, or publications, please cite it as below." +type: software +title: "Anthropic-Cybersecurity-Skills" +abstract: > + A structured collection of 753 cybersecurity skills for AI agents, covering + penetration testing, digital forensics, threat intelligence, incident response, + cloud security, OT/SCADA security, AI security, and more. Each skill follows + a standardized format with YAML frontmatter metadata, step-by-step procedures, + tool commands, expected outputs, and MITRE ATT&CK mappings. Compatible with + Claude Code, GitHub Copilot, Cursor, Windsurf, Gemini CLI, and 20+ AI agent + platforms. +authors: + - name: "Mahipal" + email: mukuljangra5@gmail.com + alias: mukul975 +repository-code: "https://github.com/mukul975/Anthropic-Cybersecurity-Skills" +url: "https://github.com/mukul975/Anthropic-Cybersecurity-Skills" +license: Apache-2.0 +version: "1.1.0" +date-released: "2026-03-21" +keywords: + - cybersecurity + - AI agents + - skills + - penetration testing + - digital forensics + - threat intelligence + - incident response + - MITRE ATT&CK + - Claude Code + - open source diff --git a/personas/_shared/anthropic-cybersecurity-skills/CODE_OF_CONDUCT.md b/personas/_shared/anthropic-cybersecurity-skills/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..90a4a45 --- /dev/null +++ b/personas/_shared/anthropic-cybersecurity-skills/CODE_OF_CONDUCT.md @@ -0,0 +1,83 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, caste, color, religion, or sexual identity and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our community include: + +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience +* Focusing on what is best not just for us as individuals, but for the overall community + +Examples of unacceptable behavior include: + +* The use of sexualized language or imagery, and sexual attention or advances of any kind +* Trolling, insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or email address, without their explicit permission +* Other conduct which could reasonably be considered inappropriate in a professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at mukuljangra5@gmail.com. All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series of actions. + +**Consequence**: A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within the community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.1, available at [https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]. + +Community Impact Guidelines were inspired by [Mozilla's code of conduct enforcement ladder][Mozilla CoC]. + +For answers to common questions about this code of conduct, see the FAQ at [https://www.contributor-covenant.org/faq][FAQ]. Translations are available at [https://www.contributor-covenant.org/translations][translations]. + +[homepage]: https://www.contributor-covenant.org +[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html +[Mozilla CoC]: https://github.com/mozilla/diversity +[FAQ]: https://www.contributor-covenant.org/faq +[translations]: https://www.contributor-covenant.org/translations diff --git a/personas/_shared/anthropic-cybersecurity-skills/CONTRIBUTING.md b/personas/_shared/anthropic-cybersecurity-skills/CONTRIBUTING.md new file mode 100644 index 0000000..11fde20 --- /dev/null +++ b/personas/_shared/anthropic-cybersecurity-skills/CONTRIBUTING.md @@ -0,0 +1,74 @@ +# Contributing to Anthropic-Cybersecurity-Skills + +## How to add a new skill + +1. Create a new directory: `skills/your-skill-name/` +2. Add a `SKILL.md` file with required YAML frontmatter: + ```yaml + --- + name: your-skill-name + description: >- + Clear description of what this skill does and when + an AI agent should activate it. Include keywords. + domain: cybersecurity + subdomain: [category] + tags: [tag1, tag2, tag3] + version: "1.0" + author: your-github-username + license: Apache-2.0 + --- + ``` +3. Write clear, step-by-step instructions in the Markdown body using these sections: + - ## When to Use + - ## Prerequisites + - ## Workflow (numbered steps with real commands) + - ## Key Concepts (table) + - ## Tools & Systems + - ## Common Scenarios + - ## Output Format +4. (Optional) Add supporting files: + - `references/standards.md` — Real standard numbers, CVE refs, NIST/MITRE links + - `references/workflows.md` — Deep technical procedure + - `scripts/process.py` — Real working helper script + - `assets/template.md` — Real filled-in checklist/template +5. Submit a PR with title: `Add skill: your-skill-name` + +## Skill quality checklist +- [ ] Name is lowercase with hyphens (kebab-case), 1–64 characters +- [ ] Description is clear and includes agent-discovery keywords +- [ ] Instructions are actionable with real commands and tool names +- [ ] Domain and subdomain are set correctly +- [ ] Tags include relevant tools, frameworks, and techniques + +## Subdomains +Choose the most appropriate subdomain for your skill: +- web-application-security +- network-security +- penetration-testing +- red-teaming +- digital-forensics +- malware-analysis +- threat-intelligence +- cloud-security +- container-security +- identity-access-management +- cryptography +- vulnerability-management +- compliance-governance +- zero-trust-architecture +- ot-ics-security +- devsecops +- soc-operations +- incident-response +- phishing-defense +- ransomware-defense +- api-security +- mobile-security +- endpoint-security +- threat-hunting + +## Code of Conduct +This project follows the [Contributor Covenant](CODE_OF_CONDUCT.md). By participating, you agree to uphold this code. + +## License +By contributing, you agree that your contributions will be licensed under Apache-2.0. diff --git a/personas/_shared/anthropic-cybersecurity-skills/LICENSE b/personas/_shared/anthropic-cybersecurity-skills/LICENSE new file mode 100644 index 0000000..d885118 --- /dev/null +++ b/personas/_shared/anthropic-cybersecurity-skills/LICENSE @@ -0,0 +1,201 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to the Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by the Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding any notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. Please do not remove or change + the license header comment from a contributed file except when + necessary. + + Copyright 2026 mukul975 + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/personas/_shared/anthropic-cybersecurity-skills/README.md b/personas/_shared/anthropic-cybersecurity-skills/README.md new file mode 100644 index 0000000..ca75208 --- /dev/null +++ b/personas/_shared/anthropic-cybersecurity-skills/README.md @@ -0,0 +1,358 @@ +

+ Anthropic Cybersecurity Skills +

+ +
+ +# Anthropic Cybersecurity Skills + +### The largest open-source cybersecurity skills library for AI agents + +[![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg?style=flat-square)](LICENSE) +[![Skills](https://img.shields.io/badge/skills-754-brightgreen?style=flat-square)](#whats-inside--26-security-domains) +[![Frameworks](https://img.shields.io/badge/frameworks-5-orange?style=flat-square)](#five-frameworks-one-skill-library) +[![Domains](https://img.shields.io/badge/domains-26-9cf?style=flat-square)](#whats-inside--26-security-domains) +[![Platforms](https://img.shields.io/badge/platforms-26%2B-blueviolet?style=flat-square)](#compatible-platforms) +[![GitHub stars](https://img.shields.io/github/stars/mukul975/Anthropic-Cybersecurity-Skills?style=flat-square)](https://github.com/mukul975/Anthropic-Cybersecurity-Skills/stargazers) +[![GitHub forks](https://img.shields.io/github/forks/mukul975/Anthropic-Cybersecurity-Skills?style=flat-square)](https://github.com/mukul975/Anthropic-Cybersecurity-Skills/network/members) +[![Last Commit](https://img.shields.io/github/last-commit/mukul975/Anthropic-Cybersecurity-Skills?style=flat-square)](https://github.com/mukul975/Anthropic-Cybersecurity-Skills/commits/main) +[![agentskills.io](https://img.shields.io/badge/standard-agentskills.io-ff6600?style=flat-square)](https://agentskills.io) +[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](CONTRIBUTING.md) + +**754 production-grade cybersecurity skills · 26 security domains · 5 framework mappings · 26+ AI platforms** + +[Get Started](#quick-start) · [What's Inside](#whats-inside--26-security-domains) · [Frameworks](#five-frameworks-one-skill-library) · [Platforms](#compatible-platforms) · [Contributing](#contributing) + +
+ +--- + +> ⚠️ **Community Project** — This is an independent, community-created project. Not affiliated with Anthropic PBC. + +## Give any AI agent the security skills of a senior analyst + +A junior analyst knows which Volatility3 plugin to run on a suspicious memory dump, which Sigma rules catch Kerberoasting, and how to scope a cloud breach across three providers. **Your AI agent doesn't — unless you give it these skills.** + +This repo contains **754 structured cybersecurity skills** spanning **26 security domains**, each following the [agentskills.io](https://agentskills.io) open standard. Every skill is mapped to **five industry frameworks** — MITRE ATT&CK, NIST CSF 2.0, MITRE ATLAS, MITRE D3FEND, and NIST AI RMF — making this the only open-source skills library with unified cross-framework coverage. Clone it, point your agent at it, and your next security investigation gets expert-level guidance in seconds. + +## Five frameworks, one skill library + +No other open-source skills library maps every skill to all five frameworks. One skill, five compliance checkboxes. + +| Framework | Version | Scope in this repo | What it maps | +|---|---|---|---| +| [MITRE ATT&CK](https://attack.mitre.org) | v18 | 14 tactics · 200+ techniques | Adversary behaviors and TTPs | +| [NIST CSF 2.0](https://www.nist.gov/cyberframework) | 2.0 | 6 functions · 22 categories | Organizational security posture | +| [MITRE ATLAS](https://atlas.mitre.org) | v5.4 | 16 tactics · 84 techniques | AI/ML adversarial threats | +| [MITRE D3FEND](https://d3fend.mitre.org) | v1.3 | 7 categories · 267 techniques | Defensive countermeasures | +| [NIST AI RMF](https://airc.nist.gov/AI_RMF) | 1.0 | 4 functions · 72 subcategories | AI risk management | + +**Example — a single skill maps across all five:** + +| Skill | ATT&CK | NIST CSF | ATLAS | D3FEND | AI RMF | +|---|---|---|---|---|---| +| `analyzing-network-traffic-of-malware` | T1071 | DE.CM | AML.T0047 | D3-NTA | MEASURE-2.6 | + +## Quick start + +```bash +# Option 1: npx (recommended) +npx skills add mukul975/Anthropic-Cybersecurity-Skills + +# Option 2: Git clone +git clone https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git +cd Anthropic-Cybersecurity-Skills +``` + +Works immediately with Claude Code, GitHub Copilot, OpenAI Codex CLI, Cursor, Gemini CLI, and any [agentskills.io](https://agentskills.io)-compatible platform. + +## Why this exists + +The cybersecurity workforce gap hit **4.8 million unfilled roles** globally in 2024 (ISC2). AI agents can help close that gap — but only if they have structured domain knowledge to work from. Today's agents can write code and search the web, but they lack the practitioner playbooks that turn a generic LLM into a capable security analyst. + +Existing security tool repos give you wordlists, payloads, or exploit code. None of them give an AI agent the structured decision-making workflow a senior analyst follows: when to use each technique, what prerequisites to check, how to execute step-by-step, and how to verify results. That is the gap this project fills. + +**Anthropic Cybersecurity Skills** is not a collection of scripts or checklists. It is an **AI-native knowledge base** built from the ground up for the agentskills.io standard — YAML frontmatter for sub-second discovery, structured Markdown for step-by-step execution, and reference files for deep technical context. Every skill encodes real practitioner workflows, not generated summaries. + +## What's inside — 26 security domains + +| Domain | Skills | Key capabilities | +|---|---|---| +| Cloud Security | 60 | AWS, Azure, GCP hardening · CSPM · cloud forensics | +| Threat Hunting | 55 | Hypothesis-driven hunts · LOTL detection · behavioral analytics | +| Threat Intelligence | 50 | STIX/TAXII · MISP · feed integration · actor profiling | +| Web Application Security | 42 | OWASP Top 10 · SQLi · XSS · SSRF · deserialization | +| Network Security | 40 | IDS/IPS · firewall rules · VLAN segmentation · traffic analysis | +| Malware Analysis | 39 | Static/dynamic analysis · reverse engineering · sandboxing | +| Digital Forensics | 37 | Disk imaging · memory forensics · timeline reconstruction | +| Security Operations | 36 | SIEM correlation · log analysis · alert triage | +| Identity & Access Management | 35 | IAM policies · PAM · zero trust identity · Okta · SailPoint | +| SOC Operations | 33 | Playbooks · escalation workflows · metrics · tabletop exercises | +| Container Security | 30 | K8s RBAC · image scanning · Falco · container forensics | +| OT/ICS Security | 28 | Modbus · DNP3 · IEC 62443 · historian defense · SCADA | +| API Security | 28 | GraphQL · REST · OWASP API Top 10 · WAF bypass | +| Vulnerability Management | 25 | Nessus · scanning workflows · patch prioritization · CVSS | +| Incident Response | 25 | Breach containment · ransomware response · IR playbooks | +| Red Teaming | 24 | Full-scope engagements · AD attacks · phishing simulation | +| Penetration Testing | 23 | Network · web · cloud · mobile · wireless pentesting | +| Endpoint Security | 17 | EDR · LOTL detection · fileless malware · persistence hunting | +| DevSecOps | 17 | CI/CD security · code signing · Terraform auditing | +| Phishing Defense | 16 | Email authentication · BEC detection · phishing IR | +| Cryptography | 14 | TLS · Ed25519 · certificate transparency · key management | +| Zero Trust Architecture | 13 | BeyondCorp · CISA maturity model · microsegmentation | +| Mobile Security | 12 | Android/iOS analysis · mobile pentesting · MDM forensics | +| Ransomware Defense | 7 | Precursor detection · response · recovery · encryption analysis | +| Compliance & Governance | 5 | CIS benchmarks · SOC 2 · regulatory frameworks | +| Deception Technology | 2 | Honeytokens · breach detection canaries | + +## How AI agents use these skills + +Each skill costs **~30 tokens to scan** (frontmatter only) and **500–2,000 tokens to fully load** (complete workflow). This progressive disclosure architecture lets agents search all 754 skills in a single pass without blowing context windows. + +``` +User prompt: "Analyze this memory dump for signs of credential theft" + +Agent's internal process: + + 1. Scans 754 skill frontmatters (~30 tokens each) + → identifies 12 relevant skills by matching tags, description, domain + + 2. Loads top 3 matches: + • performing-memory-forensics-with-volatility3 + • hunting-for-credential-dumping-lsass + • analyzing-windows-event-logs-for-credential-access + + 3. Executes the structured Workflow section step-by-step + → runs Volatility3 plugins, checks LSASS access patterns, + correlates with event log evidence + + 4. Validates results using the Verification section + → confirms IOCs, maps findings to ATT&CK T1003 (Credential Dumping) +``` + +**Without these skills**, the agent guesses at tool commands and misses critical steps. **With them**, it follows the same playbook a senior DFIR analyst would use. + +## Skill anatomy + +Every skill follows a consistent directory structure: + +``` +skills/performing-memory-forensics-with-volatility3/ +├── SKILL.md ← Skill definition (YAML frontmatter + Markdown body) +├── references/ +│ ├── standards.md ← MITRE ATT&CK, ATLAS, D3FEND, NIST mappings +│ └── workflows.md ← Deep technical procedure reference +├── scripts/ +│ └── process.py ← Working helper scripts +└── assets/ + └── template.md ← Filled-in checklists and report templates +``` + + +### YAML frontmatter (real example) + +```yaml +--- +name: performing-memory-forensics-with-volatility3 +description: >- + Analyze memory dumps to extract running processes, network connections, + injected code, and malware artifacts using the Volatility3 framework. +domain: cybersecurity +subdomain: digital-forensics +tags: [forensics, memory-analysis, volatility3, incident-response, dfir] +atlas_techniques: [AML.T0047] +d3fend_techniques: [D3-MA, D3-PSMD] +nist_ai_rmf: [MEASURE-2.6] +nist_csf: [DE.CM-01, RS.AN-03] +version: "1.2" +author: mukul975 +license: Apache-2.0 +--- +``` + + +### Markdown body sections + +```markdown +## When to Use +Trigger conditions — when should an AI agent activate this skill? + +## Prerequisites +Required tools, access levels, and environment setup. + +## Workflow +Step-by-step execution guide with specific commands and decision points. + +## Verification +How to confirm the skill was executed successfully. +``` + +Frontmatter fields: `name` (kebab-case, 1–64 chars), `description` (keyword-rich for agent discovery), `domain`, `subdomain`, `tags`, `atlas_techniques` (MITRE ATLAS IDs), `d3fend_techniques` (MITRE D3FEND IDs), `nist_ai_rmf` (NIST AI RMF references), `nist_csf` (NIST CSF 2.0 categories). MITRE ATT&CK technique mappings are documented in each skill's `references/standards.md` file and in the ATT&CK Navigator layer included with releases. + +
+📊 MITRE ATT&CK Enterprise coverage — all 14 tactics + +  + +| Tactic | ID | Coverage | Key skills | +|---|---|---|---| +| Reconnaissance | TA0043 | Strong | OSINT, subdomain enumeration, DNS recon | +| Resource Development | TA0042 | Moderate | Phishing infrastructure, C2 setup detection | +| Initial Access | TA0001 | Strong | Phishing simulation, exploit detection, forced browsing | +| Execution | TA0002 | Strong | PowerShell analysis, fileless malware, script block logging | +| Persistence | TA0003 | Strong | Scheduled tasks, registry, service accounts, LOTL | +| Privilege Escalation | TA0004 | Strong | Kerberoasting, AD attacks, cloud privilege escalation | +| Defense Evasion | TA0005 | Strong | Obfuscation, rootkit analysis, evasion detection | +| Credential Access | TA0006 | Strong | Mimikatz detection, pass-the-hash, credential dumping | +| Discovery | TA0007 | Moderate | BloodHound, AD enumeration, network scanning | +| Lateral Movement | TA0008 | Strong | SMB exploits, lateral movement detection with Splunk | +| Collection | TA0009 | Moderate | Email forensics, data staging detection | +| Command and Control | TA0011 | Strong | C2 beaconing, DNS tunneling, Cobalt Strike analysis | +| Exfiltration | TA0010 | Strong | DNS exfiltration, DLP controls, data loss detection | +| Impact | TA0040 | Strong | Ransomware defense, encryption analysis, recovery | + +An **ATT&CK Navigator layer file** is included in the [v1.0.0 release assets](https://github.com/mukul975/Anthropic-Cybersecurity-Skills/releases/tag/v1.0.0) for visual coverage mapping. + +> **Note:** ATT&CK v19 lands April 28, 2026 — splitting Defense Evasion (TA0005) into two new tactics: *Stealth* and *Impair Defenses*. Skill mappings will be updated in a forthcoming release. + +
+ +
+📊 NIST CSF 2.0 alignment — all 6 functions + +  + +| Function | Skills | Examples | +|---|---|---| +| **Govern (GV)** | 30+ | Risk strategy, policy frameworks, roles & responsibilities | +| **Identify (ID)** | 120+ | Asset discovery, threat landscape assessment, risk analysis | +| **Protect (PR)** | 150+ | IAM hardening, WAF rules, zero trust, encryption | +| **Detect (DE)** | 200+ | Threat hunting, SIEM correlation, anomaly detection | +| **Respond (RS)** | 160+ | Incident response, forensics, breach containment | +| **Recover (RC)** | 40+ | Ransomware recovery, BCP, disaster recovery | + +NIST CSF 2.0 (February 2024) added the **Govern** function and expanded scope from critical infrastructure to all organizations. Skill mappings align to all 22 categories and reference 106 subcategories. + +
+ +
+📊 Framework deep dive — ATLAS, D3FEND, AI RMF + +  + +### MITRE ATLAS v5.4 — AI/ML adversarial threats +ATLAS maps adversarial tactics, techniques, and case studies specific to AI and machine learning systems. Version 5.4 covers **16 tactics and 84 techniques** including agentic AI attack vectors added in late 2025: AI agent context poisoning, tool invocation abuse, MCP server compromises, and malicious agent deployment. Skills mapped to ATLAS help agents identify and defend against threats to ML pipelines, model weights, inference APIs, and autonomous workflows. + +### MITRE D3FEND v1.3 — Defensive countermeasures +D3FEND is an NSA-funded knowledge graph of **267 defensive techniques** organized across 7 tactical categories: Model, Harden, Detect, Isolate, Deceive, Evict, and Restore. Built on OWL 2 ontology, it uses a shared Digital Artifact layer to bidirectionally map defensive countermeasures to ATT&CK offensive techniques. Skills tagged with D3FEND identifiers let agents recommend specific countermeasures for detected threats. + +### NIST AI RMF 1.0 + GenAI Profile (AI 600-1) +The AI Risk Management Framework defines 4 core functions — Govern, Map, Measure, Manage — with **72 subcategories** for trustworthy AI development. The GenAI Profile (AI 600-1, July 2024) adds **12 risk categories** specific to generative AI, from confabulation and data privacy to prompt injection and supply chain risks. Colorado's AI Act (effective February 2026) provides a **legal safe harbor** for organizations complying with NIST AI RMF, making these mappings directly relevant to regulatory compliance. + +
+ +## Compatible platforms + +**AI code assistants** +Claude Code (Anthropic) · GitHub Copilot (Microsoft) · Cursor · Windsurf · Cline · Aider · Continue · Roo Code · Amazon Q Developer · Tabnine · Sourcegraph Cody · JetBrains AI + +**CLI agents** +OpenAI Codex CLI · Gemini CLI (Google) + +**Autonomous agents** +Devin · Replit Agent · SWE-agent · OpenHands + +**Agent frameworks & SDKs** +LangChain · CrewAI · AutoGen · Semantic Kernel · Haystack · Vercel AI SDK · Any MCP-compatible agent + +All platforms that support the [agentskills.io](https://agentskills.io) standard can load these skills with zero configuration. + +## What people are saying + +> *"A database of real, organized security skills that any AI agent can plug into and use. Not tutorials. Not blog posts."* +> — **[Hasan Toor (@hasantoxr)](https://x.com/hasantoxr/status/2033193922349179249)**, AI/tech creator + +> *"This is not a random collection of security scripts. It's a structured operational knowledge base designed for AI-driven security workflows."* +> — **[fazal-sec](https://fazal-sec.medium.com/claude-skills-ai-powered-cybersecurity-the-complete-guide-to-building-intelligent-security-7bb7e9d14c8e)**, Medium + +## Featured in + +| Where | Type | Link | +|---|---|---| +| **awesome-agent-skills** | Awesome List (1,000+ skills index) | [VoltAgent/awesome-agent-skills](https://github.com/VoltAgent/awesome-agent-skills) | +| **awesome-ai-security** | Awesome List (AI security tools) | [ottosulin/awesome-ai-security](https://github.com/ottosulin/awesome-ai-security) | +| **awesome-codex-cli** | Awesome List (Codex CLI resources) | [RoggeOhta/awesome-codex-cli](https://github.com/RoggeOhta/awesome-codex-cli) | +| **SkillsLLM** | Skills directory & marketplace | [skillsllm.com/skill/anthropic-cybersecurity-skills](https://skillsllm.com/skill/anthropic-cybersecurity-skills) | +| **Openflows** | Signal analysis & tracking | [openflows.org](https://openflows.org/currency/currents/anthropic-cybersecurity-skills/) | +| **NeverSight skills_feed** | Automated skills index | [NeverSight/skills_feed](https://github.com/NeverSight/skills_feed) | + +## Star history + + + + + + Star History Chart + + + +## Releases + +| Version | Date | Highlights | +|---|---|---| +| [v1.0.0](https://github.com/mukul975/Anthropic-Cybersecurity-Skills/releases/tag/v1.0.0) | March 11, 2026 | 734 skills · 26 domains · MITRE ATT&CK + NIST CSF 2.0 mapping · ATT&CK Navigator layer | + +Skills have continued to grow on `main` since v1.0.0 — the library now contains **754 skills** with **5-framework mapping** (MITRE ATLAS, D3FEND, and NIST AI RMF added post-release). Check [Releases](https://github.com/mukul975/Anthropic-Cybersecurity-Skills/releases) for the latest tagged version. + +## Contributing + +This project grows through community contributions. Here is how to get involved: + +**Add a new skill** — Domains like Deception Technology (2 skills) and Compliance & Governance (5 skills) need the most help. Follow the template in [CONTRIBUTING.md](CONTRIBUTING.md) and submit a PR with the title `Add skill: your-skill-name`. + +**Improve existing skills** — Add framework mappings, fix workflows, update tool references, or contribute scripts and templates. + +**Report issues** — Found an inaccurate procedure or broken script? [Open an issue](https://github.com/mukul975/Anthropic-Cybersecurity-Skills/issues). + +Every PR is reviewed for technical accuracy and agentskills.io standard compliance within 48 hours. Check [good first issues](https://github.com/mukul975/Anthropic-Cybersecurity-Skills/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22) for a starting point. + +This project follows the [Contributor Covenant](https://www.contributor-covenant.org/). By participating, you agree to uphold this code. + +## Community + +💬 [Discussions](https://github.com/mukul975/Anthropic-Cybersecurity-Skills/discussions) — Questions, ideas, and roadmap conversations +🐛 [Issues](https://github.com/mukul975/Anthropic-Cybersecurity-Skills/issues) — Bug reports and feature requests +🔒 [Security Policy](SECURITY.md) — Responsible disclosure process (48-hour acknowledgment) + +## Citation + +If you use this project in research or publications: + +```bibtex +@software{anthropic_cybersecurity_skills, + author = {Jangra, Mahipal}, + title = {Anthropic Cybersecurity Skills}, + year = {2026}, + url = {https://github.com/mukul975/Anthropic-Cybersecurity-Skills}, + license = {Apache-2.0}, + note = {754 structured cybersecurity skills for AI agents, + mapped to MITRE ATT\&CK, NIST CSF 2.0, MITRE ATLAS, + MITRE D3FEND, and NIST AI RMF} +} +``` + +## License + +This project is licensed under the [Apache License 2.0](LICENSE). You are free to use, modify, and distribute these skills in both personal and commercial projects. + +--- + +
+ +**If this project helps your security work, consider giving it a ⭐** + +[⭐ Star](https://github.com/mukul975/Anthropic-Cybersecurity-Skills/stargazers) · [🍴 Fork](https://github.com/mukul975/Anthropic-Cybersecurity-Skills/fork) · [💬 Discuss](https://github.com/mukul975/Anthropic-Cybersecurity-Skills/discussions) · [📝 Contribute](CONTRIBUTING.md) + +Community project by [@mukul975](https://github.com/mukul975). Not affiliated with Anthropic PBC. + +
diff --git a/personas/_shared/anthropic-cybersecurity-skills/SECURITY.md b/personas/_shared/anthropic-cybersecurity-skills/SECURITY.md new file mode 100644 index 0000000..e845925 --- /dev/null +++ b/personas/_shared/anthropic-cybersecurity-skills/SECURITY.md @@ -0,0 +1,47 @@ +# Security Policy + +## Supported Versions + +All skill content in this repository is covered by this security policy. + +| Component | Supported | +|-----------|-----------| +| Skill definitions (SKILL.md files) | Yes | +| Scripts and automation | Yes | +| Documentation | Yes | + +## Reporting a Vulnerability + +If you discover a security issue with any skill's scripts, instructions, or content, please report it responsibly: + +1. **Do not** open a public issue +2. Use GitHub's private security advisory: [Report a vulnerability](https://github.com/mukul975/Anthropic-Cybersecurity-Skills/security/advisories/new) +3. Include in your report: + - Affected skill name and file path + - Nature of the vulnerability + - Potential impact + - Steps to reproduce (if applicable) + - Suggested fix (if you have one) + +## Response Timeline + +- **Initial acknowledgment:** Within 48 hours +- **Assessment and triage:** Within 1 week +- **Fix or mitigation:** Based on severity, typically within 2 weeks + +## Scope + +The following are in scope for security reports: + +- Skills that contain commands or scripts that could cause unintended harm +- Instructions that could lead to unauthorized access if followed incorrectly +- Sensitive data accidentally included in skill content +- Dependencies or external references that have become compromised + +## Recognition + +We credit responsible disclosures in our changelog. If you report a valid security issue, we will acknowledge your contribution unless you prefer to remain anonymous. + +## Contact + +For security matters that cannot be reported through GitHub's advisory system, reach out via the repository's discussion forum. diff --git a/personas/_shared/anthropic-cybersecurity-skills/assets/.gitkeep b/personas/_shared/anthropic-cybersecurity-skills/assets/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/personas/_shared/anthropic-cybersecurity-skills/assets/banner.png b/personas/_shared/anthropic-cybersecurity-skills/assets/banner.png new file mode 100644 index 0000000000000000000000000000000000000000..8697e71600cca120fda5a83f55155086b722f1a3 GIT binary patch literal 678283 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 W}e zqegsT^$6kFoYHx1NHkv!${Z)}i@JBTdKhK&V6%wL0-8vNfC`BeWR~2T&2W7K17*f9 zj)=KDlKe#zldZ)Q15VcNr{LYEkbn;NYYJ+s^X-v*^kznE39U-=RXd~N?ItrjH#267XbnTi;o~YN-1hvqcxPwv4FQy=se)3p3bxBxN|2yLasdPdH&fJ3CMK zX%y$f87Jm!%z>7M)u5<+_E#sJvvPG8JzEx;4vc$KCfO;8ok(rv*ndmrT$#R3JD#f{ zzWG7G(N1|M+hicHUGkaG-G48yTg#&@ySl$d{D{8pG&coV%}tcLW|v|)+!Xy0&MB2{ zPX>l4i5?5a8pXJw{^Iq8i&=KPPq{U#$c-?1<~e5OB*@dtdVuO*FY{12&R#Irq$|gv z%{;oSRJZ-MSZG5gN4%Kx7{K2e_J9=ooV0WHRP=TGwN!7yF+DkV{b3ro@g$(9oS-Zv zm+_jW0hUn7NXlKIv^YY-UT*rk(bBM5)6eP&!Lz?U+_tRyaN)x5@m1eos&9*;$SEHB zSu?b48c+C@)Gik)G^;9jZ$8=sL|Itb_(Xdpi>Vfty=$HIqYme5V$=FXVc47x0QyJ8 z#W9HHYS~kZ{p>9E9>15VpzSR`w?o<*GEEq*y^$~Fru*-^D+$pQ7k4low1>hO7i1QC zmM(I^fIXVGE5CLNU0i&plgmBI0Nq_BXfbmvJF(6r3>8K6u&7By5xD017PXFvwgsVg zr`Hx1B6ACd3(UKveLXzr+E`Iec;^*6nT85!mYp0UJ|__W{80q@{h zAn19#x8&eFTsi%r!cEfP<*YA7u1<>Ikx`7{aZfhv4=GLsHD?8cetu>YUmjh6CnRTh znB3MK$+S2xO;TP+uVNAy7>FgYQRxyBgD#6G!*<0Bf)0L_DP3_Y?L3S&sfca-7zpwq zJVu~_FD+yrs1VL12tX-B|b3tl-Y4Vr#OxGu{}WJP*}J zUd~--bGK%?Yoc?QXoFCdzL*KA5K_-u0iRYE0GK#})7o?z`4}8y-uyeOj5cE`A&lv# zhi>3JbHQ;*#lach7)ISA1KKT^;y+2`wWzO+7PzQV?wbh}l%Sh-#2ed$g!EDV zh;+$36)7CCLi_d<2I1^y>SWXLD_Z<+2Lf5nM9wCc4T zx>!_8;!&UFh0HQ*&~zXi9v1!_ebr#RUHEnI)LJ34-Pj#&x;59lL@sS^Q~mqYhMJ!8 zrXg+#Ro z{ha74E7aar|0E$WsAqDimkQ_8nMqH=g|GB@(bkp>bQ>Pj1X;)GyGnBWqtr9u_su83 zPmm}wydQIg($FYLsaLk@L<7jF6oEiDi1$igy;aDaHIsl3C3M{KVrjnXirusOYE*o! z_4c8$WQ$c(02YKmZA?yjw3D*|7L^F0-uP5{Dbm1n%Km0&EHLNvn3jjT&kAypC^om` z)w7T~vU8fI9K!^W$aJ72c`E6&ZAoD#Vj$1e(fO^;{8)nw%r2<7{EX&pBC9xw>n8_4 zbG#Qj!}ITO5BbKlf4ZuB&z~Ma$30q+ZZw)V!a6D(cEl6ERsZVj3C{EU zJEPgs_y8ghRQCUB%+}TVdhrn-i(d}R&0oJPv%YJa`6-un;3gh^P7W#x^kiD3oOFd5 zDejxL8rqwd7VqR)7^WJy<8kGse3yGWoGqhvplVCBQpriG@S_*ZU{s|wOs4)upe)eJ z02wzw@v@HDE(&2TFDxh`DKN#Y#*Le}@^sTdQKZGgGaK*P+uDLq#?3vb<|^Nf`u#>R zf}D-)<8?0#?|I35f?UPFnRi8%>J;qZCw71EMqkQRyA@B%tNjjWNdmJxBaC#Dq2;R7 zRs%A#ZIScOEL99Vh?!n$5r0(*T!#cqNYt2TC>uNhMtUu~1rt*6ZSYlVvi);pPCSKy@SJ z+-jwiR7W%tZ}aFaY^D2$#Rj%h8tl&4h+c1r4dP;cnk7(4sqa#@^jl=qUmyjN+3>|3 z2NI#)#4vQq6)7wBj^I>QK!vK0Lp$jFV70xudHKSIX~8CPWJs@8`sXp9+J~K1N568y zZ2gv=@$1jm2Cq^+|204Ok0+~zcv5vD(P@a_S~8B*x!r7jdyF)C7dF*(NUK zyuS$^b^UEX$$eer<`Jcf7(L>EL8djSBi`E+6E|*DeP>OI!{^w9<;<-4`u;319`yX^ z_boy|PJx?X&I$VIm|XD(cd@NZeP2_Gfp8qz3%Wz=UlD^p`a>p8tFB$$o;&x6vU8+< z{!;Ze6szh~ezF{N3{wP|LQftj0l1&H^l# z;-K#NL|*Z@)z$|7ZPFBu+Gtq};vv#dt~cNrb1Jb0Qmy_^HEMjfXq@7O)e1Ysf2ur_3uYmesOZom%-W- zk|CT#QC#P)jSHTQ5*6R*^uX|hadxxAbX?1&ZUyO7|z z_V5o6=hEG0W!Mm`2}5glpVotrJLBMuw32k3J;?Cz^-&~&xNdkovqh7w(<20yuXs2x9qmn}KP}#MH54+YWymv;*PjsDMrc3Up0`UTYkyLtUMGkTIQ&IoL9&y) z-w)^snM|{qB7AO|4v~*&Bn{}QC~`ULXYncLRxbP8Nh!PFN_P$`JS3fe$zaj{q;UqJ z?ojDm{vXf36o&?fOEZAuMbj9Y24AIYVs+mTLE`ZRXmqM&u4-9mKkVY|NRaKIbq}=`82ru^m5$a&mHpAdVgW zufp(Xt_fK^4Bz4HR!H1sM+Dx_ccZU@vMENbCkiFy9Yv2cBKJBu2dRIPyE?|$Fi8al z2UBu!ygKOw$;fy?ZGbWsOg{(SOLjSSrb5a^L%Hxmpp>@mVCZ0eZfaju$rHfzQu{!;JoYNyg^qLg)lm{*Wa%@XY;RQPOk55Y+inN?u+PM3=0U5ZjFk< zyS&uHoMCRN5h3I4N~c|#mJbV88%t3a)7S-#Bt@^hXDqp_BY>o1xwYdR1we)ip?J&u zSwZ&wMZyO+@g8eY$O8unAhA&kK$zN>>N0d~+OGU;FA|IGOioT_6%Y?m^q*ZtCDhmV zpY84LcV={)(}Pg+=znG43(!LX_L>zRm^(l zk_6J7iXnpcSnE=K9I@lGbRUN)Fe2=H=@$jp z5qm)B64XSZ*?4Q-8sYbvbeN=aIj#0w_IybzIhE_y26(->xrw!s@C+I;Q$nAm)ISEr!l`VL}vN3!h?3 z!a^m=_hu=xDu-=-wH*mNg$6vF#wuNIS6bd5!#%=JDhu)yE!;M;JXhNSkPpL*-FxHc zzuk9Pdg14c=;Q7#?3MaPRl5kO?W(%uA>Dd02(|8Xv>2qsDxx$qjwICSEe(EuZUn%p zbIf7Sv?PZ_|Mtc2h8;;5jlqq zjUkM8>4fRo{bso4E4{Bwo5K?;J8=F>7OEj}6ZU(W{{>UtwXI}K*N>C%AR5I+!$)MMobLa)m!DJljzqS-T?mix>gApI?ND1-xv$2A* zWf9_uH`LsP0Z#Bpf6+cd2fjbyL{3s$UZJ6Z=X+Gi5fG$3q43q-);Nb@5>8Lrk zVp~^e)i&};?8n~?qj01fG2eNF<2U?KJ6GL(4j;R3Um0^#ji13qpCuOJFS`SPfp{>J zg?vy;IO9p4{8I2A_IV3wvnRd#wl;olyf<93*0D5 z6IKHQpA&mE1f9TEGrA7wLAPb#1ISiNp2zq&bbN9hC(|_tk7fVp=n@)`UmwvgniwmP zqI7;U*>XE0f;nhVT+7XMb9fjS49}YY?@^-A_moio<*E=bocasc=!F7c(R)D->+AQa zld!YkGuzE#`{&)^*5MGndR}<%)11c;lqQb*LCY8(9$mAK2JLnq0Kg@4&H-e;U!WE7 z=YWnk&2zMRdk6amBTw{e!jq6*!TwEoP16#cx9px_tB06fFb0W%OWC&m^53(O{OUK)djN%+aB8>K%!tWgv9En{#DJKg8gj+&&*t2+N&h>b z-@bh6QD!`bq)Ic?pL#M7HaI@_kEQKdm_%(l{Q>6}9C-M)$i4b>o- zR_Glo3QF=Aal+8oV$Q|w`(R$mKi>UgTYsqJ!1-}qjo9QGwwrq_ zJap~PbYv}(8{!q-MG&~k3cF#%Z0lKuY<`A&tleKab_Zg5Zx!%xXxP~~*k4UwUWzy8 zgHK?tR+!T6)A_Y?%>7?5Qn33Hk(9#$Qdn|t&gli_Ov)3%;OxiD#$I=j0Zk|`&9|&L zf4zD0s-zK9oku1V)V@;2k}7>=M?^q&_+zu*HcT7}fmBjXSMdo+m&GydJ5Y8=N=H02OdlAPTosr6+0$+LSSN{P0F*Iw_Z>QfFR#dIt* z1}r8Rq$4fSj;^;Jil`9J0Ml_~Y5X1eug^uV%GW%rF-;V<#xZ7(MVy^D%A9L2oX++F z6@s?!?B-u_$SbvZA8)@wIA{B(FUumv1KMvld&7|HLf%fx z2UuNy$xy@`)mx#LjP(#Oc{ZUmqEA81ib@K1OU7Nnm=i^_ostoq}S@dK?G1H%#fzgP@Fozd|Tn%}HZs^f2$9 zT|-yfA(>kxC0kMbB4KE8ar=+0!tWYDEQ_hC~tB2$96&V>0jl=PPIa7m8)#$a< z)qrim5=GIlbGMw|?K3IQwkHKz{cyFN4&$QhQuhaDF5c_KbshcmZq+6T@gq$0jP4UD zd4EZrws7nDzMu5av6PX!m}#q$^=*&GQMK5ZuZ;XG#oRF)kGZ)y8#{X>B}@X>7l(S% z^sqFDO(;kcM!6*;A5gnEp>POMk|y$gC>wHY5dAIlhyh1s?s4@c85!=73|_L6<_E7W z2q#(w)L&Pa!tz##X_o|TiPSR0|irvU%tD6Jb6blS?{T;S; zFAs)~qbtEzv*QnQVl21|V;6DOLT#2he0tSHTU9N=;1Ltz<$s^=Z9%%7ZJbloHizUOK+@`$)!FAuE=v?7LP8t{-53 zLi;-7d7BK*CC^i>j$|)pqwqK2J=UcZ>cYSkxk(RMsm_C_rDi$%_|o@_#egrd3*y3j zRW>V+3I-^93@p zT~fLp3yE;XuluER-j_r$wU%qKO^H6 zmAqMsJ`wAwNfRJ3P{@r*!9Z^dxbF*z;YC}pl3vk%vPt2*cmd)kEQY$6pEmQKi1g-w z%g6pQN(w{)0U>8!=DT+F9u^Ch-nE2X>|()aZ>Id)rD?4A zHR+!ZE$F^zg?oU%E1?Ex^*ZiX5y5cm#%O~Qd~*vgz!Ln8!DwF08Et`#oLmrk5l&P~ zv{l=_5N!u1^lD#Z<~#K`PruMN2`Am|)B0WD0B56$9IU00;HOLER>8-kiwU6Wu{9NA zg~%#cYYnW+c&wi6-6b`5U05uyVuvW4i`nr|blhkZ+yiw1zDWt+=k30^2X9h#BC+JR zhrDIv_;@m8Ob@+Za2J9Z$L!`<-DdZ1Y_CT&nQ>L<@eo&w`s_MNStL{8aR>Qtw&L(O zk&vX9s5rd)$Dcaeb%h7oSOLO#0|AKAKyw;&&TEE&1ak&>N8KJ5c92HAy zO&W9N*GX};Vb3xFZ|PE_UtF*&yVSQO9$Col4f2NDV-5kdmWwuVWs-rEgw&_AHNmr} z5G0XA@n6wOB{k^W60#E!h84Nym6A~R$NXh?>*pCBX7IoL#X+v2$B%f1^Z6Z>cWph= z?7VFG7;zs3!rGUFpDw~Z!Dp^|Yf6ep2MGr?rikBINLE?;9fG9l(WztmadQC}${l`6 z6uy2{6LOHV7QBo7T<_fwCuYH&nT{lMrK&CA@k}I6fUk67k7>DNTs{+jKyNg$VM*}# zK^%+{5Q#4_Ld9Q?{2O@m{!5JaS=-b&%bn7IVMC?G7w$h)!DM&N@cQG<`ESp&3UQWc z>k9tu;*{acN1((%Z6qONcP+TR5hU4i1IvY;=pS(0auz{H5;sG@{Ps6}o7VFtN*KE@20Bpr zg&Zr-Mzf30S_jRrbK|zO18$?{XRd#8tI%_)GmYG~mE8y5Tq|-8RLiD{rxW4}`mro}|tsTA2%_JGlkG%_i~Hq^5IEd3^f2dDwH0Pp-0B{SF|Aa!k9S?=t@(O6tzpneCDd41H~L!* zi`@~`7EuBr8M1nxH@{{*X z^ZRBcqz<2G6m3P z-b|b-f`vp@MCV2LN@98de0f#S3Yjh4b>Yr<@u6igbRWyBJNFFR9ZHS}>{{-;2t!~y zGm&hpN;x(K^{*x$*5T*l<%DG?I|0cVs??+AvTLhonDNzW2?-M*>oTKS;{`BO4<+iP zx$BU<%+WEne;0Pqtn>gv9rB9C_TLO8v6hvUabl9NlRWqiD{Nq=hKdLS&hoH8hbJ21 z+2(u=W%rcAPYp0)G|%|*nlG?x_odEsQ&u9K-V>ksFFfkQRPBMYp$fWs;nF|k%tQ2& zg;j*c!1Q|0o^NsvIzxp1M3r%~7iMITk=+lr}<_`s#roL-%A_n3F z|4Z-2U6_`L9`edAO=5pVkyQ!3a+qM-=zWP+t_4^wT- z#7Fijbv^D?$q*Is%rWXkZ~pt^oPhA1t?o#Hd2|4Hr{-ax;33^?K;F?2W)d6d^?@mC zcHo7yT9-oWt2dzu&P0^kIvk|5=5#ltVdxA~oZ+OMxMB|n+v-88X!eDSM>>+{NK#|X z4{;2u(Av~k)uE5JPB#jXtptl{zb{E0b5JxCVHAKY~6!mGH{4JS+ z@}B7}u~j!k7QXD=9xgzw9qDFu{45FlUWr#)jj)%cme9@P&GVsNhM7?Um&9)@mI$9D zu>oMrC(f73CQ@webi8Skn5uRcSh-%jc!>d#kET}S1|bOpTa&E*qskXMb|PHt z@3~BewaVX=QRQ*-JygDX)IT<9sP{!hHjA0jgKu=PE^5pxI2eIO5y8u_c`wk*$LIV{ zM)##(4c268?HXd-dAB1VpsCid3~jFhPtuBn?Q2`rEg$EH-Q~pLJ9M5 z0ln$InMY5Bgs+D+kDnY{=Ua26Q2OWd$SP@!Zpx!jauCur)AK3fax`D7$6#?8- z(Cx*mBUW9ac~)yAqv0bu2l!a_8n|^9p&jta*JQ|9?HN;#b(w)diI%Jlaner{?qW?* z|J+3HxqMm_YzYbc3QI4#=4z3}TxR*!^tRYL$A z&OF)dV;*)#ojJ|m;}Xv0V+Wtpy*yU`o(&0)Y;w*CF%?mE`=D%W~P2g zL9GQfH}ER7Rn?sfxqUH`?!2I+Lze)<(yM)jiBpmGn+umbBw?P9$4$R-Jtx^eXKozfPg>%V&xrItomcF2(jJX{eAJZ2Zz@<9@#itI+ezD z^vU?bvmIqX$~zN7d$DU*`2pY?NA%Socqb#7FeJiD zG?C8nk5Ig{o12?9?&JF6ij9o#i(6W+?yJ-5O#XvcGufFI=x0U$UXCWz zu8eUJQ8Kn|sdjwSc{jWJYL?AVn>3Bh>Oefo#iMY9duXnWOvzbtHr8g$Oi~w*h3r?2 z8dy?l?^wFjh=3frc-V9Kw``8XMMMt&ZP${tS)>`xOMRk4;lz^FW8qyaa6c%*Lv*)tfyqn0NQnh1y@XM5#U?9kzrS}78b}wzsuDm zrAhLL77-e*z|YMwEJ+Q#iwiE2O-=l)%eQq7uC7f|Qh&0(X|SXfYsKSh;qtLcvjDvD z!UBxfX8k^_TIH>wl1Jmb);QC$M69|(Yr0~-&KaS)LT`C^6(0O4k1e9$YcS-W(Z=zUs8^mYp7vN;?sE#(e7O+kxb_&gqlDZ4KEpB^?}x^)-v@*S_N zpK50sEM(LIK+kR^efr`z-?Zzy7PU)uz3-DeaLuW0Em<>aYE35t&u@=#+H8f^;tsvm%$+^Fmz8pL(5P({uz9k9n*eXsma&oB`H@@q0fRE&m6pKvuuly?gh@_3M3V zNg$?(7}!e7#Lha*I&VoF$9{QM2Kyc?XCgpC(yY*C+J@WKTksJnCWQHpmMYM;9=x{g(8- zVvY4=QZcy7TdWJb5$~|b>mB@T=xG38$YMuXbUx$&(`bt&@VwE%dsn8zE@>A;}@vJ2~r|tCS%I1&SF!=-jyr za~H23J$&@eqeqsuue|;CscyeF9Zd^c=K+;+-ZDD?Xtk#H?AaB?`r5U*a~CeTq_g|B z?Z5WBzxVKCkAC%UzIfrCGY#PBW=p&y1|%d1+*4a?(HIedJlD6}`!q)$5)mi9a&#wq ziUNbnfKXX=&GDy_7o*^|CA7&xG8Nl3-~*O=PZhXScw~z6y%m;Sl$8<@@m_1qSvSh4n&{~hmtzy861E}J07*&xgxKt)65h1Gj(Tnwtihl+PM}1LH077Lsc1#LA zLnyoy!hXVxAk5B7Hs@kk7%$aeL?andvTHPwQLV9Jh>n_AqiS&lM5An{!c~eLtX!2v zO?EC+08;+8_I@phCVVx{au{7jgFs z4UZXOKQ)d37%!$}yu)R}4~OQHgP^h&7<1w9hk8~yy8Jq_5|KRgl?z`Aq3K-b&ZB_& zP|Rp8_d5NBXlg2v<;x56S65g^YG!73cEjd%GaEPEw)M9C`?g)a9JgC(nw~rT4zh>{ zuxjeo<##Uq#h-lscmA8-ZcR`9UX^hQSZNuo^*4gi7KE2Y2-BDf%- zB&$qQQW=1GP+_71?Lyv_v8%FQW%$$hr080F7>`<`{FX}d$>^&bV;Dt@iY7UPZ=pS; zfOk?PB(n_cZ4t@K4Pf4llPY)5d8ksFu>y)bFKvk8ipR>jO2PR|_*$;f^!~1UEf9C92=iE!rJ?}))Ua!;dfl=-C z>kjSP`^5X-w`29neOPB1-mH7DLO@{Gg;!m65EhPL&a_76oCV)PeC=MPZ#VLRe&_9A#zY z@GjMnQIS7RwuPsIzh_j<9sMmLHk(artuZD^k}S8=xhRUfwSfYI{T3=dHyRDCb&@1V z#LQc_Zk?WQ)84OeEsEDPQ88do_p_q_Za%`Go4 zvN&r~4f_0*t5>gHJ$CrOKmEV`%jci|$x~nb>XrGM)6HfpG8)LG)(|v52Z|mwJd5BAd?>$H(;6YWsDnp?ekd+Bu6JUZt`pAdy^S*`I+%%z|fPmX8GtG zWcW@+Uf`O83~PjEg)7$1e;nJd$fI$RZXAJ6u+6aKiuy0SDqx3Op%6?I&9#OoOd#;J zB`?58yZKwLeT|rK%Xv;@WrII&DKX2>-%@r!DX?GB&vNCetdW@40@X z)i%@A?WSNH6JNTqc=_fq`!+Sk0Dw!K zR*Hz6v#mzcvGtyTKx+bE53ICmnnt(RiCfL2n}CBTYIyHW969G~l4??%dTM|Q$5TOh z@DfVy4C7C;*-TRh8U)5aZp_O{or+qG-g`HL6cdgIN#yLbJ;uY9gGJ=L6@5u;Gi zN@rztWo341np6A7|NcL$-dvbbal=GDwMZCRZV&?_#6V3G{os4wyW{?QqPUrOzHR5u zoqP9OIdyhJtHok*k{qvhQm>hyNW@jpwX*Qq!PSvRKF#b=MA#_=m-f@7PZY9Wi9d~FefpA13mRV=%K4X?*P{q<(GGUQ`g=Mify75**#hb3AV+5KU;7<@S4zk-6xpDX_%pdy{u z_YF)(SeV6maE#7Gnt?Tu2or(#jHGHM+30rU{N!D@t7;z`uRad;w^nwJQq3p;>*qZF zv9(ON_S;*k%eAYncpu=_%0zgeD9<)DuQi#|qs+eu=ocTxLvliBViX!{5nk_V7m8n1 zekfx?bLFYxu*Y}w)st6!ON1G9Cr_Bn1b9~NjPWCn1)Yq!DuYkBDnKZ`g$sh4{FAke zUtzPwx1%TY#=&RuD<<1E!_2b`G|ru4;b#g&JBv{~H9bAM+*w`jE;yY!?1S+h{c6A0 zOZu1QE^pep1r=IJ4jUsrzwRYFaC{etuyrMdstm58iV;e>k1O$aZ5M*Wc3qnNa*?I<}qh{Q>hsKsln%i2#ve7Na$Y*rXT7u}u>IHcGL#CN{ma@4^TtcBPPJN$(sSt@_us$yw(Y62vC%I- z`~1?)n+>HEDO5@jqp5a#dWsY-EG#_z%rkKuZ`iQ$YN7v7NwK}02Y_@jcQtPib!d5?dsg$fBUJu2ln1`?_JhfQtI5f z3pa1x6eg{sIF6WEp&~M&N2W5(hS94@`ts{toY2zBBUa(l1*ZT5cEeg5j^h_7mt|JpN;O;y4q#pD(aHkL(5M|jL_)A#DaGhSyl7Mk7!WlQ zf(7fv^?NolCRQ<9t5J>iEUCb`c3W$AsvdiX9K&lOfm-iiEyt)4v}2gVTHd%8b7(S~ zdbbbUa&EaoSiVtJ`X^|Ilfih)YmLv365~VpP zE8?|@;;Go%Bx*J`Y?!g)7Z$H?-?RVl&b{w>?CzER{HfC?UV7!Z>+@G_U(TJr_#gl1 z&wlS8|L#(^_xu0k52n{meeFwM-LY|d)QbC^)M)KeYZS3(8NUm)5GYT+g87qSTjmv` zj*@W`sDQLA??%-~7?FVE_-d_*IC{U9ZBBME)MyJOQ;h)|t)?Sf?)JRkj%}OQty{Oa zxOnsCO+paQ;vEn;=ci_8n7O~|5@$716h+?qZoh|0ZMkh*r@Okkyn;mDIbbFtrF2L} zWarDvDXz)Vs``~w)skW~n8;N|l_EhnbmVZS*Jb8No0ndAF;bcUk(nI>fC_a?5D-yo z9p2)c^CC(iY3+Qf4LBALmX%-bEjjRjlvkyG_D3R`4ckuSZPZ6cw>EUF$ zG9=%vKW-e=jC-bb@Ig}REnY8KZ$rs{R!PRIkoPFR7+oaRl>`_js$003E7sa5K+Dfm zP6pSGSvl2H8bKygLUk1a<&@fjAoJAoL9r}EWDy40w*9t)`w#r|r$3!;wbL{WWXK@lm(ZvfF?zr>L*Is)K5tAe_ab&GsTwW59D2faa5tibg zp=ik<*RFmcYiMy^MiDHfJ?GgNga9G46tzZYDU^g3Ps*SG0tthNkF~MRop|%jG)?cm z_pV-=D1t~EW>5-?9#bels04t8kql{4S}t6@Ilr{Lcjxwh_}~AtH;%vfwI`pvarMe< zdm6+>t;na|Gb6M0+%S!zTx}V>IZrXK;!;-9s$l(4{3$YML^TFQIR|w$7BP-MU=a1A zvcm<3uOV0HFor9o!eNYHYligZm6a8(b(Yqj9m%NH?}j?+8=2m?zF3%lHIz*ua%+K6 zeyP!#!2>uC9t>avqL9({ebP_5&N5ihs974~UDRxf9SV>Za2V&DLZ}Z^ju&h`2Y&H} z8pWbe=E^N{%DDzasvObHIVZ1R&6ZjQ>PTCA?!5kx7o$^3_fe31+c*|0-P#;o0Q z5g8b}DYYw|?76rlFv}zc0HX&+YN2;aH_e2}-16ID?li=IN_QZ?KN)kzlqeiOrP`~^ zv?oJ&G6crGs5rVdm|V~&iIw(A(s%6RW&@GD2!MA!O*kdyB3 zpa0v+gFyHMFqNQ}SH#Gc5n72L)>)GIsk{NU zXJ<7_+G9{oyl}JKo|#?OOA=#Dzu&)j?!3_&MS>YJAOHczFb(|=t_QQ8Pdh_POiPzrjrk!@&LJ{_E-MZ;BCrcrtdJCh8YhT|pomBdqo9EI?Plxr$y0y##VD(p)B<{(8Bk^iCGnYkF4q(pCsdeu-G z8#XL1FYVg1`|O!_rkX7PNYiwFem*lAg|m8<7#f}m5IWDAGo3xmSx3e?*J`&PdGyg! zr%v6temxkIKJ}x^ne2M6#j1BD9T_uo+WK56_uTLITg}LOOQcXMn>zGJ8rZT?>in5= z=PsVR^R9dL?cQ_d%-Myd74V)&5oL9TnAsSmNi(2#E^4$Bm%MrU%*}<_y@!wfU;ow7 z?|l2IAAk3|Xl<0HnoO(>5qWS<7??57YObuY98w!qxJMC2#)mP-!LU}(tK13Iy^7Ce zLJO7IU}Ou5eJqv`PSq08jT<+ZIgaBHoGj`G6o=g_I55uDH2K0xd^`K|-GnJfJbTna z8bCt|wqut&y{<*unrUy{we865M;^HQ{)KCc|L6by@7FcgAx4CtN`=M8!FDw0x<=qvGiOS{&WuP(F72`d^}9Sj*Qx4hP< zWx}mU^_GMTb;vf^DKmV3Y#e4Bl-3S}vL-tDF}PK>#VDDK^21^iN7aj3Sy_I|1PYok z=QUnGhjXNsExapJXWUHIj+wRInF`n|#=J;g)sRx8*ts<1o@itGY{7e_NrSe1A4T!j z?OO!xl^d7(c9lEHYp+~7@zx8Qw{JOk^w7cE5A5B&=iuSnZ`{0j_S%KlPQUiuzyHC9 zKlwq?@Y&z_joFQxKL3CIx$WDT)(ksgU?H*25e^Q;l--8o_&=^8_Bd`U-z@@+W2BXT zD^(aPHeNB@$b1mTb7RP5cV(Q~wTz>bpy?I&qbN?qcan6^{sRv_{OH%d{`I-Jxvg8b z{?ae~(uK>Hu3esEpv_yiC}S>Myts4M_Ndi%y{sh?#YWLlR18=vfq{ zXAu#FL|(34o|~Cn*X>&snQc3FzIJ7t;M!mT)kQI^-M0+FI-JmZlEmRW3(yJ+Cw? zgH;UArlK?#VTdw2knv(5e{%4XU?CVCZP~i*`0?XgH*XOECjy?saWaw636a>jR;&5= z6A!(0^6iV~&S|AYB(-T|%&V`yYMoO`A(1$D*S+^#ymaZn!F`u5o}ZbT?)UqViYgGO zn8Bmh1jb#X1C9A%FmD(@Y4mJGLU%ycq;21lfk>;=IS|yMz<~x@yuR?_v(Ihby7lnk z!#j51>654W{ZvRtQG`rUv*EpWN!n;MQUGTii^R>=^`*u6)y3PkZTi5cKXLyf4}Jaf ze|7Tp*A03Rub7%9N_uImO{P4J(%w+@D?bm1TSji?0xZL_vUC~t zfE3s=Mp-YUU?CT~yEMEhcmh; z#?0?C07T%>chgQk=}}8&cdvi=zK8F)|IY1ucedMY@gS)jfJ0~J&Oh~)Zy}f_wv-Gp z&5AKR&StBi=(s!M)ZB5;OHR>%L*M$~7snC`g$HEZtU`u!SrrTMhahW9zW)KRhpuo; ze>8SH&M{Qpg#2b@)1Wk^%oZAWga;QR0++XV41X@(TkE!%tcVOnJF|;mBD1Q!a7;jT zoI7Bg#WhYRlOux5;(ot-<@(uH8v&g*B+6gCa^dQ=^Upr}vt7IQ-G0|ydk!5qcISNu@42JL zU5mY?ZZE~_kA3_XW}CBr@^AmBn|33Lf-q=Ck@sG_0FdO;bI`xKZWW^%QINcdP((Fr zDRGp7N-(KHt`>|$L)G;puWVON%o(*O3*4yiDq?7b11%E?s{0trL3>9oTpKepKW|5I{wSJ)byn zqU-w)Z{FyIEqkpLd!IVyG;!iut(K0o=c2SM2!Qtzx&r`!h#we=phN@*T^edOx71Kn zAhf8-Lr`3L9x;SJsDcvA?3tVG78wly*4m}TMPSh|FwQg66A=kBXNm`7jCkvSfiPtj z@XmG>gC(om(eA^$=a#Q@T|eH?P+Bc7Em?36KmPEG&pwm-q~Gn4kO!QaUN_TjJ7x|cUSLm@!$($K~LDFWmSRvyGmueaXmhB8RttfVfAqQu!&)bJuoX$)2B zkpvhNvT*32j))4;dyG}Iczr=qhlmW)nw?4fP>=fjgFxO-q0D=62-@{`DojX9xnw28 z9-R0aMtCYxKQxTvX!+6Ks+Y7b$QBOuRR_rNa+IhhyVysswMgy&-y-(|OHmpc6S61| zSZE+N3s`vf%+X|0WQeFp8kJ$w1e)rEx}TetkdKmTWMz54Q3 z|L)04XU@$|O{0KmX!Z=k0s>M^>m28m2qZAB?|B6cCL7*E7y^KT61pc*<`yl+$xb%r z*fJ=xI*6!eS;~X=BIhe$Z8S5luC6k((OQHF6rzOgFH)+pj3a@>akN$&XBbrrO7&pt zz0vi=iwp|7w$oT2J^IAMkA3*j&3iWXq3^ZtyJU%Muh%yaC*nT(s~`W-Pk+=~?#H^p z&Ius0AgWM0T>yAkVI5}}*JAZBCiq%7jP7Q*Mx)t~`%o$fGf9`7lqFR&^Kpyfv2S=6 zLFQY&=$0|13eJ=~ymje1MLglh;hPPodx zl?rOT+i_|kJZ`AWZ8tH{UECP;DNInrM+f}5FUbNV^(^b>fb-}|$I01I=hP^xnY_lJ zi#ui|VqLG(Uv5n`i9o_pH`F#|z-Z_0JSy2(S2KYn2Mf&b< zEsz2LB8`eQkYg_{sHOmQZT3c$;fzwshT_PthX2ff%m_MoeTGQ`5fvaKAd4U{vq!}s zt*I&Ry>qVBXmq<>Yc0ouA%!z^2E(ik1qcwRP=%F=!ALjlL{sLGM;|`0fA^WQr+@g& zPs~(=E#o^$qY*DIEXK`7v)!I*wUtuSn`W2imyX?a^xnJfU0z&y`e)DdI$edDP$7~_ z91;o$XQzb<$1$^rGMd?;Xrp82*&wlZMw?LCL=$-t5LAGYI)kXRN&89Mh#hlSi~ur% zDDX&1kvPXFKuSAmlz%7H|kBf1L#1SmqTF5F#Q{J`XU82m%&8uTUx_JP`m0 zi9OfO-iseKbt>dNEEEeHZ)kZUPy~Wl07AvHVMH&^0fxl}3psR0Wo8f{M9&bqDh|yS zu&9OOA!@e9s-Qkjp9g#+BY&zw5qK}3KccojgAv&p*Y_R4}@Sy=)QYg27Bp+wDw zN)yfw__^j(`_joX2X8;%g~@2!PfTQzUWYp#0F*K8y@}N7>hjc7YpOkCOl)oFDCihK zWgH+2YB3IC2ULVr!-*yq7}lR8_fm{<0f5Z^B#csI*dhn<1|ApyiIOB)?yNrg*yD58 zuK(mGKS^9iBWa~Qi_ykf=dDdT-KK8%zKgUGmJ_eMe)asNBgc+D{NR1(FP*=9;j&hd z6EP}Qk%qvvy$CA;aZUtjexcV(<~;a)_uc(J+?cVJoiW<17o60#FRT_(~XsNRtvo zKpmVssKOAQnY}O~cyZw{ozF~>4A4R-jHt5OY9O8^9A1#%P(YDxud}+cqKLpV5E3I3 zgj`nA2qE}a5!!tB1rHkMO!JZ}X<{I6RusZZm{I)tD5nl$Kh~(8Yt5>bVXw-jHf>IE`LtRxz ztmOgKXU|Y}J|YX(=OD|^0_EYaqL6s49-QY4qEs6iGx*9(vishTs>R`BWr4NV1SOub z1RV();RpnVgmvk2_`oCBRh|irB3K0vhifPlpCKQA$R1#3(rWM%$~UE$whTM65sEWc zRiqnVX?87m1tWrB@>TkIL4C#esx;>C8wI?#N;4Q*?JjOLo7?T?wHrc2URY5yyJ_q6 zx{dvQ_tK?{gidR=oRAhI(xib=Sd2Dk%*xFhZ!O$->y6`E_HNsCV9)jgdk!8sxPHUN zOIL5~*s<$>{8#_e|MUO;ubku90EGYw`=BszJ|z+*k|m~9Y~UghW(XicIKH3lC_F2! z@Pc?V?k$oXEDWqz1j9K-^Z{x<4?-$X72jW>C?cX0M?{qIWL;WI7L!3hrcAd)3<@G3 zqEad(@eyGWQptU+gU`OSSYh-M*gT*@1ciVCNZHv)@*wP;X}8mU(rPxZUAsZ397C`S z+sziU5QqmcCQ_(0Da+o(&7_~kI`XLn00J;GM&^`qF(>aIX3?>TzkWw(F z24RS^)Ydc+5${B6jf#RCEFz7TEuvy%0uaGqVgL~^6Nqm9d9TVFCyN1@vOB5WbbXm#NG`=GZCfY89fn!2X8eOi;R zvKIVYoJzt{eraZTmIl9Z?Rp%?H?QA_V}ppu;Jr7bj4_=~hm^9;20NkO>owzs zwYJr4&d<-!OtqWM=F-v!+(LU8S(u zY8d0;ewEV&|Pp`kQ`QH2QyKU2!3l}b3xpq}VbTbaElL*Lr&)yL!W1=nF zc1*2nt#;?1edYCa&G;j~_~}O;dE{^Z^7F4e|6;16=~la&^jmQ=AT<#wrJS`!#~y?P zFe|`CN);<5%=C{Tm))o?UUof}!6hPv&4P#=dJz#JkdPX&&B7oeMWRokF6G$;D#slJ z4s`B>OJ^x@b#;{p6rg}04`PENK5Q|;Kq-Mb{kUK{5JCYXI8Z_6+B+~t*{Wa^gF_7t ztyvd!q|?{^9`-PT)F;X)r4+O8cYA=?mCk*S-t+CRe9PWUB2-}^Ns=^70|lLPke}Dj z-< zLvk%xQ3sZ>J2?8J)>?p-melsW_i-EpMIcN_-M+o^o_*H4rKN?H)rILP5=hA?5f3Z? zq?96MJcvVXwVHkLU7Nmh@}0}quHWl<%a+@u&%3wXX8Y;<{K8Cos(*6@$TUpkoU8Q8 zBFWuVbqgQQ633>Pje|hWlLs-eiqSscz%V3>%MTRA0t!D7`S41f`-?`Hl&RYSp|)XaosJ@FZM)s>rhOpY zv-b8o@0^-lw|&!=cB84|1`>INU7K|Ky`Mh)tZu{(kOfoENZNU!rtT&ED8!aQi7>M$ zh0Iv;-G?@OnB(1$aJi-VvvxWy#?##fKkw;Dwi76zi1Mp|{5!ci+=&HoyO) zA2v+nnU|MWSC*C@e)!=Z{oscHoY=lnN;o@Nm$}3dq~t*y>{PMdF$P8r3W@-tFd~G* zus{M#A|fcro&hk5qBLw1asI}_uI)Q+KYC>I=FO+yIkVbbC8NE!%-o6_ra85B%NA|S z{Ll!7T+E-M} zupE-v2L^G%4BVJ*I&hv?l&Hv1%7B=rmJy7OUFui*E3MYHN8bPFxBvW`?N-Z!C`F8H z*&_p~Au26t5?q8!MrSxKuuyz1|2dSTN(e<1k*M<93hxTL55tj!wM-zGL!%zL$`Z!u zVc~xWV|Ce4lcQ{W!q0UTdd6VPk%Ie@2Z$z*GZh#AzK_7Njjx$bb2bp|vu8q7)n`>}(5lg}FihFm+yE=@A z`JZD|p9;EW`s;XhBqE56c?Kp7$7$b{N%?cwpXGPeLLmPZ^F6Kk6;u(J4Wf58QgNE5 zX`1S&C7=>7sGv19H8Z=;+TOYIXMj@>Vs90ZcP?%=m{e*l2s#CcbKOp2rW)IJ@4Wkg zdyd?7$HvVYl-AwO%Cq19(F;F&=Jcy?`EGxzs4WF*9kyo;`>*+BxOD2dzd}qAwZ>l(DWzkF20>{%;nCEKGFB zBrj%rL19(du%20K9lDZwM2g~GYOTh^*`<}`PLg`0RomaNW83fklm9LzP3#S+&T{AK z#mld}^zz9!--?JtJb`W)-Ah+DZQb|>|NI|8ES8cD8#es#2S4zR@4olGnMT`!efzby zzWlvynw*L8an*7C=EN2DD;Lq`ebStc>yAi#Veo^;W4!G+ApAk+c>;@0|kj;z(&_ zbl;_@lo$D>&;0VfJCBGWt#z8Fsb?pQTFtO`%=?er(M|d{SC*MM0^PH=pSq5l&C~CkJ9XxaO%km& zDRS%yPzRw2kf-0~P32jN4~(_;G89``YgK|s7X8QyJp+1RlEP{g0!@ZpD6XS`@wsQ7 zpSyJB*s)_D|L7;qUpV{R3(qdE&NFx&p%>>l(K=Fq;@Nvv0BzHBsXcf4%!TvkkKT3c zo_p>%clqLlOP7FPc6Mgl)~#tTxpCu0r_-^{0YF48@Ya~9)9b%^^7Q4a&7Iq~{Ih@g zFW-Fq)vx~TlXDj@t#8f%0x1ElePU4&h2s*;UR>~vc_*2e$zcGjMlCgzSuF@Y|uF6#)bV7;@P;;EyWmn6bk=6*-`U6k$U~5g^d)d2w;Ecxh46471K0 z`SbuQbFP??P?F!7FLAtSF-qg**a%GGP#_|LEGRlst*NHdoPzVj*7bqFTW04NPz$8a zMqsWjUwPn>`@i?a?^yQ8={gA&Qs5DIEya)haWdY{HGMz5HiIn<>_a!@Y|p zIQ(Qx0rHIaIjQEoWQjG7dVxf1PWTKdc=Nlczd)C}A%w+NfSPO%fZmTH3UeekVL-MdLsuh+YNZtjgY z-gxfmpWV24u|Zl3&bFt)N?|7yy#@*^3i92O;mwtil!wuk)*6|DEo|zppb&0$ifJe8AiYup26XxPo*7H07Z6;0A>C2 z29ztv%prUN09-Cpgw%{1t7*U2O+=*8YI>1|F~U4GHP!3&gjs9tya#~L-%BH<0N%L> z6)0t$(^^NGlu{vJs5F8Tms%pyI7qQ6M`#P7utxJE7>bw&jYf8}Mqdi5BPmL(YsAf^ zUQZ(3^X}gFJn-1#@9m|DAoP;NFuOEea$bYPCQ_uf?cH_c$dQ$s3xD|U{(Wz0g^08! zaeVmLq4v7wg_{>QZr<>dpZw$-U;Y|-eD&Gm?e_G=%a^_P{neF5tLakb7FWOf&F_5s z>rd@Duz$6?vVO~^2Ooa$^|#+Z1yRH7*|G1Q`|o=0d(WVbg_#(qOl&;NG+N?hU2{4n zO&+v1$fOJjq4h3`6o~3{mr`~s>8ceX7SN!@tw5`kyU@c>72K*Qp!EvPm<*#3+%Nj7BO>WR;igfAtW0?2 zS5%JRPv*ZBNk2hwj~MK-0O9kCW>^-o?U8{(Fza{N%)0lo<4W+ox^t>z4NZ4 zK-lSY&YU^r5+`1iGEvlEC*nYxrXpbm73tL^ee2AHO&gYW9Xj+c{-^)P-+%L|AOHRL zZPJ@*v{Ii$%}AWrMgf5LHX}1eL{LZo!~l>Vum(aBM4>XdlrgG|y@P!RRfJQ@5ET&t zAC5L63$k#)k5CCHxB8I;LP0OgPL+d@M+q)q3{sl~3078Cl3qWGBI{EkVkqY`XG{i* z%Bxrq^&m_Z2pkC|3&(&AvTSNdX;cF!eXgF!%872!YLb8%(YZ8gMpNr&5|>!xEI986 zE@8GV^`1yM^hhe2j*s4P?Cs}Ix1$yydhsX>q-l7PB-tU%;2{|xYqs1#`}INEvIH&f zV>xL#dmt2GU>;SKstPNYl*@oBV;S-!55?Lq9L_a>@nRs)De{=R%V9W4WN6sB_`u}A zSGi0@an2BPjYGhXGr9&Wx-RfI@~djS$6LT>L{1I+K+BjwBR}NwGZom5^R|qB9yK}2 zLMMx^z(88$N7+lB-z*s>fH@>}W3I{Px7T+4+;(-XpbUJfGCqzZQHMH^<@2sJs9qi) z2N9>D>n|YYr%?p~P!yCmMTTl|52*?vh{o@lxN0F*aj5+qG&>5?@>PJXx>H%1ZqLj~ zKW(%(G@9#{J82X*HgCDjdUxTkTWDq6o zCznp2f8m*DkH7R{ceT@wqxHJoiehnIEQ<$1#hjJNfM6j>`Pc7i<^FTZk7y5G4-(`mAz)sMl5%vDP+|j!02onaLKrY) zFjdOd4>K+VBoLqTTa>KrA_5ZSamC!eq!5q(xPZ?Zf@gk+Zcoqlv1;^v&Wx( z0htXE1Ctj-)LH`oWil?DQADlDIY$D7C@e&Z6neH<1{u|yBo~n^6SX7=YJFZJf}j8} z^DhvVRr-Jk#gj!<^DrVQKvl!yo$M7ydGCHj&sudhW`_`R+0{)Ix8?TBk)RR>^#4>B>?s>66Ar z)HKGJsF@@-N%{nE<>IBq#pRoGH;+I25;o|uUwB+8^pYwAPEyjysi0P3@#4Aj`{%AK zuXN75apKyQ>sm+bK_P)6rO|ol`$QnU{q7^Jb#08uBRCd8)LOG;+v}e{d3I%CF_y?E zgCbgM2EK9i+QB>SNW?QpH|h6!yulh#@{8V#*5Ql{@Ml5#d>ln;OWW1H7a|L9xa zikeZ>h-|N$IB(*Z*^BsGmoJl2y^C!Ew;w`=VSN>bzsRLI!*Iiop$t?h9ZrzaI|>;; zI-7B_8DL2L3fCw`mVZ)-+KqEj!@HD!g(^SJXI!_UNuJcopXI3C=<-MBPeceXvwr5G z2Os>=kA9RS3585*rP4GdrM&lAnbg|$%=Cvo^r2^;efGlHb3!O0tE;Q`KlGpje*MH- ziFZOczp{M)J@?*o_g$RWFF*OW);nX2SjTw=Rx!D8AVYy#_v|q^QAR11_N`JT|M<_$9+ z{^Td$`@|Dp{^DQ1{?bb^=>*aiHVG8~FbGNI+>u8BF9P}TdPdMe{EY1ExV^@bE3_CE zAoro56%h!gNUl=np7^ZzXu;405rh~i$YlT!7KKK{Tk97VmxweFvgKjl3gzmk5@8;Z z*^@1Y%0`zLJAt|-LPX;YLYefgF`z5pGftwxuuvO^ zb>kvQ!ZAtDeW zkY}e9b(3_4iLlw}@`0VZ8?D(+f8o-VInijv12T#v&UaHgJ3IUE1CKuN@WWfS-xdzq ztt>6S@#<^O{p_db&b$Lj+HSVCwq`US3ZwPH49*hnLT|^^uF&*lxg@S@=bmPjtun>_G5kw6D zh@2mB8t^U*^{Ya0N*ZVj3W-$IOOul)PaeJJ?mfG9UpRZ=zI*STZckmld^r^7*IK0j zz~WNN-fB_?$t7tp5WVxpm@r-_rm_Z>^!j`gy*s-JC zZ1#G+g~gkUjG6^FYM7I!-~RKz{Iie#;wKu7#>0<2`tpxn>@TcnGA`-w+`a4Z_dT&^ z?@ps5MzX2DaPFO_fBLhtr%pGcn1tB!dq42Rhd%fb9b>oKefjuHacsN@YEa6w+v@o7 z9_v7-|pEo6^AO+Swa!7;ZOhY zKVCX}88jFIW@7PQ`|6ixe)^+SeB$it>T1&OyVM#oC=fN`sdY0uckOxL;fH27uJ5Hu z-`j4#bMJc|n4Oyb*57@_TR+`w*|bl>0FVX9Av@ZNKbP7t;mKO*Twr*8Qmcq)r9uLE z$at1K|FKeHr{az+yQ_1Qs?R$u=)V?^%g+?b;^=xNu$%E}9F5+DVa5l;oM?g)Hu@D6 z0NYjiU*#yJ;j?FGE>>#x3X^7N_u9(dr;p+noZ zZ9jhe_&e{s;{{?PsrQkJ2o#DE7VjN5E^u{LV$yBuQCeWRzwW zV9Xt;S!O9g$Vdr6LC6Yrj_&)Iyd5mp!VL$|0YE%>fV?A7VM8*z8l@Ql5j|(;`~;Ms z79bWB@T9eO{pFR#)Vqc;E)+xspsd&QP%e0`B znwpxLnVDHly5a-~1(C&B@0z-ywE_>`I;9LJVpKdmHPvpnyDMF021I2HGJ4L=f|5{0 zqGy51VcZVtSMJ`f!ROjLqXtaH)U5GsN%dqP4VfWB7&Hz7Klv-4wQRw&OVVcCLh2v9jc^n;H-cuzo zLd&yIjeR-I|9hX&5-I-D$a^oW$!T=95BRq2fo*_XCs5lM@pj*QKd{= z-Ox4YHQDP{9>4NKn0OUurpMv*$a1RR)>6e}z3rB3EIMsMo|`oE?MUhlGdG&iwd>c{ zt)IDc>4Me`1dO7FwN@!3!PP*@CW*CvdS)hNo@!4y_RjO0Z@rC;$Ps1B#Q(^2s*5h;V*xFTdd_ z_hiL!%+962MrrX(qy$hYm3Gsg{_rO!-Z=5z_rCWR-v9oihYvsf^v|wcxf;dVvx_Je z4@6)Zx|{aFDTQQ|j*Yo+{?hlZe&@h}L-*fz-~7$Rw@;n6;Dnvon zMrqaXoL;$cW4W`ib<4*8{(t-zufFiY*S`2&rxeK;5C9M}6KQm4Fyh{dR>om! zRAgdp6Q@w45Rm}~t!8^iYv;fFpa0F`&BbIO0Ev{_5Ml{gqo z(yE8X9939r0VRXvJskPfIDQ3dzshd@HC|<_pyCIV{9p{^Ott7mRy#=THC+5~Xw|i= ztJv_BamrIIidc-=&lRube@RSf*v1>WGvWEdkh^6tupIO#eqM9|8J)&B;7?v*0g!Q$ z`oH>B;REArtpQaIdu>WH91T=farKsBy|`KN?~FRW*Rrm0FPi-1gjY}a`7K>_^*#bZ zW(NSIKpgk`eE@2x~8?7xf>kJY&M}P$A(-aXSr9qLkHtTDTh=4lh59Q8v#oWqNIf@A* zPu46jAaL1DklXzAzPu7*3}Xu+0!n@+xnP?_B%zsIUMD#uv!J3u>ZT$R@FDFrbht)E z0A7eh7+Jw2svm;`ifCw=oR>v~JhoiR5Soof&!#Uw^V~b9Pq$jFrG>?0wcBhqoO9lL zV+@F#KXvBX#fvVrAfku}Ax#sdloomYjn}%}E;AdYG?FkNA`+!(>QKD*m3reaLRrNm zYa)%oOegZ*Gf0_%7jPa`kZfvo6nUj?+?;>(7k=U1_dK}R?RxJw%uK!Y+N*#07oR8V zw@%M0?@@pVq^TV+HdW`w!k0e(m+f{tO;fFvfMxKG-Nwxujy-tKbi3^me{*r6)9G;H z4;{VzbHDy;^W7B{83MR|^~U1O#hLXp+xBd;-u~j}J~KCW_1fG`rPSH8=QnTPlJ?VM z$L?GgO`m=1+}znKnj~sO)>>sCQixhq6RjjSyIoAM2FY}G9al)fn$i{Sd{mGFF*aWGp9~` z>~o*lfAsL;QU~JZeeZd2Wq$dU=bw#q8vIk6Q*nLx@@J!}%wY)cM zt{xf!Gl#TLXjdggUR|(=YMaIs;mQhn<^GEy_jd72r7nf!NuEVT%8CKzy|0X4#7Jui zxJ&?$`L_@NLVM6cXDXhrw0aw-9o9ckmH|9yUyhWm&N)|xPsZ@F+(-ss6X|ZZD+nxz z0K&3it<%BraOzvw%Y|HB|t=}wMv<6!ok#olBXPoN;Vg4ceG((b|1Wm zNY)ZV^5d00wXV^gO1$&J;qZ6A-*304!2xr2LV!F9bEIM+xHfm~TVMay_MNxA=b;Ba z@{y06dgs&&FFe;vdY*iwjM66j3Is%$6R$-@WR$2=Z=Jk+Ztl)I?|kCDkH2&7%(;u_ zrW@1Fvx!W0K-v2!GKfmh2>}2vcDgrCo!q=-!@ci)_@4Xj{q8ru_5JVu(A(vPYBY6| zQ>O@%)qbmvVaW$f(SV00kWt*q7wGSAer+oaNZyS z1s#hQkD#?_IH5*s9qP76;}g$pEq8A$Ur#|3fQhu`)H)lPNW2F`LZon90fcdIrlmaF z%36w><+YT3J!Kcns2(3G1J}|{FlRaM&ML^v&Rb4a`;Z_f9GTd5QbEQT#X&P_+P+62 z?#h&!+S=aw*Z=9S-g@Q4Y`hK;opo7^3qWZMJsXE~Xapnz&SH<1{R*LG>pN#C8i=o zbnIEU@EaDiPe_CQhb8;7+%&baUdUMYK!-!bLUBPFOupd5;k}r5^v+>b(_e0~B|pJ< z7~S0By@3$RvnpPiVSAy&2Q8^2!U`;!aA~c}N_HXF%1gt-Jv!9UeJEd`veJs$I_mGJ zUHdq_L58^8q>9109B1a?ezRlTjy!xka?oFzO3X7mORN!*#noDL1A-Z5Q!?C7H5?l$ zq2*9c6Elb0#R>pxFU++dsMEGZ20X(xdF#vdB zKr~tdi(~ISlP1r?09t89S%+->)=DqA0>Lb0B19&m6~b=;5K2a?We5 z9S0=}NT?urL<|Qvl+ogRL`orc=9gA(ED(_*1kZv<1mdg`?k%r&mRx`s0(j@bF%P9_ zWoen28%ARu(pO6PpwFX(6O}NmisyWuFz5N+x?Fq>({;Y5N+}xtwonZiS#tJ80**a1 zqf#rK-j>_8fAFIpS?zWlbFO!llpu(Vwj>ECK_? z`tFDC`nBKujRfEmpZ)Y7{vUrtsE)t%@*|HtcQhESkeHQHY0_&N?Ge!P=Gk>C zoz=hovp@gjuYT_EUH4cAoizQxM?ZY_^r`;JVoXXAd25xBQPPjw4d~Qihw|h5QdbzKBKx+VUktYdUa;QFCP82 z&`Qg9Caizd|5P4lRL0amu;AkpN~L4X4=bwn&;wHbbMO-?H3kTzPDBEtH1(78!3Q6F z=gc{0naH3}6gBtnKX~!dRnMp>5+)E_T3T6HSypkg)9;Jco3?HH*e`zSz>&k}<}SVR z#v7*5*tBKSmTg;6DMEGe@|E_wb&o&(_(wnbv3K4%b7k&ItJz@Z1;8|7W(F!+_zYVY z2$fx~EVjBkK`@uwf|CfMGTM6vr98u(ciopdK7am_Qo0#8f!TSF0KrlsK>~_RGf9%A zrKR)d&&G{-_x@e`4((%aZ(hHiq=`c4_j^$s0fKezz`=vvmEN^0R~shQq+DVzUc7ky z`pvz2_Z&Wa@Y?mOX_6uZF6^Vo2%$n`@s88fr=}5Yy=~jn%=G-yBB0!L-(3$p_`tb~ z=jZ0;64#H729UTBr)dI!jvXl-kiuFUgnF$pHu1_5Yybr_^2|syr<8#w1poy6fc_B# z6{2LV4UJ!_zWeV9@++oraC^$TD9 z&X>Qlp)uP~O^t+EyJ;#a7}0R=jZAo#x;5gko4j0_e3TE2vlz<{*M1(pIm-H^_9sO@ z>j`fh3-h5rhrTM)axGEAFdS-mb@eC1ff8)sj=;AuUgx( zmZyzd+9(W%AzrfuSTd3;`#X8gqfjk=j>F`d4SuUb922l~9E6AOjGBv+a57(Vwe0ig z!-aw}4jW68E>oq9?p^J>^3AE1!Ww>{K1I0}R->v*EeVvrEv|A!uCi)W$bzBqu{;iq zd|Qu5#haKx1QCpN7`LaV*KgUnW4mdY<8Qv)XsciQ2cP@ICqJ@v>y|W4uU?+};rG7x zwI~1f_2aKJm08zpH?(SL1CEt|lKjj)W;t@jiy?D=Bjsrvl=pKHfUJsmUXEsTcP2m# zMrgAF&q6F_mSKT#!|ALDlN=SDUQh@X5R%dYqO@jVVL?Tq#w!46 ztrQtWx$jTEx<(b8eND` zs%<$!5#@a;?EJuaFgGKIHndz$uS7>>&R?_*3*(4Tqt)trzu4)0;xoUrdE0HPNk3{d z=C9xQo4@$a#__sF%Lr8zRHobWH!l5IFre@l+v+Ga1`Ihb5 z?(*vF%*_4+dlP3bUAs(LZMkjhGfzL$j+z9LLeg=aN8kV0@Bic9fAFz~9(&?Z0DR@m zSA@9FX(#F2^`3hkdE&jxE32(2o_puwOFw;f+B6NQ&dSP`+qNCO;|?zl5L{|cy#1zQ z@yKlGGr#tkPkrte4<0-G*!$jVV)NXy&ls&6QOwphOp~2MAp-C$kul8v#K}`T_wL)U zev`Ep05@*f@cJvS#9Au?5hloDzrq|giU=!l<}&{&bHFMA7Og(>4ISmW<>%FrM?S^O zz*RF6Wnf0;KOQR%$1#GO1>IWq!$jF5lQOufO8n@6wQqqDHMh*R2Cs&pKg(~a7sexI zBxZdhA;d%hkw_tC2NGGtS0plTh`QmVaKSPuf=ya%lrfC2Bm=YiP`o8}i5R##V( zBtZhD2niHv5MeI_B+Nz|=Tl}rb>>vJ-`TNq$L#FP;R6SIz14;J`6xEdI}vc+-+t&& zXQi{Wu*l9Mpiv|uyGQ zE!i}^F+ab&veaHT^TY?BtKRDFFgO7SAXGjL5-MsilZfyKz*%ue}D(2q6$47e;cO85yuCOSc6C=dFq2 zSB@W_o4c}k!{!Y$>xsdD@u@drz^W#;H#IhW?TjN!q3M=tktgvCxd6*flZ3Ym zhU0j}#D+|_F}^eyuk=sU&bb`cs}9HrP?JxKBi`Jgdey$`mKI3e17oe1DhH#yBlJiJ zbR54ebI1V)7-^1D26Z03u31{}o{Y}6@@hg=%o^5w)jiC``H|67x_s4A-k<=b3}=h~ zp#HZo49s!iI~dV`>(?bzE_ZgzwU>t)oLm_%v&Z3i#n8%mmy9#xi_>Sd!Z)iijrjpI ztii5wG&M(|@(XHO)d4`L4~atD_c?E<2hXU&R55x#?q{`ryXRGyU%J z#dGgG_04ZT^|h~FI(xbm>FGujoi_ps#HA^*5CB5fn!Y$y2>ITJ{|G`GC4q3>G&@Wv zkZXzppz0i~6Ht8;;g?P=QDz|x7-#_pK?O*hnMQyi-l!CkLQ)|`07+q1nhp?^5HTYl zAqIN{1xYD|N`>{1GNd&Cp;Ag2R3t#2nUp4_01$|X6bl2YAfqMRRYas2*n0s2B7<6i zkQYYMq==Le5UnCXLO{|6Nt05Z0gy;*r45n-r0f;Uq!c0vvj{6h0?5mcGjh-z07MjE zXa&d2%r+-07SF&!Sw3*4$rSm6Fx(fq1bjX4Y_bgK2uHTBQ|s4%=wlyCyeA!Lt)Kkjm#&{bzrNK3W?)Z%q!cLfAfcFTKnV)m1QAdixDS8) z!-?4TboA&#&^UAT(YSneN?A`L4|IKfA ze7BeM8&l0AcO1QK_x39b*IOH=?|t;akACVCoqms)4C_;`zIpbIw~Yg|%w9Ha-g^77 zqw}lFI*tz<+TXMG#`65SEgOIJcYky5kwZ6E7A*K1i;H^?>^D*L`YW$ZwWrv5v7(g{ zhBQr;F-WMC>Gk@T=dK>TW@H- z+I!=?v!jCTmP@XkYHhj5%HWa*h(rWIqr$h}KEt9S6MHu7$a>hcdDreehu=PR^3tVC zT8j`g8jabF(+7?m+_7i3YD5?2=2p{wD(=1q?w?sdd-eL2`T6;)SFgrV^Y+6>Hf`M8 z>2wg$5`i|TG$SYoX(0f?(-0z7`D9>Nq8BqS;9uT|!CHoj(gOqqnk0&%o^_yg_%cb7 zkAC!{H?Ge=_ssJv%gZ9tXf)UZhGY_FQEBlUp|LiNw7GQQ@*Q^^ed*&`;Aeb=x*<`@uJV@Z_IAIgL{- z)kKTTK1)@_!KQowDY{qaa!{(pI0;k{#s&lrk;1CRiW4RRkEn@!fcKH=J*KXMa5Y;|E#^ZKt3>g); zw_IZxixAOU2ZCCG^C>B^t{XMH^PLS_H*VgxKJBi&^~UR`PQG#S#On(6R3p|gA#=lM zh?P$*2^bT3mtyG{6=p^wKq$yoyWUcuG&G%-&~s*d8(SE|QYJ69hH;5d$)zQEYP}Z* zK~#iDSzHv97Qj~?PwL+k9Zi^5FLk6h<5f(y0P?``Wbb2=mNg-s0X9fpS zoW+6>A%umQh=4R;=Hx^YAylFvYa4<<3Uy@ivjEI&Ju4mBOClg5XD7u_5h+qiu?H`# z@=`Yn(K`zOK$YH6j648H-cV0+w;__$;A%xbsccQH_AJ)f&L8}K0#-CMXIiV>F2{20?qf&pKIYgnLYg|8 z`UV-S}7$$CtiOGQl4o}_if^de(6_!`JCs& zLr=WdTmIoUzB|)s#oBo9iBuzwNoi(A!Hw;iix)4Qd*}3_!$%pA1&`c$*Z05mjj2{7 zoJmT|4i^Asjj5{Pfs*jAr?_Ir1!`!ymRXH0v6JaZ;~By5lVi_L`ib0Prlxj(pEO^EAK_b!h>9z%3zdGw<@@gP!u2$Y3m`18i|$E(#-7o!$%L! z&aPitS#c6=+p&Lkunp`*7SzVq1io7ayYKR&;( z5VxAQA33&d+ijh0ce&peA|+y3tak!{q-~NAXN|wHW@$1wiBs&t7?2LUr$a?BvpTo} zXstCSvLcA6NxkvrTi32X{lxn|@TV^gyX2CtPkZcb z=wL{7^BmRYabkzVT>Gi^a~Q{;DpZr3VM1PF6$4?cn>MxQUp;@~_*)ydZ8&_#;RA;b z?cKZo?|=BcFMr|5S-q|$4YY`WggQ5|DR+eAdd(=!PUM&s6}eb%(cuc}nFH63HuCWvV^;KL!Ld5@@a9&J9@NRCTeu-YRCW|EvR{?F*YDPCG484}%zn<1 zvFJNGGCqtL)kEtV2`P`ls)Bx1;Wb8nzdPr_+T`HmXBeb#1kgn9QW-ZG234$TZKv{A zlZ;-;(iXzg%8wbYH=USK0EnUo`Yn(Xvpr>g0u^J2+V@HklU6?Sp+~Ej_!jcG! zNJs0`sNU+*mD4Amzi{c)wd)rQ`E`vr)-eK$PZ1zZ`oN-;LJ}5-s;B^*p9=`lBaw2U zwsM_Y&XoB;!d zd2vWemD)2hI{-o=1QrpcHHm~{kltqna+J~q5Rx=S#9;JFZWAxkL^nMutNN5)VFPa#=re4U`Gtp!IsiCzRl9I_;RfY38L_Ph4%vn<~G zD2m>G<4q-5I7(?<%)~pPhE|>fPYmNQiV!gYKy-6{aoyq)E4O3U_Er@4Z+2|g0zhYF z)upKhjI|+vq~D9D8%iUm!UP*OZe{?j4Fd==G*oAGIhtT8FO-T?rqcRTm&W)bT}Aqo4SWfBd< zw*UbUfUgMgan8y*n$4MS-M3*JUBQ(qqO2__HyX{mkGIzNYRe)9Nh6c1&gKF$UE zu0l5zd09(`f|2u{&}`nk<-nmM>(*`T^-}?Cv?A1Mra3)B0LVhpb?M^5vQ=(=`R2xr z8$bNfk1utXpMU=On+rE}9PK@@f5WDY&hx_Z3Mh(6iHLaiBBYH6vDQXJnvmJou8VQt z`>*IXm>1ot+pqHTA*3b{&TxwW5Oc;FR##V_dg`e|2lwB7_uc#U?R)-(7fzo#g@}=99ufH<4wKz_ax(=`SrL-PQi`LVu~ zwbKsVqQ>{c-wx~CC6%#Q9XabrW2_xJ=(ER1I$Yrn>g47cLO zw{#0mUPu{$qxOt%6Sm;z3(bu)+e+Wo`hcn{tpK+Cp;n5GlNz;4&i(p0 zIzB*F6s>lsaciHTF}#~d>=5Y2*}!E7MC}6=`(X@ry$d_)-7KO08r_I-5103M46fE^ zrqnw1Yf-(<-8OW;0<_Q&LcnQ4CidydwYf7a>hjeqtE-D9<_*(L^r-1N+kpMLVM|88Yz^_x$A^MCl4 z{~{9SgneqST)zH|zyI=^uf2&qnTn?<0Fp@oY-p~(c4qEB{Of;r=;(pFAH09xfxR1N zH}=zB97WIl_-9}J(wD8ZS1(`v?63WbF{az?-hS5|ziP~1{n3Bk)Sgv}P{N@O5nzR+ z1g~D2qfkmmIBv9}Mw6LUu7_iGYD0vOIpq4|aOH)yiq0~XRfKPaNWZcI~J$UcnPb@_k z!Ur5V5Raud7GbBs$ee{M5degNMfFphp4oi=dmi1p|B#MaNy@1w>k)Lr0rgTB&a_$K zm2S7w@2Ylm-+S)ecVPed3+GOsJlR`a-M(Y{&fU9OQ&Ucu0h|bt7NFF+h*V5;@{Ko^ z7v@`WgFK68tw<4tY`kJ68S_PiF%E@Vk2?S;AcS!24ly{PK|sKJ)>^ZG^K#_qor}wz z`J2mH>A5R&r%#`bo6)`Z-?w$^=J|!2D=W)dE2YrdR4W2NW3+c}@1DKau3lNXe%;$- z%cf1%+qX}>-Anri4j$(r7jX*?FrpY}vNyr$718 z%*@P@!?)Ws>2_AVcVXW^p)f0@f|E;7rQV^|9>j{ha$|1WZCjpr;=LQz&z^bbRIk%h zN*QI?XUF`@STsp?2FaF{t@8mjFgu@9{0b$M$3sL=g)l@A5Rges}(mJal74UG~%eCL4k-k1_oePP7^HXs5TKGn{A!s z7|W6Zwr9;F2XXI$v?a^C&psP6*^wMC1{9|pQ)Dn|Mbi;9B>}@xOEr{(2o?CO6mWiC zM+&cp3USfkx(?9BaO9gX=Lp4GVQ~7WS{N)lbCpkhlzolzxjLpaRCr(*ZZv_9*KNUA zjdp?pJ!+X`Hwnx2JVs|;YtmHgf%2V7<*?DUl%ujWOkNO1)=_=wuxehr###u3y)Hjh z`iT^~UpD+Q0bE{V{zauwO8G6LF34I4S3p|(7yy7&F=QATjDu5iQvN*nuQ-*5gF_az zlC*9Dqn@GguOJR#b^hBSvkHTN!|LF*V!f8Q005y3>Z+e2|2YEGV{+vc<1l2hAzk2V zEucmR&vBrPOI6$$njTeGT>(@DG8IgyJR>4XlS1+~?X0ZM-@K~WwVG7t?C_peK&Qfd@kunU(0;F`-{+S4gP$?vG z-iH&h;cTY>0#R09D<>$&-g*zAq&7PNz|as}IOC2e3@C^?53nL;b+8FTy>rW z=474m-%xRpDgsd1@G7%~zf^pnr0fh>IR8}=cAt^uwL^?7Hi#K=d+%`4UXtd&Pzh|Q6 zmaW@feC|arJiBSbuDyGrsn*^@`!8R);>GEvI&{bFzyABb)pvQ%cTXnW&d&XN ze*Yi;qXbzHT8&nu%-6s6jYhlm#K%9l*jw`68&W^~_V>U2uw0PG@QP z$KUxcC~~MkX~YKUs~6|qc;(d>pLxEwyxP{$@u#2v z&ey-G1shR(<DnFjF{eqB88E5*L+0{|H}r!wU+|V%P1*k@4B zOv}TKa@fk93X_FfU1hOxOW)zRKLN4~s_|jN(oe=@)QpZDg220$(Z zo`{5g5eUShn9(xb`@q9Hb{*{Xl5PV1#3_mxwJ2c_QKTC&qwCw=YQF=(NA5bd^T77R zCC@(p+_kwYTefc6y8X7OVUoV{=+G!HNZJ5U(oc=n{eC|cgNP_3&IfW8k%#jqGFkqv#-T|`H)wWpAbU;CSeh6w6#vh4Y4kcn`y6)007j8n(XA6pFMl}^y&NW zzxPw0`ozhTCtrO2h56;>MjVq62&7$h;58|uNNGvZ^wGy3?j_x?KlQD1=gwTdHrJYN z-F@dB-twETyuQ*|o@uX7TpGp1z8^PY>kh#_8% zh{#B!(1}J(pse+-?;FODj+}GagLjSvjZz582?P&1lypI%yel*gou-pq@RfL9jdyLp zvW64VMm0GA08rUEmkEKU;oG&5e0cXvvNFgxPBr?~t=Z;yMR{B}xBNh*Khxl`VR#nA zQgmmjj9FIhYhzsHI}?nAe1sYx<3vrj3QVf0pj(E|5b{jCY9F{|e$hC1uJLt61D^lF zaQB3nk7CX^OLa6oS zc6o%GE;MEk&x|NGGD^TUBOxu$r7m&6uAw!13y6l0NIT~g5db*viO6U(+9bAky{fAd zHX&LICChdKCe+@n2^Exz!C-!vuv~g4tIi6@41nlK1QlsgI+74W^Q^T2fHX}(o4DCP zREf2Oq?Gm|0BG8+R;%T`?{@k~gd$)f=RE_=te(yxX6FqV7QenVllav4<(cpPeVO8Ectu=@!rL47DYxLlRy?A4chtLoVLACo{3;Sjv?)+wV?mqzil^Eydj@4jRE zZ$IKet@Lct10jvNWgP$j5IWm$PMdzx>-Cp=X=iF(>*&$LJ9cgR!B2m9ef|aj9z1+_ zW_F!tPMq`|kXBlZLG2u~WuwWbHUcGH#7XcJ3UhEGvIsH&gnqt|1zIzsqIA47!-TLj z%+f~6l6bIi5Xp`VGKc^qNz!PyJKZEQjkMQqL`^LUoiIxj8x6R8eev7h`1aoYd+xaF zj?et+FaPLAKRS2roDe7i6Gv&%@Aj4%UC(z!rOkEbp2H74{qocE^9v;AN8kC*!@d5$dOXBxLL2I9lxYPRyNa>&CDDqu)CI)91hPS5IoKm5PxC zP*A8)G;%HdsykFDFV86ZW_%-VTRQS5DMno|ma*kfvaZ?(6w((kRLEKOr<($_>{eQ00l zm@SJk8nVW2AOgUS0D)9lHb53`9Gsx2sL`1fECce&={nF(a7L8+Q{_2A5~>uUVg?e@ zM97HNYXJc;WB|aiFk8qE_mZM;U~U?wvFs<1)9d%~qIj(OMCstdrE&L$ye zHDF}8;!#*qC~_YXk`bzIwQ9++kyu=XievQEhGU%>}RBf?4B%E@0Qr7KFB=7?YS##nMAXFz0?aMMPhV%Fp0HFqe{M#_) zu@=iMZLrW45K3l)LyOVs!jh$uiQvm(F)0LlO$%r#&c**jlw-9oP&=P{|kd^oFOz}bkPs3urm3l~+X zixD!Mua8oOzSg4{Ui@3Z1jwu`5)l>$!iZjJWKI-8c2J64rYr$CpJojziqRYpszSlX zEK-qO9-wT}-;a&ban$W}_UzmLv5$SM(QG!G&8t_h|Mg%0H4&}!(`K{z$xnT9^R}&Qxz%cX z@4MeWapKKh;spGm!-qfd$&Xpbacr($z53-RpG=olQ|m#|j(z*?xaaQu2M?^Su72wq z-<-d8J&K|&i~ct|H=bfFQWB_KKhY6?zp43+L>K9{k12* z{K^Y2MeRtuN77a#^%fL;;6oq0?|}yrn?{lO!FT`u#b=%aMUgg1udftEjpqIDdC!hL zyOx#~zVYOfO6k;kg~SZaW^4WW_3PHHo1dSbyK)&kgNR1bN@I*#YnP-+l7z}YMN!>xzvtRB z2%^|iqtS5PRR>Nf1%&94*%_r6#e4D0akJ&D7eXKm?aK(sJJ)D7lGM&$UV15g<%!3C z0l-g9wT+?(Xyw|?zxvb9f9|(_0~kA<4k%jjebsSO&DQMt_ES%O)Xw#rivGCEOk~oN*m{S-~K}zH*fD;Usft+i`?h+t@W{Lp(bW_ z{eG;p_dW$iC6Z=ToTOB+$T5I&oUL1RjDYb6%ZQBnXzP;P+HjG}l zcH`{XvnGxN35dY4i0CNpc`t-fBSs`)P|9?cyNUH$0W)i1Bobf&4Md?iv&yO}m3S{B zgD-eep#=q{EY$wZ`T2mGdG9I?zD(c?1AsdG@F!V3FoSp^aA`m3^^`&a$dr!Kb<5t$ zWeo{Nq+Q7ruFq*HOC`wFh@nAnXk-8|Ml&;E=Dk2f6l4)n6xwQ+bOX$7?utOI&6$DB zWI~2PVf^gLN{709Zh;5~%fh@NBO+?8opnSJsW?q=-@!YM9Jw#GN+{~3f)taAtdlTv zA=WXjbQV`u=T(e{j~&{$dBe$5CtiQ+jc%{M{kGe-Y`slslUhzZ3lJ%zb(AGS3uMJo znbC8L@Z8%cSLc_SS`p{voC+^*o??d>8bqKB1G#clHhKUP2pT3LV#sKS;<%rtN=K>l z_uO;e^=mg57MB$1$TWPCDn z?RFc@xYzFh;=zLlu3x*6Ch1gjiY;HecKzm!o9kw0cJAH{BrB^ch-7V|h?G)Dn5IeG zjFcut;D9}_ii|Q!fDi3I@XU{Y1`ae()}#ZlqNY?ds}VreWRq6!JpS zGs-AUY0_Jqzuq)TIK8=e^UB=iI8y7^uQ!dD6bb+#YNZfac@IA2UcbAt)L&iowhwH* zO_9JT#nzz!5_#dsM2>xs?&3W&8zQAtStH2z8Y*iXcU3Hz0Zo;!qM^b_>N(|nYhZ%` zAPuD#78=#4r7>z6RXEi|9#(_%L+dK}TGldbt;$&bvl79meWrN-I5M34(t18TKD%Tv zKF~mW9)GYF$1J{h^@M0r>CaI^dQ!yu-9j6%G>72_22W!3{;1@teE^_dhaN#GD$l3% z_R=nl2@%vv*z%tv@tQ1ssz1ljw<~nolB@;Fge{)5qFZ1{ z0roWzHH=$+ekPN~zNNBEM1Wi-B8KCILa21y?IzoI?mBkYT?;F#aeM0I>30^Fms*X6 zOVc=NeEd_NHtjYO-I!mvaOqOJJ=3?g*=~ROv!A&!zwpH`{mrYdzj5#V_pRT!`OOn= zr|chl{Jr}Q9Qwjv|MgFv{#idsKmMsto_PCYH%&~V@r$4RrMauuzVKIn`NrEP?tkck zD2h&>cv~ACHyZDG3_9)6_J zY@RuLP7n?qIr8Y^kA3;8U;X>>-9eTkq@6dd9v5(BBN5|oZr4<`=+g1E?>LaYBtWCIgJ2_gaQh1YG#H=31UC# zuP(150EP@&06{@SVJ>^P%0_$6@P}pGNPlA-HvTIlVKk*KCDdAL@4a)5w6XvxV(Q_M z$KNMNzz`dK_ROi3#f7Ox6ddYWDN-two+VVe(d=7m8qL0QULML))`np!C(P+)h&As{Nn*eg#)~!lYW2z<2Up#f@YhQZu`JX=BYQ&xy zBlW`gO{n=v(vCs^pLtadw5qv5^RN^4Rm<|AqUG6q@OhPE2P!lP~UPn>leQRdjUH3hZS}%Z6tY15R9N9LF${N~; z2$5v&9w-&fEQ!>-7G_RaWj-_Djb#GQ$#35Yv6drb_ z-{Klgw`-90@sojUvGn46i(JM5m2FXOUaWWAOMOq zgDfsCCP_ksNQ4aDrgIlBUAuPG5KgzJVr{@%-|zWe*LIif>T1$i?kvu)E-%=m%V{5l zQN&qmNGYu`^h*wc2T4Rw*pLd81vum}DXr}mD*g!Z4sdViTf%WaFF-dcviMo(P)t}+ zdFu<#Hv01*W=EAGfLa&ZqVW`uDhOl@r72a^??t3;Ov>RufF=KR%&T!>DaMj ztDVl7ch2nHv-dp@KJe{tfBVLb8@+CCW@h@}fdj9;bUZeZ2%fue;f)g~9((-px88Vb z{>Hr4n!Rk=yy>BbAO6-;Pg&#I7VA)ikr4*7v*p z_dWi;g&RxPE?teICVPMJ!aJKcZrr_dPiLij+qNCO<=&-p7n)HMTyCi#q~aG=7Q1Nx zT|uJ0f5-lPkALKYANlkre&Hh@e)xTlKk(Qi_dfd2JrCTsWB0D5rNwL4ui4Z#5UhWZ+ioo^2D;CeD2vG<4WXQ>~Viw#O<U7VBeq{QjLTYmTE?hYO|7Y&cf+abU13^$#&CJ~+;$3+o6Nv;8NF4BEH>-+uuvpbB zlG9}O^z=;c_Q>{ptnBxStjLP&{OpR1$ga$;bkBBAv&klhWRX>@lXas|K;Z@oM<#&0 z@AKt5Bf{OyOm#oZ!^6WpBHqhHab`^*iFkgPtEs7}>1C?V{OVQbdE3q%TNbuC_IW0` zcZf(@5rH8fq&#=Z6yZKbVLg%dao{=@Fyw@ipO_-;d6$=VGNvq%OedjyhdEXOH{MN@mdhosjKls7-5HX2k zr7*Yb*hPt6?{`1_xlcXw+^?2aZX{+#lj+1JUGM+!yZ>J$J zmc_AMK7aZb=g#ihd*^`%A8yUgv|24|^K5M`>#pW&YwI@_ZNKNOBlgaDO;8vqZ61P) zNQe+UGXiL(MFc{aUnR(S(yDJRfk_uPFRzT1JZgI56nN_U*(i^KGRU*B>T&eRTNbc3 z^Wug&@TBmt&Gg@7m}Bu-tIAM5He%hV;-~T1+S;Eap-viXZDtY2cXA7JG4_=a!l*F4 zDtO$$@K`y13jL2=!j+4a9EQXu-&}M*{(wh44^nPl#r-46t zJS1bKtb)RMnB%U9swGlNdC%Va{`&e{)WSh{nlakto|IZ!?JA=Qu(#e-NNgP$-RX1? zadmZ7YYj*@Zrq65t*u+PT5D%!X0Ben8fgQeWC-)j+$@7^+p)c$rE6IQo{>I!H1rD=YK!^8}~}!~u8~WSeEc zlBbyhWM+*D0GxALYeWocA)pWtAPWcMHM4+LikYFpKtjpNa=tcPU*(fchKT|UqLe~L z@4Z$;CUPR(EWhjSdmee@k@bEbNzc#BKKqNOu3ov+N)m;l2vCqc5FsiJ0xUq0wgOQc zfA#nONxR+tu=~h2|Mu^$UAuzF?1VtYaolOO*L_a_KKuFK{@&kx=jz2PTNbwN-@pIT z)oZ=ARU=3&c`t28k&Y6FvgfYb_ujep&9~oLTwiL>bb488K&Lj}v13PSbCiB;jLXe0 ze)O|LhaOU>BW;o-vDSLU1Q110?wtc!T3?;tzVPMW`^tTX@3$zK%`@jYx1xz6J+ou$ z1KYQK_`@H*aO%u={`OnP-#xZ-+xFDu5oi(dHfQH(z(ZQCR@UuxqQn5j+7Kv563CAc zNKuTmzOwwwr+)sqFaK^ocPRdW2OoUn)faADx;Pta0@PUWw`#21SWZ&g$fob_&38jn zieTfqkrkR4C=5Kd>kyqkiGA1f%*sKd;=igDZPFOZ{az|;NTG7hhl)TUcQ6roX03IQ ze=DV~6vFTCe$H(=g!^t!2KWozz6R+aNw0! zU%q(dqKP9!@#2`d-|t7-FoQ8MXY84cF&9o=yn5~O{f7@eaQ~sxXHN8NH)%yP3rTAx z?)SS{mS$OosJ!=mmr{En&0Rn$%VJ9EyPBg64H7sZ7xA zt%)#+Xsw;K;G8fU6Dc%?VEMwC=P#UX&(H0?{dOV(_C8B}zi<1!tk)BpLT(eIM8vYy zTB9Omw1@;Ry%d&6K}4k#D&^dyAjYsTsJ3~Glii0|rfNej*E=>6IaGKD46PoMtV<19 zu6(^z32H*j2#~>6I+oHWdt=_C0l&Euk99x|7#-JcO_JDTpoUews{lo+m9k$hKQ|nE z8BU2szE8x#F^O=zZTOYyNzp+DBUOh)WHeT7=yqrl3E~Da=u|oWCVXWw;LxF#!B}8u z5cMJzPvfNNt9h;_=}40l<01ppX!K2U8=a(Aln-pE^iFRz1$e^|)jrf*Cbg7HRZC^# z+XV$yx6vujz#z`2B5ee}B?ACZ5py(_Fh+Eq@F^M3-x^a;{#ngcJ5k`-OxrxsPMI|- zgP~w(xGsVH$1^D0Y-`H01Ja`EGu6A(U9~t2SsOa2Ju@j4;wMSKW_e2~rL|VtGLt9~ zG$yvzx1tsbYOO^CgoM!xTQ811gTzq_LAg9fL~SAf)KP>=>o|&M+KNn`72YUoeXG@p zl2+d`GovD<453y^8Dm5w&+{lsdc8EuECAUw1t9C$M9KPkPnpOGiwKf(&h>h|`T2R$ z$_pFR#zYXAxRs=7s*JJLqDY#i3p2Bb!pvH!+@|3;qcDR=mSw1oQ-!J2T8oGZDV$se z1mq%-IaKx+s_>A(VP*wD5DGX{vN0F*CKI>In!>3_F*6d0h-HtYtrZsd@JBypJu`Es zWlo(u@zl?LIu~gZ>5%q?P?16};H*P!R2=63>sjwppZQEQJG;EPx_y2=ZY5ck={WLU zNWqmWSKfT{$ic@Bb$i);4;(fsx^V9N;lmH!dFP#%E?>ED=FD@y_~qrZ=M~}1+{`CF z_t~wt?VQ;%w`KdbN6(*G?XSggthJtvI%~_T&prR5_oCY-%Q6x4PK+_mOTXX0c=4hL zGBZ2YI-RP|Wi`Kf`Y5`&{-g%#8SuT>N`TUOU|MK7bxBu|n z?>+UiUu@~jMp}y#6mFU5yWjopKl@kzBB+a7wrq3kRidr;3Q!r%IV+_?swS-|7WLMz zUwiQ3M|SMl8!EJa@FO4n`~T%HGwa)t5*83vMG{|x2`-Is8r%jofk)Y3ImHNt8ad7t zIq8!dGNz{D*Suaeb=TD5kRD^K^fva>-g~2!Wv@|L@5pG|@3mSj=e-gL{(GqGAJtDI z)ECbvjtWr6+GAj{Tgu0sq9##?E?UmQn_bijLW@q?%{_YwmF+7q#<~wWw#4DkhxZ}Ki?WI@WdFR-ZA9~;CKl`~8r;b1O;tSq;P0D*8czUE_o8?NG zR-CYh*ihC>-+trBrE@d)9NfR7y$vJRO;@j8y{t9mHuH{&bfhEBd@s$EHk=C!myu|7 z@h(Wi6V=GN%u$oHa5MrOD^CJIVxracb(h;nNuIbTZ8EMIT8 z+i@HdvpDN>3(lf=trd7L2&5G%B0?+dg%LnJYptWC#m8&~TCVp;~*N^=~`FpHuPGm$6sXDa~ zhw1xk(2gqMwz1&lJoAULl7i;Wvl*&v~LmS`QCZe)O1gy%wLpV%d zBrdNB!m&nsBQO-1Rz`fSwS9chAxr_R zs>creU`2AQ=I}SfaFf(0+M7le%8Q^-sP>zPctX6} z*cYJ)V;%&4%bN7)@Tg? zS(X{4gX_#%8}t)oVR8x(kg1yRW6)&WMAWV{tur};g8fH1RCDkzfz)VhUB0ohZTrqU_wCn_xv{eBkRN{X@!M~|{eSqs|39+a zK5*B*$3OV~i&w9ZQY-6g+qZ9D;4SN`YYVe;OV^kF@_+u%SFc=~>CCS7x8b`%S96eX*@?z+qFI(XoZ|EquAwON0CEjD_tGqZec@!em)xwyO} znzn7vR_5i$Km5@@`8BNgQW^vyC{YlO6oXVUxyiW0S^+?)guYB!R81|n__uO!d?5a! zcb>E%rN~j~jPl&~;7z0+eCXg^2kw0RjaT0OCP^~4FbfL2cgxEw-up@A| z5mf$J$?)c!6F_zj0EtNJ=<}cd!rO1Z`_l8T^wztxowf+)*6Tz>(_>(_Vh+v6l>7G*7kG1eo=6tqduQwDC4uSz3MNrI$YN z(f8fBzPR3B)-k2MK57&ZcCy}GZ(*cnjdN^-1f*I`u`1<^TcVTW3CCS2>cs+x6sMDe z^O%Ud_p7U`0H75jO75)I8ik3#+gvLRd9hzLdVQ%rXO_67kl$pcVoUGn+UHF*yMYt+f~So|CwxwPt1{?b&H#004xXV;z}1 z%VI>O6mvLCbFo#5@1(p;kz;KojK>>I`1k%E&lmVcyS;cW2 zMiH5aJ)|yo-UZEBDV67Wq*M|`$ddMYs7Mcpw%pMUYC9U6`Bknf*`y z_WyhK_{pr-S4uI06`q-!z5C$3U;NUSsHQlaW2y4 z+NI0;@4v@--m!bn_B;0Ume!Sytzec4i6z2t27}o9+$LIGK7a0=qi@}P@ILRoSL(h8 z9(?}kr`Q7kVR0~uv8=PO(P^XMDigp#t%<6BG*}uV5v}ERKN8g1Fc8*0JcgdCLS)nE zSe{NSFA{}(LSyWXljwDVsa?Bv-F4SpXHT5M3Y!T#R@mli z+;+tOQ|t{D?wtHtc?OwQWp4si&w=a%koAobUxEM-A zf`|@#a0q7=6k#08pevC?rK&$^7V2bI47s=x6j4S1A*IL*p%-9$`NfxQjvxHcNB-cC z{_y1&UwY$>*NLdzN%A~18i73#wqlj{djhW4T|@N5YGJNzeIB*rZM$~#Tj*NgG2`YoiJJ&`tnQap z3A+rmtNJJaAc~^v*RKaNB!*&2N(Etvy(6u>cfhQO*xE#CL@&8x5hBv07+Jh90C-lB z@ywtI5HDT7y63Ka|JVQizn|T*aQgJ=pZw^jPyOsCYgeyspPQdaT9T!kkv2w6Q{yYA zO+rGo9>8>Tl``uji`7^~C9L0?-Kr}7PT36YQ-!YHs`b`1 zCyjN*G)Fp?_{ZM*P3Z}fx~&zccFt*CRLPFWco8B98XdAQGYg0P6oS=T#F&Vgy>&bg zogI5)jEIcqHdL{Zf5 z_d~F&bI#_u%dNN0kP31s5zWuf_qtuS4s{eKiFK}@WqF<_CJOm(ocA-GPJeYhJnynR ziDNG=&vRh$E)S_zt+mJ!)ZGAQSbroztPf@Vgqi&Sy<;N;ONQ2r_o-21(@Z(+K}ac5 zq;luHpox<^@4CC2x;Snj;+wC(G1E%Gxt7)lTxNpJoy~08nw@#yLr)xj^wD^BHuJ9E z@6XM&FP}aC#%r%`pPdC~6=I}~wHa#Y=$7^Vum9s;EnZyw=tn<#?#!9*|LBL7{J;3$ z{Y$O2ip(GXi$7UeS(|CKt#?b;ZhY$-f4g{f@%S5WAA9xaj&0jseEx-Nm#>l0k3Ig_ z@BaSRB#N9cC~(fLudK#8e&xBB=2{&MXsre0)vWimuYW}ptu3$3&(E(ful%R~>EHL3 zmS>~Z!hB1dX9Q(*>g?OEzJB_|sXzSZf3oBD-Q8|Cnn}L!r7!)v6aTMnFKsDhOdNZ6 z`uK@YeD*Upx@$8tGynDf{$J<)JVLER-MDn+um1GU#pWHY+51Ro$3BUpg_+Lt&pvzT z!3Xj@?dRG4d+vSV*=Hl^m7j7o{xsEMqqQVAlx>Vfew!xy@RgwsTg;QEqK0 zuxhQ%vaAy)D3F8GN{RPMhXSla!M!P3LF2RoOHmfAfY~zuguM+BWsndB2P$GnC0E+A z0RRa?{V@z#e-wmB14Q(B?vePvD+Us;$2M!)=CCSB$7rU!n5zKNMX@i3789jm*0)+xZrJ(`@ z7!j3_ALec?_yrM3rE*t5yD$`bONj0$X#m0*m?AWbLv8!Q?Gp;KDVvM1I(_QQ<;&lG z@S(#`zW;p>J@U{me*V z@?P*oJ-|u@@EYW`D^(eyCM=etXhy}9Gt2A%hPwt1&P*~-^43mN7XdaUvX51VO1$~O z8Nac#*VOTbImRn@%PRwe-C%wv*i0u;MZkCCmErz7I*H>IiJG3)L{61paNu7ptH3w4 z#Oi)JF4lCU)FDez8M%qQ8qW-WhB}JPrPNH3H8Q~9732wP4TqLuKgg$0oBC$|1^q+a z0b3)X8nm@DqUB@34p>e^iR79$6DzE#(#7y@*?(0k8(W07ity&G(fZy)UJ;h+SWUY+ z^|f(T>8I?l%D9H(RYO+LoX2urN-KjPij2gVuy_FqlB%~(X$1h{okFnI8l{p}%sD$| zP~?2B5xlp~<;Iv^cg=g7BvG379P`$Nd6(Pn+PX5vdv4EkNNYhgJ3CLLS8l9mqSfVP zg(^02p67_Tb?erZ8%s7%frYdZ@a$}CG`n2b0c!wQTVIKcLSUn*+wJe%xqWqU(Wj|6 z+h1SPTBBBuZKu;-n4f?9ts`0?0eeOU$+B!^Wo7&J?ciA{)t;GMn4LRy^xZT~TPD(4 zTWe9kdrtt!;|4BY?YrOnW;>3+S%c`)+#Aw_&I%G5rRU82uYUB*vp@VLY4pUY zWUs&Q>XRRQB32j#k$CfAgRI@}K?d|7Lb>uHQ{}?b?0dz`>JmzpX^akx__Cmlw|-KfUL! zeZ4$m#g0WM#^!eCoqIp~r7!&bH~uDOZ6B_YsFt@d$qS16wk zuarNktO< zv}I3bjAypS<*VS;B!=b=I#z0FiUP&mq3MtTIfRPMb~-P;@ZybYi~IN8voO2WrrxFE z9Ytn_J!utLFU*eFdGMW?dI2LyN2kk!f z%8JX`Vx%5^?2+q>S4^S+J#(IAeL~SliHcp{5))W%>4Y=?{4>w}!}or$ZOgXb`@OF| z{`lj0o-#YfHnS-aL{ZdT>v``Z9g7EG$ohGnIs)BWPf?WZIdaNnKJ8_L*pd+vs&1&0 ztTmHJrAA;3MPAw9W3S3gX_^)`l*-Z@?5qP`AOr<`j3^3$K*}&HMrP#M*;(rygsQ3l z*1LNS9CRS9`T13w-{`ORg#XpQ`ENh-`QKSyUlRm|F;=ZfgKa85W97WDT{Y#%+V3Vm zsqSQxFh7Zy%Q|M&DwNYZ+2mKI40bcE_K7B1n$jv%*Yf7#<(l~4%=1l7Sc7CNNi;uE zWB%3oZ9-Dh;wB+#3cyWxsV zc0Xy>nqQngqOm$;ZP%_n+~hgU50wCya!$3?^q=bW=95#VW)eQ7X}_K@Wz)Yn=@Vlq z92*5=nmBCE`*fNgD;sS7^_yzLnI+_4zn4Ma9EvvNTuubyoC>KB*%^gKk+&HjE2TtO zJhQj#yh3E>mKU$DF0VXr=)QipyD&HV$Zgl4?l3^&A0M2(^_j%F1MOUyw^a$0xSw)b!j>4^&dI> z;QGqy-TU|7z3+}=N8h&no(8;l_ROxGJCjHQ%iVY0HQSjvbK--yM6m%rBfcabkXU7690L045@%GzuVMWOVp$fwVzBYNZqcND=G};ee%5 zD%dhgIV8v!#+cyMtWIKe-)Xi|kAMm%FRzOfu2JiG`}Uo}2#Vr3zH<3;q!fBbC>jA+ zI!UYF?`K&?1Oh(G(yZT&2xpQwR+RU;-}%Nj((ZbU;PPC&Gny2J#Eyup6srWHj_S;s znM9&FJu`1+P9HtKeP-d#-FM8lW>+pRE?u}rPF{ZY#YA+(G^g4#y0g$;5R3huJ@mkX zxk#_ykK;JfdZ9ftqvI=QFMZ?B|0-4n1$3~IAl!H8eim`gwUXrZmtS4HbY)h@8c+*r zB88*{HJ}kRf;$(sW^3K&p86$$V&*i@9(?qX-1?}MD5YYPaBlzZFTZj5%*7qETW8|- z)~(yEW3SXwx4Y-AyF_UK5YJ(jQGkFWZ7y6mFM=$hlxnxzr3w}n%%lQQlUR+&NR(w$ z&1ueixUR!v%q+~o*Bx$KYip(6!YNAcU6WsgP*t29%r&)Yvge1iJ5^=icp~OF}edBiG+hC)jB~%5RngMfEgiFnDoQMJ0yb4 zK!OSsdT5zw{l@aIo_Y4|qenja(GUOeKmDU$J^#$n<42C4K9;)PYPRxU{@$Npo3KS) z?n{Py@4ai^y?1Av<;Xr1F#>PJ-ErrhSAO|oYS+`Q)fg*^n4Qg25ksiJiB?KHYSIem z`sEwn`0Kwtc>ldmyzj~T@4xS-PyPJTrOQc@K+Xi1v|`6uuh#=WLY;Q=b~~|oN}%%0 z+MKP+OkzkVA_L$>uy6^`kdSZ?4viQh(u?f1W1BQW+KTHiC;$LTn8UG1WptM3-F{C% zP>}#s{eco928uZa0AfH8AS%>L4?-F278p|CwCAVvKg1X^7H6boXrY9{$Ib&mMCw%&CdOfo1$7cCChZ!N zG}X=EzuC_S0xKTPX6Z{DN1tQi$HCc>4X1B2pw_a0)(}3Hdu*mnLgN(J#xBeaG2Hy_ zD70fer3ek9)i|23o`!8ywH^6-nR9XkpH)a|vhozX$sk--q)IXsI4f+q>N%`FS(OUH z6b6L>Sc1QCl6pCQzZkcVV8uJse0>sCPcq`bMs0}Ifv0^E0>{1>qzS>g^eBS}xG9=> z5sM&*h%UE?iU3n*6`{*gtglJy{FSf#i+}#l71GSi%n!f!UEfb5rJ4E1Kls5XKJl?b_uZQ$ z$vbZzdF6%YBTeX?%RPEW!V07lN4DS7owl&+roCT2_45yZ;Dd+mKiHn>9Dn=hOV2;o zi41#r_vn#FA9>`T{=wI~-R|B!w}1QZ{`T6XOGy;lJPSEy6v2@bO326{f3W32pk0=04fWYFxl86!|?1>Ob**q6^)kQa)S(Z}%lfo(O zl_qeN7=*FZKH*B86iKlNfDn;q5zpJUZued^k|A~F(nU>*fKfOc((m{CT5BN4^PJhA zIeqe{Kl$;Fox8eOdgI3OsZ*!WyLJ?jc&!MQ##=lvWBE*(4f zr~mK&aQ}mc1pLWk$KQJ6$eta$uV1>Bu=bYG2{}eoF-97*ZCiJBSG$JH<{u|#? zxkNzMt}ez(w@s|cSZf`CWq;THd;0x85h;dOpMNn%)z*=CKtxpr&qI-NhBH6cnmzgM z37fenY4xq!xBu?AGb4;?mPXnnQOo82Pyg+|-*wxbZMWSvw`1F*ANYVHAZ=|Jk=9H^Ch7LOue|coT5oOl?%U?(<`$QiE?zwU>tA2XvTWOvBBFo?89i5t zpaeVs3MfP;MQ~9FtmWLtI**Kd6L@4k88p#z5>Iuy;Qpa1G-S68ll_>&*J^S=Fy zy&Lm;wxw_8S(^Xer+?p~SP%zdg$*gt*w1?V58eez*3&ftTI=mMl)}A zIAx3*UV+icnnI160icG8>AGT-!fP8PR2F8n+nSuP$qCxzh$hOcwPz;b5U2Iigm$d$ zVC)i}0&1<>l8=>I-}LF*5Org_tpTv1jjzf#ps~KDkG=Yd4e5D`0z7@lQYO0y{|lLo z1hg?(mbr`*9jz}f{p?3SI(Oz=OjN?#zkffA^Pv zx%>7zR@T~H4qdt4?^!2NYM4*&GkRz}E`ttcRJ8#>) zva)pU%voZYiCf+|@jfQ)`q>Zv{=3(%UY%_vh+qL@(yzYw!j-ebJh{QV20EW;x|INStoBjLuZ`-!*S5N)? z%&F5xp&=#g6e{a80!D#L7cQ=@ECaAcQZ&#BfrRM~4 z3L*qm#<5pvsSJsNO*9cH)2@0h&OI7qycb0B-gmoQ0w$tx3+BM7b8*<`kpulHtJ9h>EmiNX2# z_T2K-8y7BIafS{(^oW`3uy_45_X3ED#ES@fK#go2f?8c(0cH`g)+R~Yd#iVs( zEFArF-1X^8ID8z?$Y7hA>4N3FJtn z){Z!^%2mj;Fz*T?=FWzlDR|rSJdfKgMC|u_NfL`lr`@`A@#5u6mqa9FJ+?0Qp7-zH zzhlRaS6+F=TH8tzp9e2XCC3H^bJz$Yt1wC|1^U4Q3IU76wpiu40E3cifX%3tAtp-& zm;in5*$Frh4+un{042@)acmG_W~SZCyZyZHQg`+G+ST)BMp(q(Jy zwr$()y5p|pl^e(2Io@4ei%pC|Ia>vanUR6HJe~o_Lrh0n%lGGdVlHDAOFl<_uaGDTM-Scz|`kmkTos%a|zW(~_Y@LaewbohA zvfMeybE^bw?q_G)cOSU3owP2UJ->Zs-WVgUSOvoaPSrJr6)GAyc&D4{YumhYK2plD z*IIioA@^Fp-^bCokrCg~z-vgNU|3<0%8ajrcR1L|NC5$-X&$%Qv#r*D_;>&IzC#D^ zf9%n<^)8tx5=u19b~|0E=WdeI zX3|KTQniQx4K<%aJQ`SghI&y0{;IpF^g-QJYpU@#OIatU03w$q3h#@!Vr9RAcpLsOp)+m0k{GBK*~E zYdzN@(Z-W>m}MM+P(*-0K=P~~=@>LQ=9Q)8_0=`-V3Y!3^h|(2z)p_5{%e=pPSP<5 z-sX{sSm|q*uUx)(1r=$nV--Q31M!U3y_MBhfAuTpxYKF1b&M!^zh{h!0IprQaPjOp zrBEStTCKd_D>Hw1XWx4L*Re4~;2i`dsuRaIu3f!){#@ql?Ch)|#mr)zB6Ml?^pAg{ zh_t45k|;#i?2A_Z+pEj&m7;halM6x2@VoJkUc1mTdHF2c1Kl!yo+5fKtFGlcv>pyR~3 zoCKJqrQ6 zL!>o;2(GNGX)@x3s0b|%AuduHb`cbr%sKYb%d!Y(9eXPfC#`%f^!Pd6F7!)mrUCRmL}zTt~TK^+$0M5^?9wod*vd zR7&-Fy^yg61tM+47Y8-_{r>Fi>}`8?U%Ytnz<~qRM9)nO|-r3E&YyH)z74_3KX2&R*%hEjU_0~f*Ct&nW3~Ei9n8b-9GRpkq zCqH@m=TGh1x9{_x|NO$%E#3Zl?i~?%C+llnmwCeYzIzWHJa}knarxyJUcPkcN}lIj zp`S85x`xIH^Z-qQZxT|u^r{`MudkOmjjG37Lnl=>QH2TsK!}7CV)#!ZDZt>*1b}vu zi07HO6#@Mp{}2D&_rCGX*(gao`n2C)>+aaHpl}H4q}E&=V}<+a%JcNj$5Pr@;)D_D zQzm`72|!J_8QV5g+cxu|4IiHL-6m{Jrpm>cXsb)@FZpk8_^hya;hmaa#I|ml%}d*ugY>`5qk^b&~yOC+is@L zTJ?)#n}{G+>Xu0nmZ<>6Tk%Z5t=Je+kv2vXu_qL8-e;Kxh)B(MW;BB2IXXt~Aj@K< z+L3ANh!_k4dZ*ZPmKu>Zsktat-siop&r%KI(mv-|qSdU4+GJv-^In>0!_E^+M9Ltw zjMgAZKypXSAPm+U#F?bEr9ER9zpF$^9ktU=j3V<1{5m>w*zh5$7Yqwu#A$@(#U2U}rNRVE{%D z8aVwJw4ebpKpH6|$Q4125qU-4C^3<0gV$O#GKmvrHgOzO9jzm4t<9ayopYS#mXSak z>jh1OsF*g(8h2&BNLI{O$puJ1s78w)~4YL#Jn3;Q7 z4y2qHAw|k~CnDmlM^qx{2ReC(O$!;hg1!@uTvkmsW0}l`i*6EohpBs0k*O7h5xl@K zz_rZM0~5HgjH(<;jhs>_W0@wg*Agw53|{fw3OU~j+kb{*;6x9>S|>eO3ry@e=I6g~9NBRh6% zJA3y0(YKDKX+G1Lz4MN{4jwpg`uIt2S;Qq#EX+D2Q04l}OKE8W;G)ViGI$VTmNIg( zcoJDagrSnOtvnya!j*u=f^-BCkpYmI9Uu}&u$_=WaXvAzC&}_YYSCI};GAG!*F+)_$qsb%CSF$`?;d~I#bdw%!bV@sR;r2au?78js>({Qm{r1b=`@EmFlw#*fqe-azCJhs^RIQ|AC~9K% z&hTeQhQQt{rG!}#rTu=tn-VAj5MOw(L!P9;N(>7P06>b`LKsYlg=0quAP`&lv{wl1 zEGmTzNZ3}ITAlyf|M~y&mw)!>U--foKJ}?j?b)$w?`?Y&k}v}h2$XC=L=dJ0)HT;N zG(@rH>fU6lsq0OC0+U(cCfgJ>W->Rp3HWaTr0O<5MHn564VWMuRxS?fd`iPJ@E*ro z=|A){!a|z}Z^HSWzdq98SY4=GdSl@kYXO-8^DU^pQ^fbpw9OM$bK{04swYIEj-7?E zSZzL8BU+s7NuG>D9`SgI0AcA@EL^REFky-}$rEb!j+>g7aTWnJfPB3Fe3MN!ayX9# z0~J*RtcKScIq+*3sE)mfkV!028qTODZkv-nX1 zg3lcy3ZnNuwK)M2QEz?K7-L9z>i_|qi;}q8>nBM>EH2NH#YqwPpp-JCoO9wr5V#l1 z-Z=q`6$v}ea~&D)#1kqao29HtA%SPl9>fd#B4&&|djUnofiEuzAiyEz2aAMK0)zD> z%pM4d2u8irL2eJJF3aq!gTy?+7f}E*}^$Sl%N9xiy|Ol6o`xtDrjVkt?Vl#gb;krLu$;$8>N^*l2)gk_j3|zQRMnoDXnw_9);04R$8k#o-robvVc(p zKhQ3EV?UTF2>^u_(nvO$LK#R61G~iltZC(#Vf27mh_ns02T4YF9}zk4iz<%J zhjOkW636lS`nmxU0HxGJ4?T4L{Q1k*t^!K{wcqdSI1<9bQ~<-H@G=NQ#L5}Y+CJ1A zCJwS-S8A|GS|2LPSUyWk%O16Uitp_est#I>92qE!}r{K;FXtNJ9qZd?92jq2LLS0 z-YHKkjwo;nOrlnAt^3joFP}Jh?BRzW{K}VqH?x`da{Ch>v|0Yzi?5|=8q|9Vn+$6O zs$`YOh2A_}IG7~A)5FapaD!3H6-ame6Y zY#1OYB*d1zSI znzf07n)Sf)CP%|@K~^w9f}8|xRVy1^%hSRT{?~6cwqE5n)gj-kt|h}@grOQ)^!A$= z>6AHYs&LK6#`ZSJ1T$%XwZVoT)#P>6_1h${Nm|4d6v zr19GF1Z+UXvP_kP@>N4~jnT;jfA5CFpFS6(kBy9F?1akb-Kx4$rOzgn4%MX1%}W#7 zn!r$gWit!8vRWrCET0|1U0EEWNu zT7DhCGyI@{tg@S2ZgXh2NTF1^@tM#@7+$pHMnJ8js8ChX43b1otDTTpzhlQ~1oQtAJL_Byz#E>`>iAwvt3=YPj7_F># zQ5^MqnNnJzMhrlq z@7no`2FP1$BIA46>gwv;&Yjl!+4(Iq^9y!qEz(-RBS1BOgdj436HdMFY~8+PVaLka zDzNm{yJ@fAjw5R=5vJCfna=Ni^=rw@EJP6+oqF3_Tc3#{7S>3>MJO_}Q^w@ZGw3Y~ z+ljO=I-jR$Ps9Tl33(T#ko9XkZ7`u@xK@$qWJ2)QaApUBeaVzG3^rF0G!*4Rl`G-R z0tiMR0V)U+rOJgROd*EUJt0uJczsO1%0c$0!C4d>oYO#n%yS&ad7j2mgr14W*<6#d zc^1d9_YM?!h9V6K(c0Qt@D7Q{?85x@m8JIlti62EIX5>mn`hZpy%iDie&0KbT6=G6 z;lknQ1Qbb?hM9Frc2@3`CDzI+@B`q>Os9iYD!~a^xGP0OkVzr=Z(UMbjxVv1jHGHc8tG%@r6ivZyl+al=9w-fOr8Z5;7Kn>0F!- zQAi-5P!$J+3LOFoNxXmvA{CxY5klpCk=Id*gO&sWEW*rD5d@&Fx9Hb;GrMnWa@$dl4zmR_Wr6Ky1cvN!vp!c@h^zXd2|Fv@KbwYnr zEOa-|ycg((#tu)%(UfXt_@i^_rE3)BEVG#Pbp5~o+XkL({7fCqfBU5KDtSE11i&P{;@yeD%SO z#Hfg*tcf{8716ExaZx@oZE|L!5ph%?G+YBjQn+vuG@Kq{_z@^l**Z5*>wm?+u5XXe zn);uv*?<6~QQBZkCDsUf9y5k|h9f-Zh%i|QHwo9M(T~Z<tmXQ(K2cBdJBKg3f=) z$rDHzGK**m^DBeP{1#sqcT|L+==;`Yg>%TFHi$;ZaJYW-@{RdP;+;QZ9hlqJ;BEht zivNPPSy3fw^s3y3<-`)0ML~u9c@slsQ@*d;=uJ({{pXH;ttXurra_2lNv#2N4DZt& z)Znyev#v9ejo?SALSt_6w+_kXnr0h%H9`esM6_gEq<2Dn0ZHgL)B`kZELP)*W(c*p zQ*<9?VtFR%K6UF%$BGFh;3@nT2X!;2nSlkO?LePm!{PZhwc~zo3sp4ba0SW+I(%l+ z&7y50-{m@w5~MxQ4%D{L{czi+!Ka2Mwa#IQ^+)euBcFo1xI3pR?K*e&D}!WiAI_Fy z)Ibf{mGv~rVth?8PhFn7jZg7Ub|Dg~)JQlbk0*z_^*sg&B_u^sL53{t3QgH%F1>qT z;|!w0LgA$a*M~0~$}U9Hm3Dz}rEB1KT|d%64{PGs4pGwRdOk->4iMI{#7A2lP=QF$ zUmDb2Kp@4LAlB&je$b3?&d))}<8i1TRpdm`0uOQ0rO;(5KzOL#6rdh^3r)rCN*Zw4 zlr0Dc>pfaHKAeU0flI9Dc&JjTxX(9Aj8cb~O)v+RRC63OVFd$2PJS6_Q?&viNqaw# zLm7#caNih%0&j;7gkKs*3@1Bsz{ux>kCx4pSS}#f_bj=o$6^a%qU36YK2KDt7(zRs zd#DKzg2Vxf?Fcb<+K9V0`u_?uaa&I+y1=kVHs zpyeAUrrf+$43_d))}fb<8omA3+lc*Q?LA&C_Q6W(2?&dm4Nb<7jm)JU!F167~Z8{Gdnorw42p zH#ZvM_m2hceJgGqxLUNynAMg@>>32oXS8CBm?>1*I?_>k3)I(}yTylv#{fOgSrwYRV5@jq*9W7*HE zQC^ZczL&oeY^EwQ*%1bo4{20A*Jr#Ggr()mewA*89h*^OdXQpIx<&Cedf{{y<%ZBXMeDyswnrzm9h{oeDTlN%L%_d*HZo!vZ) z*8_3E-Km$UL^-6 zeMT|S`*O8OMc=Bb;0`6uOw?N0jSBRfOjP#?|9U4h-s8GwXx0H^fd{#IeiLhAcrsM? zPT?si+Z7tC_cLDCBzQ-U@R1d{3g?%amb!OtL4^l}uN;56sNT`KO#z}==BAq;fBm=Q z$^oYiYly@mq>jZ+jm-U>dmBSpxpQVZ(g;3hLK7z*HV74S7cOdAMHX=x0Zf2=zfHke z{watmi^3mfs^w-uIF6Y}|I!wLM43PERB7pmX@-RXY@4wOCUFDjvcZq~ zJt%g6%zChCYj#7lRx(yvZ4a*)K=fBMTou&Lniy)%Mb(Cu3pz2|by><8!$SIW{01@b zc+!V;PT7x#85Al;2DnkqL!6kh`4*OFX4D`kI{562O(iit{`vLadJd%9eQT_&!5Uds zQz?T}F)$QT!A7Z&lorUZHP?ZqHm9+1YDBGM--)5Emcf9zk3z#Xps6QIPfHg;V}&=O zDSv?;M+25hUzUw?$O^i-6G-WMW;8C2H%51~oa9GV_J%4sTfQ_-B{G33*Dme>y7jM4 zHC5*j$iw!})JqYMZ}x^vuJZ5;k+Q;i!tJyc`UN!`fmaa)Q|2JuGfA96Gx9 zitoKq->hqna^8J4f^j?hyR6BFvrP&}(}HCc=f5vHt*plYK)Wp4>Y2hz-=%m|q)FB} z$9yB_mB8fTA%KU=QS>k)geVEeGd^r!Ox8w|z?z_v>x|HZ+1|{PZD@g^ilu=R<4g8P z!;`S*Ax%g^=i0K)?L{!w)c05W@R<<`epv5wUCRm0>ASyXJ3Biws=qMy7r)-7&z}OO zm`=ZgI!3;ECy@e2HSt;esNu)U^d*fFVMczP3@0+EEp9p&3rS*tv6L)nRIH=OS?&fZ z5@HLx$}TBDAJJ3f_?{dR1upiV83W1ojV8H;!_F!)kv--IKHk!X#B z(HCdJ1c@(F(2I8P=3=~ta^qplxNbckzZ8!TCq(^B6??qpD86XAKRhB*r;-Rgy9;~4*OdS#_oIki*ck;a>@`tw>)|6#vwOs)+8Xqa~zH%`)%o>b7AF<{mjn0NXVTs zU5c)9{4md2Rh40eI50UdIF3;dzC^iP^-iyohy)pGOP0K9B`HZTKF_Yvy8Wa;DuPsM z;OzbWnJW{1P(Yl~!FyW-^0J&L$0e0fW9z-5>BY)UY;Z)SCiK3cYStLfiD{?w+!S!tAICNkPpwD$22)4gnektyf%Pi3>|S1YYc5=K8?lpbY2ZR*$tVNoA%aX zqGrrVPbc^1VH39BspV$@zW%mmUAdU)2hb<=ZrpSYqg>EP{MhwoMIiOdkzH(3=AQ1D z8B1F_W7BeltHZgXt;ew>qikQ+EWG6Q1ubgXab_9rK z?7o)}n%oUxrd84VQf!I8$N0_ydSa(2)PcK4692S@<^1CA4%*7yDMF6pnF#85+st2{ zsY8IqllS5BpB07~Bx`&G$zuDmTIbfhn7puO^MCp-H=!IC!_O1hD%GidYuxBa6f$nF zJ2DRpN*P8=DQg;U*R}X#o5=)^4NJzxBkS!Ms3f9MlSL``gEo9B)Z#v284UHkiwyiQ zDv2ULFh)FXyC#K}mT25_bi#n%23g&DUssBQjZ(}JMz>@e3WaQLeH84Ca#kIm?wj;? z!~U%^JgFLFtqsiz%zFpsD*AdW?i;d|HP(r#E0l0+x3)DH`1)7W!-oI$KAPls(R+pn zoczd%<0(UYxz5(t(30NNy<9}m)`!ryjTbmzZqZ`1{Z16kt1=%d!Oh5@PD?mst`dOb zS;#Nqvbf)` zH^%VDGi{&xy6-poU^DBT>mudJ0fPUae{$UDPeLV(VfV!CZWnj+07};3=>GpG7Nef# z=xE@3+yb7uP-u5FDqa3}c-aV%%jL5o^BzF%!~Jy`3<$N6zACvE6@XtCmvlWo%51D? zo1z+2HfrsXYKvffkf}sM1Vz8M*+p-oi)syNNG@OHuboO#y&l0U0vkW$)a6b zmwrH)Pd0L8wpt;s&?FGE;CQ-A;Ms3yyMUaZFecBbowfKoq;L5i`z&JUt z76I-mMOWq-Ua;psue%?Zp5si?{+)&GDnGk&b!P3$8j;MUhtyOtiWgx`+w|w-$}p3~ zw+>eaoAsnTCH|eOhW1FQcXg`z5?Zacsu8Y&3&ySmzuyFIth}8V(e!;|IR8%K6B|82 zeO_JX%9a@h_035{8d*rza4J~2mhe6gAV#!G&yJU~cM;?ZEkM4soWD!t{N`bK$RTYK zzC!#_$ByW5=%>&Wy|#~mD352AtbxfBnE8W>FM)PGf-L{?ttCY>E**~oqu)&6{S<}V zZz#lAM*F|eeiRU`m^gTL$`fx%`wk`yNI$8*y2kNT=4y(pNc-&V!I7F|``3al^5JKdf5s2V@IEg|toh{h0*w%j5 zT+rtYp*g;H?g*n3aH|h9qP1pelGCuOaFE#2aqDchvT^VqYY$Ts{rk%m?-xoiFaZ@Yq~M}4+c*(g@QKaQTM$8t%1IPKUUGK3FU4b z-9LUTYv_Pb+unAXkC{^NL z00k41df^YBL$CfV*!R^|;T(42d!>qBhlX82`wM>(i8UNE^ctdXJPeG_c59pzlPS+t z;psaCKFd&z?T=0TH>fWEiAY9HxX2?rl2}pUUuojKpy(uDj2C2CuLIq(PHf#m$L;}@Wfhb=NIo@< zD(H5w`fX=r%JyOzc)<$&K2!jBw74OsdnJ#j`u)!n&P}2(=dDs=d%G33vQ&b)?KZd2 z@~_iH+FYny2uAIjwTh0As5>OB?3|%zD*i}1pnNL-?tEU_*+SGDIVy1!sERPaitr!MCf-W#Es7&Q+Os6SLAn_&Pk zl7`gbIVEEd#!h-T;l_rJu(}EGY*qz)e;S-n3!*9Dz|tvI^^nqkp|%d*`Qvj%vXl0ETYrVmcg-djxD9xO;61mVDYuw;9r4@&(SfXBNR8KOs(i6%Yw*Gayt^fG#_`Vh6M*pLk@;pHy4YXENg>;pp!4;nBgnizWDE9FI<18v{|x-($rlu)CMD+ckh0QS!F3xcTh0 zKf=tVD``a#j|y&5gadRwC(QmChHt zdCrN`BA$riZ)^RJi4y<5yxpm;D;L2I{9r(@x%0uJY3-fws&EnXJa{Z##r3dPA^n5* zP1sw<){oGG({}a56?)dOP&>cSmlV^pUUo-0;+djFnh(}^NZZntK9V#AYs_vUC|}EY zFT$Qw%oB{1I<}nO|E{%$ne_*FGU_pxdE|N(N-b(n`x>V;)G7l^&@%Hqw z>X>ym0z5sMQ`lH~p6b?dKZB7IrKK%mXo~4|sRqA_20!iq>At6V33EeZ(`Df>AnRUq zRoSYa)5rltpyG~JYRchUJBZ17^XJ+|c$iWk?B<^QMXscffwfRU*|TM?v(-yMIt8~O zbQs_I> zWSoJcw;nd}Px7f+0(a~tHcpW>dCqbqoSJ`3`xgw| z`iObpUA4r&^E$Vlo=sk4u%ytb`_{myIKNOZZPL#-|H8oj&J|N&1v_a^+FEjB+)~ii zhi@1fnpBz0vaj1M?#Eg%!0Q{!GV8v)<6fsWrcKzfR}Dr#P{pEm!Scv}v-R4nbeHSPevbz~9_~sYft_9Dw2MgS zzZLU!@7Z3Z`inxH=BM7K(ATDarySmE^pcezlAaC_0Sl6{;r;lwLE(?K(9qe4m&@Ka zO$pzjnj~S*U0LQRl-*$w=T?h9*zF)(TuWWKnTi^*2S7;!b?6HI17z*R&yaS<_XDva z^Y+*~Mu!J%L`UQQ%uKh5jHUG5L_CBE2Bcp0y7yReRo?bc%C*k|& zsHPd7ub{eoWAL+f{t&B5DEBi5T)11~Kv|QgsX%spbJEvNwUlu6c{--3RCSg`r?wQ+ z{*)+Q&)wjiL%oWumKnH#Hs__wA0Zqd!6O`9tt%W*HaeD!_0-&)cKtjNuAQFj=F*C@ zFHL5SGb!$HDc(D8EvOyAq^4)_7i2(w#OeZ~Wy`U*_){#kY*#BUxx?PdsJ;w9xqH?w z8b5q!a@*?M+d-kayqjAx0|O~Ak_=`%=aFZ>hQhNVao_9p6>B3Yy!sri%?B|A%XDTa z2|lK7oE&X^+PphE1OR0(e#8n{D32S0o1_pI6~yDbT8)sGPUdfmR%Ay`J~BZ&7VHat zx;{~{&w+kvcT%^A<6PzB!1tUW|B(O6p*9qN&0+tq3* zla6#6cl2ThpZ|1!do~@*i4NMZU7N+!Zs2X{dET8=*b2G<)Q7#WL4b&QJQn)zrn=F* zm%re9US1kS0-@c|I+vpdz$#!Fp+mu5Z?%8G^7=U4T0?7`q^T~2U?!^=jw^k|ifN^b zgH|ua&KLSFJjqeOc9P8Z$sl`q2#;(E3$z~nWLV)9XR~fF z<{2+6#%u4|B`I;J!`m>d+2;ALDZ079^H_~^FYBN4+8JX}$sRSyo)2l%T7GsN+7%;9 z;N!^e9~C=iw}^v^vXyy<^v;>~Bgu z2wdeqNc!R!EJ#B(6O0|9Z#xhDciBy^xZyT=uG3R*nG%nCCI23&iJA)Uu<$Yg!OTI9 z9Lu|kqHi*7eVbe0r48!0;p&zJZWRK#rvCfu-8L2Djw!J7%7OByjD&Ph0w+c|PQuTH zKa?+i9P8~OD!{(~`q&soVQSc?F3p+(?dt9P0I0D}W+Uc+wYKQpg3j1hsI#ACsMzeD zEN!HH!JUwX&O7+ck$jh8mgH8?o_4Rjrhj{3!RH9n0p}sG`CqE}cBr}bEIiIK=$3wN zj2FIJfA7v_C2la*V89#p6HU!H20jA!%&a?*X}W!W24LWeMm!=6*HHOUP{hrDzMVI9 z?i)-JuTKia!NH!6@8eY)u14&`JTLyPT-;Csk3#lHk>B#|SuK;>nT-IOtJ@#^fVuIbBDvQe@u9Jl}k(3c6S}b=nBCz1+`4VyO(}nA6 zFE;g|MCG%V{Ny_`*=t?|KnoeK$d+tW=@HIL+bG=P+}h_3?A- zgmgedUfMc{zYrtnE0-CNdwjh3PB(kMev)}KL%G+}{p~-yFSv&^4K+2As5rcSXn|HT z6k(RtwV1)resi}bgNX4J{MEFAnZ3jsv=}v7`QB^^$J#`SczrUr>m!%_XSiTm!PFS=f3@sPB6{ zoRb`BUdfEs1RoJMd&B#`O~n#^=a2ZXu&t#~SiM3jRmf8ev0=|w&RwIhcS$X~eBfIU z2uM1H0hE=Z*%WH9{g$hU5}S?ri0V5qJvj-eg%h<#dBjYX%!WKxD)cuiVc=nCKX$1f z{=!HMi(r#9_lU0!Sx-H$X5S~fMhZRxfSvMWs`7F}n)UsZ?9%bJ(9N=AH?0nY;k zOKgwpx}SPaN5~SLKPF6%+v^y;jua5==eIoG`nCSYufw)@dt>BYkdI5c+bW-F4Kd{t zIwXqTP)fLDkU2LX(*L;=yEmftq9QjMt;|m>m{KT3CLv#o+^>pN?9#_)b~C7UL68}= z)?+84S4ojrc53}eo!g|EGnr?7fx+$Yu9+y4QQX-}PDjJd3R&Q&tZ7a@Q$@p*Z$bQz zna_Csf_~=LQTTqPf7sauR1^k3C+l25%O)axp1ilX-;?Mp$VDxFO^br!Kv-T}34$bz ze5~p*idr@tSwr|VIH@&Gqy?jBnW3P8TsZaBr79O*AQeObV+8cAMe0lpznJKbZp@lG zz(c_9&HIB+K1)2laD*N_ zfM0u-Iy|{wMz&r7wq|e8?Kn9=#pISJt&~NCaK_QhOillVqJ*CPhk$M947o}0aSwTo zIV#C3xQKfQCmCyQ({sEil%bK5{pul)7eS$TYO}Q2{cIMkx@CmL7$QBFkJRC^)(5); z6T7odv%9#?PI1+;168Hh>!tn_#qB`5(95N!J||S%QdJ3+3K6Y}ypp$(*{y)P>~1WI zo{-rhZ*L5U9tQ4`-U}O>3J}>B^1U`#H7)^2+pXTmMIM@msL`l*&DKmnKi>05Fk!DL z^sb&m;%)t4zB;IPQx#8S67yINZXn`?I$*yUyWP%ysjF6-l}<3^?*?(n(?L$B;d^yl z?!*W26ndDLR$SOV*^zsGO~RI-Cn*+T!q5X3L+W-3bmmAlI*MnNy9S?cfmRD7|EQmx zp`1IU+?Y_h6CrI?wX0|w(A8yhdR}la7lX0&58AQ(q)1K@w<=$tKpk=$Lu|&3%@za` zf%p|4fzoXA@bJg}0JC836ToQeH3P75I9HpqQ&RxkD_ok?|4{gC$dc5p)RI6J)B?(r z!KFpSMHTu`&k{eWWHlBc3X)8N!w6-Nf@{~SXJAe~_p+eVijI_KsL@J9g50W#B4FX- z^cr=w6OgJ25A3Ym+lxHURn9YM^j=)|ahc6B=cv+H&(hX5W7B^v#h-eKB#fPC*rvx~B}st8#KYs=dsp0{ zs+{hC_dX#UcR5i}#I5&kwBTXW*MB{JeDHd4M87!jY0~_~ievIWW{{i%1ww@Uc4$rq zXZta>SnwmUM8JgRTI<6mGSM&f?}Kp!X=a8dPdol^JD1(Ce*>R4`(Y3L_p9U_{l2%% zX6;S+|&e zY05E|dV1glF=|&s(8YoLL>-eXgz?%$0yv5WbXWa3d!}DwQdl2-%v1q^T-9mWO@_ z7J^{4gJ7oaAfsF9?=?o2x+-hQ7#NaN^YE-eV8@EnloK98VRVO*x9R*v0lru~Y_Yho zO!tdRFb_cGz|E#v%n;txbtc2A+N&TMGhV}}E3z1&NM$xyc+AquJ!`rCdfk}isSTfP zqNDASx3hL2LWhS~jkCuZC1XIBRQ?AfMGc~%#m)IWg+MrdhA#~N0mPY7(h&bLQseuz z=W>M{;Lv_gi2fZ^oi(Ae=y{nh`TJ8r;@;6xxd^8YU^sfd(ZjmEp5_h`kM#AwK&E$@ z^UV#GyL`|3Uj1$Jrh7EJmlCXSpEmvbC~3(EDt&6oq`%Hhu~j00(hNYqXu|Dpzh3(r=H6Q(|H_NfLbi&K3jJO2+uc%zX zZ)O@~VP{Ig1T`Y4)=~h89NK#wZH69H`kRG(LTrNu+5r>5`yr$G;+PMHPyxCcpF?hI zOz;)-z1G=KC&t$6%!9;ptwtcs{?M);0LX>H{@>Vty2%NtC>Qm&xdoU2DtMll2l=sb_w5fRD}5mL7E6UeL#c!%}s5p;4- zD>OU!MDm}#{=^OlaYU65d`d(GB~1@-`u!y7M#0?49k6xkMGJ=-HORridZF^j@8!#w z(Tl1boidieVE`cDGvN1`7TEHc0lX4X3`$k$SFAD+7u6k*R8k!Guqc`tp=fJl4WZ3>Zc zjv0?DUXhcx^}AU7lG@m?DS7SLfY`tZwjeOC*`Zyh<76b&ttz2m;g>6`>+EWe-?cIJ zibz&20Wu(+0u@EA%B68AHqH>O2}64!rsU@M?<1E^v-j*H>1y5%j&O64xGdv0ZD(w= z|88qV`ukKAJpHW2u6=H^1*ibf-iH5$ox6}dZ4BrfPBt#7O|^L?47Q>1_IMJy$~S~a z;+5qH85AL405Mc*nG1XG6cG14jb~zu{~5*y^2M-+u-gz>pOj3$GoAjsUl1M)rW8t` zALUbQuN*~(H+xR2ZA80EbK7WTOAsFM&wz89)-M07a{Uk?3QJX#BEC>n-S-oOQA~9O z%%~)!7B%D(sK~#=-xFgDh$`RNRt`w)k zHN)^Y{9MAPV#w_5d!VH|r}D}q{__5vk{c!4f!K`uMyKM+itO|4>8X^>%8C!s(!p zo)F@vQ;7-RLRhZo;s$D;oeh9}5=(P%j71*->OP>hL$j}sJ-V&C{(C4-De-Ma&zG)e~ z%mkPgUdj%URa++z5d0}58&g`ZF(7m$^_dx$(%aL^Ko+ggkk+n#aHtv0wp|dRd_pf( z1PL+DNk-QbqcP8VfsUtq{4SOQcXm4kQwV8LD|$m$&%Us*^*&5aPD%j1{HWN`DYJl( zkk@}9{?X_GhB<9}3~`5Jsni_n72L%#gZmbc2ufRos8ltTw z7VU6LK`g58+26`gs%+pub=OVk@w%iswWpG{GGuJ;Fq!Qu@GwCJ&b=sCfdu4mB?lpP z8tA!VG{eH}yMhn#3BLxjZ@xURQzU(#=^diG;p`XyavS!IC7s`bPFDL|MW9WlsSN>J zVFxQmecxXE<>FB!t%q9z;B2I`yMsR%Z#6p~{T-6-TIet>=%yeDxaw3Rr1{1P{N-gL z6Z!oG2xI*(musn_Pmi#$i?c)(0XzR$37@y9YW2dwOYe=?s ztuddiqyFMv0p4CddwE`c;5o6tO1?@H%d_fHhK@yAZQr0>hfYOw0ldZDQT6D{#yN34 zxynYUxR0{{ib&N>Ri!naTeV3<-C!RcA%E|YvCPSIK6Vvq6W?IRD1$SGy-aW8sMeVB z=qDlfzs$-vFvyZ?CmMx1>5z}aX+=8!!^%m>rmi~(#=_B@bbKQ4xt`3Gw zVJr~ImYcjbk*nX)dn)T_>|=6sA3BJcBCed6ZIm)~P(YAGz0HDLHb*hTUWWw$wq<1b0y}7yA9$k$vTo#WRNO|#Eh8$8i@i;c0R8+^Q7?)R7 z?xOEFwA_!t1P2p({I3s;MZP%UT4Jaf+`pw@Ir}jh$H=*y`SbyLWG`&5PW3V}8+O>T zIi!m58+>E1SXiEL~pTh2syZdff&-2+5CHPmD*1FMImAA2<=)d6?|1Hm>p#G%< zHM|1LW8V(e=Pi{?gpXax(uCH-SbQv z=>Hqv%`>gYo=fX8VMye4JeV~X_TBkQI%Y@#=XAg84Z9TfILgapN>hwRLlZjorX}N6 zt;0;1NtF~SOhmSH-SiWGC8A|PO6NMOKQVGYNFSpLx~mi*BK~5er9vNwOQ5ajy21Z- zwn*H6Nd{ZUf(TyfR(0sL?b4W|x2tVK{N=Jc71*4&@f@v%Yb^--?mx~c5E*xT2$#{E z2&#_5`R=V+lSjbH*KgYh4)F~Qt&fY&vQ;F|EK3W=B_&{$;wzYH{OBL{G;e>u9{5r% z@wNg)_9b5b%+B96dMV2g)Q+!VlBlxLrAFN3s8X|?*?zY<#>uqy6#1s<5un0Ccuom5 za*)Mc$|;D`W%60~Tk8QQ2F*;LroFxWS9QjtQ?aJhpvS*6?d)e}?N1)r8|drPb@vSY zZ_&m~ihJ@!x;t zu?%)(ALa{g%NN|Mtyx?KudxVqYQ#zK(`EeYgnO-=p5@ z^HpeCyBPkJhY$N`YQ8SKMr7ZX@ZlOb$pD<2t>TS2dps#MWj0-QGFgS#UXT(qh77!- zT$L453UcvKfzv0e`cs9dLNVXglPhxTcfRFT#{)a?QV>>KlYw2q1Jpa+2gvV4+Qtx3 zfu!&4&@RyqL!!d#(!!AZ>M;$f4gkdhl#8>hJZ)n$=1RQx$8l%R3uWMv8R$M^Q_r-V zKmOT+8kJet)PXOU@^+mjt$j|i{wm_IT5}MNa~pxQ5ThE^rS3qMe(*VYgmRE!X0JR` zeF7%W3^0lXWZKRy2~^k{@OLg|K4dMKR~{{}ImOlKMkDNy2V30daqI0RtmM8c{Wq<* z?>B;o@vqzF$-glLhdk6je;8J)A6*}e4yn{Fkv5lk`v4CTge50jv!`0$EX-?&c^sFf z24BnAs~3m7K=40yDAx)mczwZ88T&p%DUyt3K!Sd*cX<6Pvq|Ei z{)~9$eI%|0bTXyqdGvGNXZ1PDa25Ne+njmt>KP7G(@kL!4e3qcFG90e;gk^_W>Lt$ zCPh0?O$?DD5~O8?qisb89eo&(K#on8>^*1?{Bme_uE2+}0SP$^ zySXRlEn3gI_S4C_NsXAA#_nS(QSA6XeZWm zIPAtFPgk;@p%e)g3Q1(!*95Y3+iho$(;XawS}hK&m<|^HES$-FLt-+Jk8siA;wR8@ zi>=D)Y%jhYqht(2u@W0avUn58PgL2MfApgCRLIO@=e^(c3>h2A*Wkopd znJ%UR&r0Vn*{5dhsfunsn!mlc|0VS33t59Q=%GgwaCcW$W{38#!S0hXAg%GgYZw4^ zB=2YdNYm%!G<)OavIRcwIJzD7!*$7Zv;~FsY*`4JZT*EJhn}DC^|FJ%gt4+V^OmN4Kxnv+lTJn7H zMc7**g(Bn}P#y^0TKKos^Vx1}0{C^2%;9Z{bgPo)&+KaE!TZD{Bo*fWN* zxQ+F03M0@EIaX*z((!C73Ub#tHb!xti5@Pqs>g}u$s*u1SbgNtl5VhyUfK%2`1^E& zO&N3!Km|KeDaAqoKSM)_!iQv|+2IS7FHPp%0k)SZVx12aN))JFd+wA)T%J>#kS4K* zMDs_pCVWRLm$PGte%R~dn8Xm5A#OIwJy9IuFx(g;UQFwaH@wHe#DRc$*k1ft@w#yT zU8jACKNKzvsV)Mo-0`0{ohQl^EP%)F|BySjh13Z}cl{2zoh?{2 z3)OMOD;7W)HBex4aU*nh2%|XrRk)A0HZ9Vk6j} zoqMeAOJ;}y_v^Dey1Ne1lS|MfkS;5$!KAC<_CJjyFYm17t9EB+%N>px1&iW(S{t^_VPp09S(D=; z=|5>UJ86c+d}T=i49A7cy2@NcdO7OVd(khieO&4Mqa-(H0%lvS_Ok$nFylwIkTb;9 zf0Z!-x&&QSQi;A1oo!*CZedU=Y*R0c{Bz!*8c6XEb6r7JdUZX0ESQehx$xsGIGFb=olV(~Y{UM5 zz0tozk=&~-sfrjXwRD*vdfey?!#*WaxT(1zzNsyr8}Vqm<=$gz3!JvuT?FM1YvaS7 zY^V0U=KoH=ytwz@FUx3bwze8nk|EIKa;IQc~421OVqp(wwoV zkijS&_bUU*J3A_53Xq#u3;#YLw~?#W`H!mYz>Jy>S_@eE9AmGbUR}Yh-lvq3ii{2R zv;w9mV-oN@GpC-4Bi_|Cyr%bd(%sVHrFukr{7tilTdc*lz}Rh7n5_(f+Nv_+ph+*D zpyU<#y6zjfRqg%V^fIbjr>oaofsuI{?Icq+cea1vvz?fq1BG%IF%q^k9_1%+3)yGB zDtlIKlbnwzBmv^MT!s7W_!S6}B(hu?=>|;ULqC}kMrq7tqepU=F%lIi#+DjQ@uNQ& zw{zZfdLMOdxQ#R5WmSHi{4`OCm}mEcB+s-nI2LTb7%#kRn=gc$KpcY_|DJ;I!<;8V zX#ld8QSFpe_lBJvo+fiWTcWawh*PsK<@_A2VTB&(>-QP%dc~yp!f8}3T9!&zyKKfX z#j$6ANPBtVzaDbchvIj+T*O?Sh4!9}r@%}ce0ovtZr1HVJ4b-a3c(DxSC6bP($!-s z=rYRzES^kQ`0q|ulfxco{QLfb%{$C?^YW&I?M;mfL}%NeIXnyy`uVfFJlmJXH2_8$ zcD>)stQ;<=@|r^lOm}^I&vRYz__n)xA~VmgcZ-)PkhO+MZX)Z9#WpGt34!^*Qzi+K zr_1IdW8nlH9FB9;_z=7Aw4N#gjj(0DpJt%H>r>~ayGTcpY}sq<@m2&hQVbSw9PHyH z>qc#)gTm?a#|_z5PIvynIz#8+)!b_lLf&0`b5i@4Ob-ilorL zf5z5|W>UlcS|9bGw)z|YKcFA<<)bP3!6K>TAZj%RQ8LY$j_pFZgnZc!xyU**KVte< zR9DO&Bk)2M*?92S8~;`hoQd7o8>{$B3QR%SBI)Y(cD5JKC-c?%dO2eDjPTl#!{O(J zas-l=eieJ6?yn2KGGSqFnTbn{9dz_E`Xun-ILHrjHFe<)rbA-=W694{<&GCkuwCU^5@eb}EH>HLGV^YGAFyI2x z^0^zu6Qm}s9L9&I`EFHoH!m2kGv=DzbMu@IrV?zxF_|H#wSw8CM)!&W=U%>19F^+D zKEFTLTd#{-0K^(dUi{J!16r~Y|4wGz0D8>Slo3qUg;{=rj>J)|im)yG_kkH=799?Z z0(>8L9z!bAUKTNkAP*%^y7Z>{iW)}ABun(J2P#Yvj7@4=dF7o_d!^?l1p3+Qk#Ny& zQeBX)??M0h2JG@_@iwO$lJ7W1Yo7%9&Z_O=P`O)Cxp=Ie81?XSkasJ7XJgwI z{UQsFTeO}oEuk~29b)m|{ATeH54&a6^e3HY(f`HGPft&DK5%IxaEi`VZ9jSx)2$F^ z{{ge*kTx668T5F>yMrIXi<0!=lTm+wE zc|I^KQ2~IWTx7L`u3|s;}`&dMF4zA^(9h9?}jN4 z4%LC@1QF2=%3#94^;%Y>Eh%Q;uNeF}U^M?f=KecemgBe&hAVW>?CuFS2jC{eiwq(V zB)}+AOb~-ai4sLivScezvLC

l_|ivTRAV{5i-$uWZ?t{n_#>%akn13Y5eQ1~7mm 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≪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

_` (uppercase, underscores) +```bash +OLLA_SERVER_PORT=8080 +OLLA_PROXY_ENGINE=olla +OLLA_LOG_LEVEL=debug +OLLA_CONFIG_FILE=/path/to/config.yaml +``` + +## Claude Code Integration + +Connect Claude Code to local models through Olla's Anthropic API translation: + +```bash +export ANTHROPIC_BASE_URL="http://localhost:40114/olla/anthropic" +export ANTHROPIC_API_KEY="not-really-needed" +export ANTHROPIC_MODEL="openai/gpt-oss-120b" # or your model name +export ANTHROPIC_SMALL_FAST_MODEL="${ANTHROPIC_MODEL}" +export ANTHROPIC_DEFAULT_HAIKU_MODEL="${ANTHROPIC_MODEL}" +export ANTHROPIC_DEFAULT_SONNET_MODEL="${ANTHROPIC_MODEL}" +export ANTHROPIC_DEFAULT_OPUS_MODEL="${ANTHROPIC_MODEL}" +export CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1 +export API_TIMEOUT_MS=3000000 +``` + +**Recommended models for Claude Code**: qwen2.5-coder:32b, deepseek-coder-v2, codellama:34b, llama3.3, qwen3:32b + +### Docker Compose Quick Setup +```yaml +# compose.yaml +services: + ollama: + image: ollama/ollama + container_name: ollama + ports: ["11434:11434"] + volumes: ["ollama_data:/root/.ollama"] + + olla: + image: ghcr.io/thushan/olla:latest + container_name: olla + ports: ["40114:40114"] + volumes: ["./olla.yaml:/app/config/config.local.yaml"] + depends_on: [ollama] + +volumes: + ollama_data: +``` + +For Docker: set `server.host: "0.0.0.0"` to bind all interfaces. + +## API Endpoints + +### System +| Endpoint | Method | Description | +|----------|--------|-------------| +| `/internal/health` | GET | Health verification | +| `/internal/status` | GET | System metrics | +| `/internal/status/endpoints` | GET | Backend availability | +| `/internal/status/models` | GET | Model registry | +| `/internal/stats/models` | GET | Usage by model | +| `/internal/stats/translators` | GET | Translator performance | +| `/internal/process` | GET | Runtime info | + +### Unified Models +| Endpoint | Method | Description | +|----------|--------|-------------| +| `/olla/models` | GET | All models across all providers | + +### Provider-Specific Routes +| Provider | Route Prefix | +|----------|-------------| +| Ollama | `/olla/ollama/*` | +| LM Studio | `/olla/lmstudio/*` | +| OpenAI | `/olla/openai/*` | +| vLLM | `/olla/vllm/*` | +| SGLang | `/olla/sglang/*` | +| LiteLLM | `/olla/litellm/*` | +| llama.cpp | `/olla/llamacpp/*` | +| Lemonade | `/olla/lemonade/*` | + +### Anthropic Translation +| Endpoint | Method | Description | +|----------|--------|-------------| +| `/olla/anthropic/v1/messages` | POST | Chat (streaming supported) | +| `/olla/anthropic/v1/models` | GET | List models | +| `/olla/anthropic/v1/messages/count_tokens` | POST | Token estimation | + +### Response Headers +- `X-Olla-Request-ID` — Unique request ID +- `X-Olla-Endpoint` — Selected backend +- `X-Olla-Model` — Model identifier +- `X-Olla-Backend-Type` — Provider type +- `X-Olla-Response-Time` — Processing duration +- `X-Olla-Routing-Strategy` — Active strategy +- `X-Olla-Routing-Decision` — routed/fallback/rejected +- `X-Olla-Mode` — "passthrough" when native Anthropic format + +## Troubleshooting + +```bash +# Health check +curl http://localhost:40114/internal/health + +# List available models +curl http://localhost:40114/olla/anthropic/v1/models | jq + +# Check backend health +curl http://localhost:40114/internal/status/endpoints | jq + +# Test streaming +curl -N -X POST http://localhost:40114/olla/anthropic/v1/messages \ + -H "Content-Type: application/json" \ + -d '{"model":"llama4:latest","max_tokens":50,"messages":[{"role":"user","content":"Count to 5"}],"stream":true}' + +# Translator stats +curl http://localhost:40114/internal/stats/translators | jq + +# Debug logging +OLLA_LOG_LEVEL=debug olla --config config.yaml +``` + +### Common Issues +- **Port conflict**: Set `OLLA_SERVER_PORT` or change config +- **Streaming broken**: Ensure `server.write_timeout: 0s` +- **Docker can't connect**: Set `server.host: "0.0.0.0"` +- **Model not found**: Check routing strategy (try `optimistic`), verify with `/olla/models` +- **Slow responses**: Switch to `olla` engine, increase `stream_buffer_size` + +## Supported Backends + +| Backend | Type Key | Notes | +|---------|----------|-------| +| Ollama | `ollama` | Most common local setup | +| LM Studio | `lm-studio` | GUI-based, model unification | +| vLLM | `vllm` | High-perf, production grade | +| vLLM-MLX | `vllm-mlx` | Apple Silicon via MLX | +| SGLang | `sglang` | RadixAttention, vision | +| llama.cpp | `llamacpp` | GGUF, CPU-first, edge | +| Lemonade SDK | `lemonade` | AMD Ryzen AI | +| LiteLLM | `litellm` | 100+ cloud providers | +| Docker Model Runner | `docker-model-runner` | OCI model distribution | +| OpenAI-compatible | `openai` | Generic fallback | + +## Development + +```bash +git clone https://github.com/thushan/olla.git && cd olla +make deps # Install dependencies +make dev # Build with hot-reload +make test # Run tests +make ready # Pre-commit checks (fmt + lint + test) +make bench # Benchmarks +``` + +Architecture: Hexagonal (ports & adapters) — `internal/core/` (domain), `internal/adapter/` (infra), `internal/app/` (HTTP handlers). diff --git a/personas/_shared/community-skills/olla/references/olla-docs-links.md b/personas/_shared/community-skills/olla/references/olla-docs-links.md new file mode 100644 index 0000000..cfbeac6 --- /dev/null +++ b/personas/_shared/community-skills/olla/references/olla-docs-links.md @@ -0,0 +1,87 @@ +# Olla Documentation Reference Links + +## Official Documentation +- Home: https://thushan.github.io/olla/ +- Demo: https://thushan.github.io/olla/demo/ +- FAQ: https://thushan.github.io/olla/faq/ +- Usage: https://thushan.github.io/olla/usage/ +- About: https://thushan.github.io/olla/about/ + +## Getting Started +- Installation: https://thushan.github.io/olla/getting-started/installation/ +- Quickstart: https://thushan.github.io/olla/getting-started/quickstart/ + +## Concepts +- Overview: https://thushan.github.io/olla/concepts/overview/ +- Load Balancing: https://thushan.github.io/olla/concepts/load-balancing/ +- Model Routing: https://thushan.github.io/olla/concepts/model-routing/ +- Model Aliases: https://thushan.github.io/olla/concepts/model-aliases/ +- Model Unification: https://thushan.github.io/olla/concepts/model-unification/ +- Health Checking: https://thushan.github.io/olla/concepts/health-checking/ +- API Translation: https://thushan.github.io/olla/concepts/api-translation/ +- Proxy Engines: https://thushan.github.io/olla/concepts/proxy-engines/ +- Proxy Profiles: https://thushan.github.io/olla/concepts/proxy-profiles/ +- Profile System: https://thushan.github.io/olla/concepts/profile-system/ +- Provider Metrics: https://thushan.github.io/olla/concepts/provider-metrics/ + +## Configuration +- Overview: https://thushan.github.io/olla/configuration/overview/ +- Filters: https://thushan.github.io/olla/configuration/filters/ +- Reference: https://thushan.github.io/olla/configuration/reference/ +- Examples: https://thushan.github.io/olla/configuration/examples/ +- Best Practices - Configuration: https://thushan.github.io/olla/configuration/practices/configuration/ +- Best Practices - Security: https://thushan.github.io/olla/configuration/practices/security/ +- Best Practices - Performance: https://thushan.github.io/olla/configuration/practices/performance/ +- Best Practices - Monitoring: https://thushan.github.io/olla/configuration/practices/monitoring/ + +## Integrations +- Overview: https://thushan.github.io/olla/integrations/overview/ + +### Backend +- Ollama: https://thushan.github.io/olla/integrations/backend/ollama/ +- LM Studio: https://thushan.github.io/olla/integrations/backend/lmstudio/ +- vLLM: https://thushan.github.io/olla/integrations/backend/vllm/ +- vLLM-MLX: https://thushan.github.io/olla/integrations/backend/vllm-mlx/ +- SGLang: https://thushan.github.io/olla/integrations/backend/sglang/ +- Lemonade SDK: https://thushan.github.io/olla/integrations/backend/lemonade/ +- LiteLLM: https://thushan.github.io/olla/integrations/backend/litellm/ +- llama.cpp: https://thushan.github.io/olla/integrations/backend/llamacpp/ +- Docker Model Runner: https://thushan.github.io/olla/integrations/backend/docker-model-runner/ + +### Frontend +- OpenWebUI: https://thushan.github.io/olla/integrations/frontend/openwebui/ +- OpenWebUI (OpenAI): https://thushan.github.io/olla/integrations/frontend/openwebui-openai/ +- Claude Code: https://thushan.github.io/olla/integrations/frontend/claude-code/ +- OpenCode: https://thushan.github.io/olla/integrations/frontend/opencode/ +- Crush CLI: https://thushan.github.io/olla/integrations/frontend/crush-cli/ + +### API Translation +- Anthropic: https://thushan.github.io/olla/integrations/api-translation/anthropic/ + +## API Reference +- Overview: https://thushan.github.io/olla/api-reference/overview/ +- System Endpoints: https://thushan.github.io/olla/api-reference/system/ +- Models API: https://thushan.github.io/olla/api-reference/models/ + +## Compare +- Overview: https://thushan.github.io/olla/compare/overview/ +- Integration Patterns: https://thushan.github.io/olla/compare/integration-patterns/ +- vs GPUStack: https://thushan.github.io/olla/compare/gpustack/ +- vs LiteLLM: https://thushan.github.io/olla/compare/litellm/ +- vs LocalAI: https://thushan.github.io/olla/compare/localai/ + +## Development +- Overview: https://thushan.github.io/olla/development/overview/ +- Setup: https://thushan.github.io/olla/development/setup/ +- Architecture: https://thushan.github.io/olla/development/architecture/ +- Patterns: https://thushan.github.io/olla/development/patterns/ +- Circuit Breaker: https://thushan.github.io/olla/development/circuit-breaker/ +- Contributing: https://thushan.github.io/olla/development/contributing/ +- Testing: https://thushan.github.io/olla/development/testing/ +- Benchmarking: https://thushan.github.io/olla/development/benchmarking/ +- Anthropic Inspector: https://thushan.github.io/olla/notes/anthropic-inspector/ + +## GitHub +- Repository: https://github.com/thushan/olla +- Releases: https://github.com/thushan/olla/releases +- Issues: https://github.com/thushan/olla/issues diff --git a/personas/_shared/skills/acquiring-disk-image-with-dd-and-dcfldd/LICENSE b/personas/_shared/skills/acquiring-disk-image-with-dd-and-dcfldd/LICENSE new file mode 100644 index 0000000..d885118 --- /dev/null +++ b/personas/_shared/skills/acquiring-disk-image-with-dd-and-dcfldd/LICENSE @@ -0,0 +1,201 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to the Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by the Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding any notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. Please do not remove or change + the license header comment from a contributed file except when + necessary. + + Copyright 2026 mukul975 + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/personas/_shared/skills/acquiring-disk-image-with-dd-and-dcfldd/SKILL.md b/personas/_shared/skills/acquiring-disk-image-with-dd-and-dcfldd/SKILL.md new file mode 100644 index 0000000..d3d1730 --- /dev/null +++ b/personas/_shared/skills/acquiring-disk-image-with-dd-and-dcfldd/SKILL.md @@ -0,0 +1,242 @@ +--- +name: acquiring-disk-image-with-dd-and-dcfldd +description: Create forensically sound bit-for-bit disk images using dd and dcfldd while preserving evidence integrity through + hash verification. +domain: cybersecurity +subdomain: digital-forensics +tags: +- forensics +- disk-imaging +- evidence-acquisition +- dd +- dcfldd +- hash-verification +version: '1.0' +author: mahipal +license: Apache-2.0 +nist_csf: +- RS.AN-01 +- RS.AN-03 +- DE.AE-02 +- RS.MA-01 +--- + +# Acquiring Disk Image with dd and dcfldd + +## When to Use +- When you need to create a forensic copy of a suspect drive for investigation +- During incident response when preserving volatile disk evidence before analysis +- When law enforcement or legal proceedings require a verified bit-for-bit copy +- Before performing any destructive analysis on a storage device +- When acquiring images from physical drives, USB devices, or memory cards + +## Prerequisites +- Linux-based forensic workstation (SIFT, Kali, or any Linux distro) +- `dd` (pre-installed on all Linux systems) or `dcfldd` (enhanced forensic version) +- Write-blocker hardware or software write-blocking configured +- Destination drive with sufficient storage (larger than source) +- Root/sudo privileges on the forensic workstation +- SHA-256 or MD5 hashing utilities (`sha256sum`, `md5sum`) + +## Workflow + +### Step 1: Identify the Target Device and Enable Write Protection + +```bash +# List all connected block devices to identify the target +lsblk -o NAME,SIZE,TYPE,MOUNTPOINT,MODEL + +# Verify the device details +fdisk -l /dev/sdb + +# Enable software write-blocking (if no hardware blocker) +blockdev --setro /dev/sdb + +# Verify read-only status +blockdev --getro /dev/sdb +# Output: 1 (means read-only is enabled) + +# Alternatively, use udev rules for persistent write-blocking +echo 'SUBSYSTEM=="block", ATTRS{serial}=="WD-WCAV5H861234", ATTR{ro}="1"' > /etc/udev/rules.d/99-writeblock.rules +udevadm control --reload-rules +``` + +### Step 2: Prepare the Destination and Document the Source + +```bash +# Create case directory structure +mkdir -p /cases/case-2024-001/{images,hashes,logs,notes} + +# Document source drive information +hdparm -I /dev/sdb > /cases/case-2024-001/notes/source_drive_info.txt + +# Record the serial number and model +smartctl -i /dev/sdb >> /cases/case-2024-001/notes/source_drive_info.txt + +# Pre-hash the source device +sha256sum /dev/sdb | tee /cases/case-2024-001/hashes/source_hash_before.txt +``` + +### Step 3: Acquire the Image Using dd + +```bash +# Basic dd acquisition with progress and error handling +dd if=/dev/sdb of=/cases/case-2024-001/images/evidence.dd \ + bs=4096 \ + conv=noerror,sync \ + status=progress 2>&1 | tee /cases/case-2024-001/logs/dd_acquisition.log + +# For compressed images to save space +dd if=/dev/sdb bs=4096 conv=noerror,sync status=progress | \ + gzip -c > /cases/case-2024-001/images/evidence.dd.gz + +# Using dd with a specific count for partial acquisition +dd if=/dev/sdb of=/cases/case-2024-001/images/first_1gb.dd \ + bs=1M count=1024 status=progress +``` + +### Step 4: Acquire Using dcfldd (Preferred Forensic Method) + +```bash +# Install dcfldd if not present +apt-get install dcfldd + +# Acquire image with built-in hashing and split output +dcfldd if=/dev/sdb \ + of=/cases/case-2024-001/images/evidence.dd \ + hash=sha256,md5 \ + hashwindow=1G \ + hashlog=/cases/case-2024-001/hashes/acquisition_hashes.txt \ + bs=4096 \ + conv=noerror,sync \ + errlog=/cases/case-2024-001/logs/dcfldd_errors.log + +# Split large images into manageable segments +dcfldd if=/dev/sdb \ + of=/cases/case-2024-001/images/evidence.dd \ + hash=sha256 \ + hashlog=/cases/case-2024-001/hashes/split_hashes.txt \ + bs=4096 \ + split=2G \ + splitformat=aa + +# Acquire with verification pass +dcfldd if=/dev/sdb \ + of=/cases/case-2024-001/images/evidence.dd \ + hash=sha256 \ + hashlog=/cases/case-2024-001/hashes/verification.txt \ + vf=/cases/case-2024-001/images/evidence.dd \ + verifylog=/cases/case-2024-001/logs/verify.log +``` + +### Step 5: Verify Image Integrity + +```bash +# Hash the acquired image +sha256sum /cases/case-2024-001/images/evidence.dd | \ + tee /cases/case-2024-001/hashes/image_hash.txt + +# Compare source and image hashes +diff <(sha256sum /dev/sdb | awk '{print $1}') \ + <(sha256sum /cases/case-2024-001/images/evidence.dd | awk '{print $1}') + +# If using split images, verify each segment +sha256sum /cases/case-2024-001/images/evidence.dd.* | \ + tee /cases/case-2024-001/hashes/split_image_hashes.txt + +# Re-hash source to confirm no changes occurred +sha256sum /dev/sdb | tee /cases/case-2024-001/hashes/source_hash_after.txt +diff /cases/case-2024-001/hashes/source_hash_before.txt \ + /cases/case-2024-001/hashes/source_hash_after.txt +``` + +### Step 6: Document the Acquisition Process + +```bash +# Generate acquisition report +cat << 'EOF' > /cases/case-2024-001/notes/acquisition_report.txt +DISK IMAGE ACQUISITION REPORT +============================== +Case Number: 2024-001 +Date/Time: $(date -u +"%Y-%m-%d %H:%M:%S UTC") +Examiner: [Name] + +Source Device: /dev/sdb +Model: [from hdparm output] +Serial: [from hdparm output] +Size: [from fdisk output] + +Acquisition Tool: dcfldd v1.9.1 +Block Size: 4096 +Write Blocker: [Hardware/Software model] + +Image File: evidence.dd +Image Hash (SHA-256): [from hash file] +Source Hash (SHA-256): [from hash file] +Hash Match: YES/NO + +Errors During Acquisition: [from error log] +EOF + +# Compress logs for archival +tar -czf /cases/case-2024-001/acquisition_package.tar.gz \ + /cases/case-2024-001/hashes/ \ + /cases/case-2024-001/logs/ \ + /cases/case-2024-001/notes/ +``` + +## Key Concepts + +| Concept | Description | +|---------|-------------| +| Bit-for-bit copy | Exact replica of source including unallocated space and slack space | +| Write blocker | Hardware or software mechanism preventing writes to evidence media | +| Hash verification | Cryptographic hash comparing source and image to prove integrity | +| Block size (bs) | Transfer chunk size affecting speed; 4096 or 64K typical for forensics | +| conv=noerror,sync | Continue on read errors and pad with zeros to maintain offset alignment | +| Chain of custody | Documented trail proving evidence has not been tampered with | +| Split imaging | Breaking large images into smaller files for storage and transport | +| Raw/dd format | Bit-for-bit image format without metadata container overhead | + +## Tools & Systems + +| Tool | Purpose | +|------|---------| +| dd | Standard Unix disk duplication utility for raw imaging | +| dcfldd | DoD Computer Forensics Laboratory enhanced version of dd with hashing | +| dc3dd | Another forensic dd variant from the DoD Cyber Crime Center | +| sha256sum | SHA-256 hash calculation for integrity verification | +| blockdev | Linux command to set block device read-only mode | +| hdparm | Drive identification and parameter reporting | +| smartctl | S.M.A.R.T. data retrieval for drive health and identification | +| lsblk | Block device enumeration and identification | + +## Common Scenarios + +**Scenario 1: Acquiring a Suspect Laptop Hard Drive** +Connect the drive via a Tableau T35u hardware write-blocker, identify as `/dev/sdb`, use dcfldd with SHA-256 hashing, split into 4GB segments for DVD archival, verify hashes match, document in case notes. + +**Scenario 2: Imaging a USB Flash Drive from a Compromised Workstation** +Use software write-blocking with `blockdev --setro`, acquire with dcfldd including MD5 and SHA-256 dual hashing, image is small enough for single file, verify and store on encrypted case drive. + +**Scenario 3: Remote Acquisition Over Network** +Use dd piped through netcat or ssh for remote acquisition: `ssh root@remote "dd if=/dev/sda bs=4096" | dd of=remote_image.dd bs=4096`, hash both ends independently to verify transfer integrity. + +**Scenario 4: Acquiring from a Failing Drive** +Use `ddrescue` first to recover readable sectors, then use dd with `conv=noerror,sync` to fill gaps with zeros, document which sectors were unreadable in the error log. + +## Output Format + +``` +Acquisition Summary: + Source: /dev/sdb (500GB Western Digital WD5000AAKX) + Destination: /cases/case-2024-001/images/evidence.dd + Tool: dcfldd 1.9.1 + Block Size: 4096 bytes + Duration: 2h 15m 32s + Bytes Copied: 500,107,862,016 + Errors: 0 bad sectors + Source SHA-256: a3f2b8c9d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1 + Image SHA-256: a3f2b8c9d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1 + Verification: PASSED - Hashes match +``` diff --git a/personas/_shared/skills/acquiring-disk-image-with-dd-and-dcfldd/references/api-reference.md b/personas/_shared/skills/acquiring-disk-image-with-dd-and-dcfldd/references/api-reference.md new file mode 100644 index 0000000..a7a24b9 --- /dev/null +++ b/personas/_shared/skills/acquiring-disk-image-with-dd-and-dcfldd/references/api-reference.md @@ -0,0 +1,99 @@ +# API Reference: dd and dcfldd Disk Imaging + +## dd - Standard Unix Disk Duplication + +### Basic Syntax +```bash +dd if= of= [options] +``` + +### Key Options +| Flag | Description | Example | +|------|-------------|---------| +| `if=` | Input file (source device) | `if=/dev/sdb` | +| `of=` | Output file (destination image) | `of=evidence.dd` | +| `bs=` | Block size for read/write | `bs=4096` (forensic standard) | +| `count=` | Number of blocks to copy | `count=1024` | +| `skip=` | Skip N blocks from input start | `skip=2048` | +| `conv=` | Conversion options | `conv=noerror,sync` | +| `status=` | Transfer statistics level | `status=progress` | + +### conv= Values +- `noerror` - Continue on read errors (do not abort) +- `sync` - Pad input blocks with zeros on error (preserves offset alignment) +- `notrunc` - Do not truncate output file + +### Output Format +``` +500107862016 bytes (500 GB, 466 GiB) copied, 8132.45 s, 61.5 MB/s +976773168+0 records in +976773168+0 records out +``` + +## dcfldd - DoD Forensic dd + +### Basic Syntax +```bash +dcfldd if= of= [options] +``` + +### Extended Options +| Flag | Description | Example | +|------|-------------|---------| +| `hash=` | Hash algorithm(s) | `hash=sha256,md5` | +| `hashlog=` | File for hash output | `hashlog=hashes.txt` | +| `hashwindow=` | Hash every N bytes | `hashwindow=1G` | +| `hashconv=` | Hash before or after conversion | `hashconv=after` | +| `errlog=` | Error log file | `errlog=errors.log` | +| `split=` | Split output into chunks | `split=2G` | +| `splitformat=` | Suffix format for split files | `splitformat=aa` | +| `vf=` | Verification file | `vf=evidence.dd` | +| `verifylog=` | Verification result log | `verifylog=verify.log` | + +### Output Format +``` +Total (sha256): a3f2b8c9d4e5f6a7b8c9d0e1f2a3b4c5... +1024+0 records in +1024+0 records out +``` + +## sha256sum - Hash Verification + +### Syntax +```bash +sha256sum +sha256sum -c +``` + +### Output Format +``` +a3f2b8c9d4e5f6... /dev/sdb +a3f2b8c9d4e5f6... evidence.dd +``` + +## blockdev - Write Protection + +### Syntax +```bash +blockdev --setro # Set read-only +blockdev --setrw # Set read-write +blockdev --getro # Check: 1=RO, 0=RW +blockdev --getsize64 # Size in bytes +``` + +## lsblk - Block Device Enumeration + +### Syntax +```bash +lsblk -o NAME,SIZE,TYPE,MOUNTPOINT,MODEL,SERIAL,RO +lsblk -J # JSON output +lsblk -p # Full device paths +``` + +## hdparm - Drive Identification + +### Syntax +```bash +hdparm -I # Detailed drive info +hdparm -i # Summary identification +``` diff --git a/personas/_shared/skills/acquiring-disk-image-with-dd-and-dcfldd/scripts/agent.py b/personas/_shared/skills/acquiring-disk-image-with-dd-and-dcfldd/scripts/agent.py new file mode 100644 index 0000000..2b3d5ac --- /dev/null +++ b/personas/_shared/skills/acquiring-disk-image-with-dd-and-dcfldd/scripts/agent.py @@ -0,0 +1,181 @@ +#!/usr/bin/env python3 +"""Forensic disk image acquisition agent using dd and dcfldd with hash verification.""" + +import shlex +import subprocess +import hashlib +import os +import datetime +import json + + +def run_cmd(cmd, capture=True): + """Execute a command and return output.""" + if isinstance(cmd, str): + cmd = shlex.split(cmd) + result = subprocess.run(cmd, capture_output=capture, text=True, timeout=120) + return result.stdout.strip(), result.stderr.strip(), result.returncode + + +def list_block_devices(): + """Enumerate connected block devices.""" + stdout, _, rc = run_cmd("lsblk -J -o NAME,SIZE,TYPE,MOUNTPOINT,MODEL,SERIAL,RO") + if rc == 0 and stdout: + return json.loads(stdout) + return {"blockdevices": []} + + +def check_write_protection(device): + """Verify a device is set to read-only mode.""" + stdout, _, rc = run_cmd(f"blockdev --getro {device}") + if rc == 0: + return stdout.strip() == "1" + return False + + +def enable_write_protection(device): + """Enable software write-blocking on the target device.""" + _, _, rc = run_cmd(f"blockdev --setro {device}") + if rc != 0: + print(f"[ERROR] Failed to set {device} read-only. Run as root.") + return False + if check_write_protection(device): + print(f"[OK] Write protection enabled on {device}") + return True + print(f"[ERROR] Write protection verification failed for {device}") + return False + + +def compute_hash(path, algorithm="sha256", block_size=65536): + """Compute the SHA-256 or MD5 hash of a file or device.""" + h = hashlib.new(algorithm) + try: + with open(path, "rb") as f: + while True: + block = f.read(block_size) + if not block: + break + h.update(block) + except PermissionError: + print(f"[ERROR] Permission denied reading {path}. Run as root.") + return None + except FileNotFoundError: + print(f"[ERROR] Path not found: {path}") + return None + return h.hexdigest() + + +def acquire_with_dd(source, destination, block_size=4096, log_file=None): + """Acquire a forensic image using dd with error handling.""" + dd_cmd = [ + "dd", f"if={source}", f"of={destination}", + f"bs={block_size}", "conv=noerror,sync", "status=progress" + ] + print(f"[*] Starting dd acquisition: {source} -> {destination}") + print(f"[*] Block size: {block_size}") + start = datetime.datetime.utcnow() + if log_file: + dd_proc = subprocess.run(dd_cmd, capture_output=True, text=True, timeout=120) + combined = (dd_proc.stdout or "") + (dd_proc.stderr or "") + with open(log_file, "w") as lf: + lf.write(combined) + rc = dd_proc.returncode + else: + result = subprocess.run(dd_cmd, text=True, timeout=120) + rc = result.returncode + elapsed = (datetime.datetime.utcnow() - start).total_seconds() + print(f"[*] Acquisition completed in {elapsed:.1f} seconds (rc={rc})") + return rc == 0 + + +def acquire_with_dcfldd(source, destination, hash_alg="sha256", hash_log=None, + error_log=None, block_size=4096, split_size=None): + """Acquire a forensic image using dcfldd with built-in hashing.""" + cmd = [ + "dcfldd", f"if={source}", f"of={destination}", + f"bs={block_size}", "conv=noerror,sync", + f"hash={hash_alg}", "hashwindow=1G", + ] + if hash_log: + cmd.append(f"hashlog={hash_log}") + if error_log: + cmd.append(f"errlog={error_log}") + if split_size: + cmd.extend([f"split={split_size}", "splitformat=aa"]) + print(f"[*] Starting dcfldd acquisition: {source} -> {destination}") + start = datetime.datetime.utcnow() + result = subprocess.run(cmd, text=True, timeout=120) + rc = result.returncode + elapsed = (datetime.datetime.utcnow() - start).total_seconds() + print(f"[*] dcfldd completed in {elapsed:.1f} seconds (rc={rc})") + return rc == 0 + + +def verify_image(source, image_path, algorithm="sha256"): + """Verify image integrity by comparing hashes of source and acquired image.""" + print(f"[*] Computing {algorithm} hash of source: {source}") + source_hash = compute_hash(source, algorithm) + print(f" Source hash: {source_hash}") + print(f"[*] Computing {algorithm} hash of image: {image_path}") + image_hash = compute_hash(image_path, algorithm) + print(f" Image hash: {image_hash}") + if source_hash and image_hash: + match = source_hash == image_hash + status = "PASSED" if match else "FAILED" + print(f"[{'OK' if match else 'FAIL'}] Verification: {status}") + return match, source_hash, image_hash + return False, source_hash, image_hash + + +def generate_report(case_dir, source_device, image_path, tool_used, + source_hash, image_hash, verified, elapsed_seconds=0): + """Generate a forensic acquisition report.""" + report = { + "report_type": "Disk Image Acquisition", + "timestamp": datetime.datetime.utcnow().isoformat() + "Z", + "case_directory": case_dir, + "source_device": source_device, + "image_file": image_path, + "acquisition_tool": tool_used, + "block_size": 4096, + "source_hash_sha256": source_hash, + "image_hash_sha256": image_hash, + "hash_verified": verified, + "duration_seconds": elapsed_seconds, + } + report_path = os.path.join(case_dir, "acquisition_report.json") + with open(report_path, "w") as f: + json.dump(report, f, indent=2) + print(f"[*] Report saved to {report_path}") + return report + + +if __name__ == "__main__": + print("=" * 60) + print("Forensic Disk Image Acquisition Agent") + print("Tools: dd / dcfldd with SHA-256 verification") + print("=" * 60) + + # Demo: list block devices + print("\n[*] Enumerating block devices...") + devices = list_block_devices() + for dev in devices.get("blockdevices", []): + name = dev.get("name", "?") + size = dev.get("size", "?") + dtype = dev.get("type", "?") + model = dev.get("model", "N/A") + ro = "RO" if dev.get("ro") else "RW" + print(f" /dev/{name} {size} {dtype} {model} [{ro}]") + + # Demo workflow (dry run) + demo_source = "/dev/sdb" + demo_case = "/cases/demo-case/images" + demo_image = os.path.join(demo_case, "evidence.dd") + + print(f"\n[DEMO] Acquisition workflow for {demo_source}:") + print(f" 1. Enable write protection: blockdev --setro {demo_source}") + print(f" 2. Acquire with dcfldd: dcfldd if={demo_source} of={demo_image} " + f"hash=sha256 hashwindow=1G bs=4096 conv=noerror,sync") + print(f" 3. Verify: compare SHA-256 of {demo_source} and {demo_image}") + print(f" 4. Generate acquisition report with chain-of-custody metadata") + print("\n[*] Agent ready. Provide a source device and case directory to begin.") diff --git a/personas/_shared/skills/analyzing-active-directory-acl-abuse/LICENSE b/personas/_shared/skills/analyzing-active-directory-acl-abuse/LICENSE new file mode 100644 index 0000000..d885118 --- /dev/null +++ b/personas/_shared/skills/analyzing-active-directory-acl-abuse/LICENSE @@ -0,0 +1,201 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to the Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by the Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding any notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. Please do not remove or change + the license header comment from a contributed file except when + necessary. + + Copyright 2026 mukul975 + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/personas/_shared/skills/analyzing-active-directory-acl-abuse/SKILL.md b/personas/_shared/skills/analyzing-active-directory-acl-abuse/SKILL.md new file mode 100644 index 0000000..8deab1e --- /dev/null +++ b/personas/_shared/skills/analyzing-active-directory-acl-abuse/SKILL.md @@ -0,0 +1,84 @@ +--- +name: analyzing-active-directory-acl-abuse +description: Detect dangerous ACL misconfigurations in Active Directory using ldap3 to identify GenericAll, WriteDACL, and + WriteOwner abuse paths +domain: cybersecurity +subdomain: identity-security +tags: +- active-directory +- acl-abuse +- ldap +- privilege-escalation +version: '1.0' +author: mahipal +license: Apache-2.0 +nist_csf: +- PR.AA-01 +- PR.AA-05 +- PR.AA-06 +--- + + +# Analyzing Active Directory ACL Abuse + +## Overview + +Active Directory Access Control Lists (ACLs) define permissions on AD objects through Discretionary Access Control Lists (DACLs) containing Access Control Entries (ACEs). Misconfigured ACEs can grant non-privileged users dangerous permissions such as GenericAll (full control), WriteDACL (modify permissions), WriteOwner (take ownership), and GenericWrite (modify attributes) on sensitive objects like Domain Admins groups, domain controllers, or GPOs. + +This skill uses the ldap3 Python library to connect to a Domain Controller, query objects with their nTSecurityDescriptor attribute, parse the binary security descriptor into SDDL (Security Descriptor Definition Language) format, and identify ACEs that grant dangerous permissions to non-administrative principals. These misconfigurations are the basis for ACL-based attack paths discovered by tools like BloodHound. + + +## When to Use + +- When investigating security incidents that require analyzing active directory acl abuse +- When building detection rules or threat hunting queries for this domain +- When SOC analysts need structured procedures for this analysis type +- When validating security monitoring coverage for related attack techniques + +## Prerequisites + +- Python 3.9 or later with ldap3 library (`pip install ldap3`) +- Domain user credentials with read access to AD objects +- Network connectivity to Domain Controller on port 389 (LDAP) or 636 (LDAPS) +- Understanding of Active Directory security model and SDDL format + +## Steps + +1. **Connect to Domain Controller**: Establish an LDAP connection using ldap3 with NTLM or simple authentication. Use LDAPS (port 636) for encrypted connections in production. + +2. **Query target objects**: Search the target OU or entire domain for objects including users, groups, computers, and OUs. Request the `nTSecurityDescriptor`, `distinguishedName`, `objectClass`, and `sAMAccountName` attributes. + +3. **Parse security descriptors**: Convert the binary nTSecurityDescriptor into its SDDL string representation. Parse each ACE in the DACL to extract the trustee SID, access mask, and ACE type (allow/deny). + +4. **Resolve SIDs to principals**: Map security identifiers (SIDs) to human-readable account names using LDAP lookups against the domain. Identify well-known SIDs for built-in groups. + +5. **Check for dangerous permissions**: Compare each ACE's access mask against dangerous permission bitmasks: GenericAll (0x10000000), WriteDACL (0x00040000), WriteOwner (0x00080000), GenericWrite (0x40000000), and WriteProperty for specific extended rights. + +6. **Filter non-admin trustees**: Exclude expected administrative trustees (Domain Admins, Enterprise Admins, SYSTEM, Administrators) and flag ACEs where non-privileged users or groups hold dangerous permissions. + +7. **Map attack paths**: For each finding, document the potential attack chain (e.g., GenericAll on user allows password reset, WriteDACL on group allows adding self to group). + +8. **Generate remediation report**: Output a JSON report with all dangerous ACEs, affected objects, non-admin trustees, and recommended remediation steps. + +## Expected Output + +```json +{ + "domain": "corp.example.com", + "objects_scanned": 1247, + "dangerous_aces_found": 8, + "findings": [ + { + "severity": "critical", + "target_object": "CN=Domain Admins,CN=Users,DC=corp,DC=example,DC=com", + "target_type": "group", + "trustee": "CORP\\helpdesk-team", + "permission": "GenericAll", + "access_mask": "0x10000000", + "ace_type": "ACCESS_ALLOWED", + "attack_path": "GenericAll on Domain Admins group allows adding arbitrary members", + "remediation": "Remove GenericAll ACE for helpdesk-team on Domain Admins" + } + ] +} +``` diff --git a/personas/_shared/skills/analyzing-active-directory-acl-abuse/references/api-reference.md b/personas/_shared/skills/analyzing-active-directory-acl-abuse/references/api-reference.md new file mode 100644 index 0000000..b2c5500 --- /dev/null +++ b/personas/_shared/skills/analyzing-active-directory-acl-abuse/references/api-reference.md @@ -0,0 +1,94 @@ +# Active Directory ACL Abuse API Reference + +## ldap3 Python Connection + +```python +from ldap3 import Server, Connection, ALL, NTLM, SUBTREE + +server = Server("192.168.1.10", get_info=ALL, use_ssl=False) +conn = Connection(server, user="DOMAIN\\user", password="pass", + authentication=NTLM, auto_bind=True) + +# Search with nTSecurityDescriptor +conn.search( + "DC=corp,DC=example,DC=com", + "(objectClass=group)", + search_scope=SUBTREE, + attributes=["distinguishedName", "sAMAccountName", + "objectClass", "nTSecurityDescriptor"], +) +``` + +## SDDL ACE Format + +``` +ACE String: (ace_type;ace_flags;rights;object_guid;inherit_guid;trustee_sid) +Example: (A;;GA;;;S-1-5-21-xxx-512) +``` + +| Component | Description | +|-----------|-------------| +| `A` | Access Allowed | +| `D` | Access Denied | +| `OA` | Object Access Allowed | +| `GA` | Generic All | +| `GW` | Generic Write | +| `WD` | Write DACL | +| `WO` | Write Owner | + +## Dangerous Permission Bitmasks + +| Permission | Hex Mask | Risk | +|-----------|----------|------| +| GenericAll | `0x10000000` | Full control over object | +| GenericWrite | `0x40000000` | Modify all writable attributes | +| WriteDACL | `0x00040000` | Modify object permissions | +| WriteOwner | `0x00080000` | Take object ownership | +| WriteProperty | `0x00000020` | Write specific properties | +| ExtendedRight | `0x00000100` | Extended rights (password reset, etc.) | +| Self | `0x00000008` | Self-membership modification | +| Delete | `0x00010000` | Delete the object | + +## BloodHound Cypher Queries for ACL Paths + +```cypher +-- Find all users with GenericAll on Domain Admins +MATCH p=(n:User)-[r:GenericAll]->(g:Group {name:"DOMAIN ADMINS@CORP.COM"}) +RETURN p + +-- Find WriteDACL paths from non-admins to high-value targets +MATCH (n:User {admincount:false}) +MATCH p=allShortestPaths((n)-[r:WriteDacl|WriteOwner|GenericAll*1..]->(m:Group)) +WHERE m.highvalue = true +RETURN p + +-- Find GenericWrite on computers for RBCD attacks +MATCH p=(n:User)-[r:GenericWrite]->(c:Computer) +WHERE NOT n.admincount +RETURN n.name, c.name + +-- Enumerate all outbound ACL edges for a principal +MATCH p=(n {name:"HELPDESK@CORP.COM"})-[r:GenericAll|GenericWrite|WriteDacl|WriteOwner|Owns]->(m) +RETURN type(r), m.name, labels(m) + +-- Find shortest ACL abuse path to Domain Admin +MATCH (n:User {name:"JSMITH@CORP.COM"}) +MATCH (da:Group {name:"DOMAIN ADMINS@CORP.COM"}) +MATCH p=shortestPath((n)-[r:MemberOf|GenericAll|GenericWrite|WriteDacl|WriteOwner|Owns|ForceChangePassword*1..]->(da)) +RETURN p +``` + +## PowerView Commands for ACL Enumeration + +```powershell +# Get ACL for Domain Admins group +Get-DomainObjectAcl -Identity "Domain Admins" -ResolveGUIDs + +# Find interesting ACEs for non-admin users +Find-InterestingDomainAcl -ResolveGUIDs | Where-Object { + $_.ActiveDirectoryRights -match "GenericAll|WriteDacl|WriteOwner" +} + +# Get ACL for specific OU +Get-DomainObjectAcl -SearchBase "OU=Servers,DC=corp,DC=com" -ResolveGUIDs +``` diff --git a/personas/_shared/skills/analyzing-active-directory-acl-abuse/scripts/agent.py b/personas/_shared/skills/analyzing-active-directory-acl-abuse/scripts/agent.py new file mode 100644 index 0000000..e4c294d --- /dev/null +++ b/personas/_shared/skills/analyzing-active-directory-acl-abuse/scripts/agent.py @@ -0,0 +1,258 @@ +#!/usr/bin/env python3 +"""Active Directory ACL abuse detection using ldap3 to find dangerous permissions.""" + +import argparse +import json +import struct + +from ldap3 import Server, Connection, ALL, NTLM, SUBTREE + + +DANGEROUS_MASKS = { + "GenericAll": 0x10000000, + "GenericWrite": 0x40000000, + "WriteDACL": 0x00040000, + "WriteOwner": 0x00080000, + "WriteProperty": 0x00000020, + "Self": 0x00000008, + "ExtendedRight": 0x00000100, + "DeleteChild": 0x00000002, + "Delete": 0x00010000, +} + +ADMIN_SIDS = { + "S-1-5-18", + "S-1-5-32-544", + "S-1-5-9", +} + +ADMIN_RID_SUFFIXES = { + "-500", + "-512", + "-516", + "-518", + "-519", + "-498", +} + +ATTACK_PATHS = { + "GenericAll": { + "user": "Full control allows password reset, Kerberoasting via SPN, or shadow credential attack", + "group": "Full control allows adding arbitrary members to the group", + "computer": "Full control allows resource-based constrained delegation attack", + "organizationalUnit": "Full control allows linking malicious GPO or moving objects", + }, + "WriteDACL": { + "user": "Can modify DACL to grant self GenericAll, then reset password", + "group": "Can modify DACL to grant self write membership, then add self", + "computer": "Can modify DACL to grant self full control on machine account", + "organizationalUnit": "Can modify DACL to gain control over OU child objects", + }, + "WriteOwner": { + "user": "Can take ownership then modify DACL to escalate privileges", + "group": "Can take ownership of group then modify membership", + "computer": "Can take ownership then configure delegation abuse", + "organizationalUnit": "Can take ownership then control OU policies", + }, + "GenericWrite": { + "user": "Can write scriptPath for logon script execution or modify SPN for Kerberoasting", + "group": "Can modify group attributes including membership", + "computer": "Can write msDS-AllowedToActOnBehalfOfOtherIdentity for RBCD attack", + "organizationalUnit": "Can modify OU attributes and link GPO", + }, +} + + +def is_admin_sid(sid: str, domain_sid: str) -> bool: + if sid in ADMIN_SIDS: + return True + for suffix in ADMIN_RID_SUFFIXES: + if sid == domain_sid + suffix: + return True + return False + + +def parse_sid(raw: bytes) -> str: + if len(raw) < 8: + return "" + revision = raw[0] + sub_auth_count = raw[1] + authority = int.from_bytes(raw[2:8], byteorder="big") + subs = [] + for i in range(sub_auth_count): + offset = 8 + i * 4 + if offset + 4 > len(raw): + break + subs.append(struct.unpack(" list: + aces = [] + if len(descriptor_bytes) < 20: + return aces + revision = descriptor_bytes[0] + control = struct.unpack("= len(descriptor_bytes): + return aces + dacl = descriptor_bytes[dacl_offset:] + if len(dacl) < 8: + return aces + acl_size = struct.unpack(" len(dacl): + break + ace_type = dacl[offset] + ace_flags = dacl[offset + 1] + ace_size = struct.unpack(" len(dacl): + break + if ace_type in (0x00, 0x05): + if offset + 8 <= len(dacl): + access_mask = struct.unpack(" str: + try: + conn.search(base_dn, f"(objectSid={sid})", attributes=["sAMAccountName", "cn"]) + if conn.entries: + entry = conn.entries[0] + return str(entry.sAMAccountName) if hasattr(entry, "sAMAccountName") else str(entry.cn) + except Exception: + pass + return sid + + +def get_domain_sid(conn: Connection, base_dn: str) -> str: + conn.search(base_dn, "(objectClass=domain)", attributes=["objectSid"]) + if conn.entries: + raw = conn.entries[0].objectSid.raw_values[0] + return parse_sid(raw) + return "" + + +def analyze_acls(dc_ip: str, domain: str, username: str, password: str, + target_ou: str) -> dict: + server = Server(dc_ip, get_info=ALL, use_ssl=False) + domain_parts = domain.split(".") + base_dn = ",".join(f"DC={p}" for p in domain_parts) + search_base = target_ou if target_ou else base_dn + ntlm_user = f"{domain}\\{username}" + + conn = Connection(server, user=ntlm_user, password=password, + authentication=NTLM, auto_bind=True) + domain_sid = get_domain_sid(conn, base_dn) + + conn.search( + search_base, + "(|(objectClass=user)(objectClass=group)(objectClass=computer)(objectClass=organizationalUnit))", + search_scope=SUBTREE, + attributes=["distinguishedName", "sAMAccountName", "objectClass", "nTSecurityDescriptor"], + ) + + findings = [] + objects_scanned = 0 + sid_cache = {} + + for entry in conn.entries: + objects_scanned += 1 + dn = str(entry.distinguishedName) + obj_classes = [str(c) for c in entry.objectClass.values] if hasattr(entry, "objectClass") else [] + obj_type = "unknown" + for oc in obj_classes: + if oc.lower() in ("user", "group", "computer", "organizationalunit"): + obj_type = oc.lower() + break + + if not hasattr(entry, "nTSecurityDescriptor"): + continue + raw_sd = entry.nTSecurityDescriptor.raw_values + if not raw_sd: + continue + sd_bytes = raw_sd[0] + aces = parse_acl(sd_bytes) + + for ace in aces: + trustee_sid = ace["trustee_sid"] + if is_admin_sid(trustee_sid, domain_sid): + continue + if trustee_sid not in sid_cache: + sid_cache[trustee_sid] = resolve_sid(conn, base_dn, trustee_sid) + trustee_name = sid_cache[trustee_sid] + + for perm in ace["permissions"]: + if perm in ("Delete", "DeleteChild", "Self", "WriteProperty", "ExtendedRight"): + severity = "medium" + else: + severity = "critical" + attack = ATTACK_PATHS.get(perm, {}).get(obj_type, + f"{perm} on {obj_type} may allow privilege escalation") + findings.append({ + "severity": severity, + "target_object": dn, + "target_type": obj_type, + "trustee": trustee_name, + "trustee_sid": trustee_sid, + "permission": perm, + "access_mask": ace["access_mask"], + "ace_type": ace["ace_type"], + "attack_path": attack, + "remediation": f"Remove {perm} ACE for {trustee_name} on {dn}", + }) + + conn.unbind() + findings.sort(key=lambda f: 0 if f["severity"] == "critical" else 1) + return { + "domain": domain, + "domain_sid": domain_sid, + "search_base": search_base, + "objects_scanned": objects_scanned, + "dangerous_aces_found": len(findings), + "findings": findings, + } + + +def main(): + parser = argparse.ArgumentParser(description="Active Directory ACL Abuse Analyzer") + parser.add_argument("--dc-ip", required=True, help="Domain Controller IP address") + parser.add_argument("--domain", required=True, help="AD domain name (e.g., corp.example.com)") + parser.add_argument("--username", required=True, help="Domain username for LDAP bind") + parser.add_argument("--password", required=True, help="Domain user password") + parser.add_argument("--target-ou", default=None, + help="Target OU distinguished name to scope the search") + parser.add_argument("--output", default=None, help="Output JSON file path") + args = parser.parse_args() + + result = analyze_acls(args.dc_ip, args.domain, args.username, + args.password, args.target_ou) + report = json.dumps(result, indent=2) + if args.output: + with open(args.output, "w") as f: + f.write(report) + print(report) + + +if __name__ == "__main__": + main() diff --git a/personas/_shared/skills/analyzing-android-malware-with-apktool/LICENSE b/personas/_shared/skills/analyzing-android-malware-with-apktool/LICENSE new file mode 100644 index 0000000..d885118 --- /dev/null +++ b/personas/_shared/skills/analyzing-android-malware-with-apktool/LICENSE @@ -0,0 +1,201 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to the Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by the Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding any notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. Please do not remove or change + the license header comment from a contributed file except when + necessary. + + Copyright 2026 mukul975 + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/personas/_shared/skills/analyzing-android-malware-with-apktool/SKILL.md b/personas/_shared/skills/analyzing-android-malware-with-apktool/SKILL.md new file mode 100644 index 0000000..1bb9a19 --- /dev/null +++ b/personas/_shared/skills/analyzing-android-malware-with-apktool/SKILL.md @@ -0,0 +1,61 @@ +--- +name: analyzing-android-malware-with-apktool +description: Perform static analysis of Android APK malware samples using apktool for decompilation, jadx for Java source + recovery, and androguard for permission analysis, manifest inspection, and suspicious API call detection. +domain: cybersecurity +subdomain: malware-analysis +tags: +- Android +- APK +- apktool +- jadx +- androguard +- mobile-malware +- static-analysis +- reverse-engineering +version: '1.0' +author: mahipal +license: Apache-2.0 +nist_csf: +- DE.AE-02 +- RS.AN-03 +- ID.RA-01 +- DE.CM-01 +--- + +# Analyzing Android Malware with Apktool + +## Overview + +Android malware distributed as APK files can be statically analyzed to extract permissions, activities, services, broadcast receivers, and suspicious API calls without executing the sample. This skill uses androguard for programmatic APK analysis, identifying dangerous permission combinations, obfuscated code patterns, dynamic code loading, reflection-based API calls, and network communication indicators. + + +## When to Use + +- When investigating security incidents that require analyzing android malware with apktool +- When building detection rules or threat hunting queries for this domain +- When SOC analysts need structured procedures for this analysis type +- When validating security monitoring coverage for related attack techniques + +## Prerequisites + +- Python 3.9+ with `androguard` +- apktool (for resource decompilation) +- jadx (for Java source recovery, optional) +- Isolated analysis environment (VM or sandbox) +- Sample APK files for analysis + +## Steps + +1. Parse APK with androguard to extract manifest metadata +2. Enumerate requested permissions and flag dangerous combinations +3. List activities, services, receivers, and providers from manifest +4. Scan for suspicious API calls (reflection, crypto, SMS, telephony) +5. Detect dynamic code loading patterns (DexClassLoader, Runtime.exec) +6. Extract hardcoded URLs, IPs, and C2 indicators from strings +7. Generate risk assessment report with MITRE ATT&CK mobile mappings + +## Expected Output + +- JSON report with permission analysis, component listing, suspicious API calls, network indicators, and risk score +- Extracted strings and potential IOCs from the APK diff --git a/personas/_shared/skills/analyzing-android-malware-with-apktool/references/api-reference.md b/personas/_shared/skills/analyzing-android-malware-with-apktool/references/api-reference.md new file mode 100644 index 0000000..44d8547 --- /dev/null +++ b/personas/_shared/skills/analyzing-android-malware-with-apktool/references/api-reference.md @@ -0,0 +1,69 @@ +# API Reference — Analyzing Android Malware with Apktool + +## Libraries Used +- **androguard**: Python APK/DEX analysis — `AnalyzeAPK()`, permission enumeration, API call scanning +- **re**: Regex extraction of URLs, IPs, base64 patterns from DEX strings +- **json**: JSON serialization for analysis reports + +## CLI Interface +``` +python agent.py sample.apk permissions +python agent.py sample.apk manifest +python agent.py sample.apk apis +python agent.py sample.apk strings +python agent.py sample.apk full +python agent.py sample.apk # defaults to full analysis +``` + +## Core Functions + +### `analyze_permissions(apk)` — Permission risk assessment +Calls `apk.get_permissions()`. Flags 20 dangerous permissions including +SEND_SMS, READ_CONTACTS, BIND_DEVICE_ADMIN, BIND_ACCESSIBILITY_SERVICE. +Risk: CRITICAL >= 8 dangerous, HIGH >= 5, MEDIUM >= 2, LOW < 2. + +### `analyze_manifest(apk)` — Manifest component extraction +Calls `apk.get_activities()`, `get_services()`, `get_receivers()`, `get_providers()`. +Returns package name, version, SDK levels, and all component lists. + +### `scan_suspicious_apis(dx)` — Suspicious API call detection +Searches DEX analysis for 14 patterns including: +- `Runtime.exec`, `ProcessBuilder.start` — command execution +- `DexClassLoader.loadClass` — dynamic code loading +- `Method.invoke`, `Class.forName` — reflection +- `Cipher.getInstance` — cryptographic operations +- `SmsManager.sendTextMessage` — SMS abuse + +### `extract_strings(dx, apk)` — IOC extraction from DEX strings +Regex extraction of HTTP/HTTPS URLs, external IP addresses, and base64 strings. +Filters out private IP ranges (10.x, 192.168.x, 172.16.x, 127.x). + +### `detect_obfuscation(apk, dx)` — Obfuscation indicator detection +Checks for single-letter class names (ProGuard), multi-DEX, native libraries. + +### `full_analysis(apk_path)` — Comprehensive malware assessment + +## Androguard API +| Method | Returns | +|--------|---------| +| `AnalyzeAPK(path)` | `(APK, list[DEX], Analysis)` tuple | +| `apk.get_permissions()` | List of Android permissions | +| `apk.get_activities()` | Activity component names | +| `apk.get_services()` | Service component names | +| `apk.get_receivers()` | BroadcastReceiver names | +| `apk.get_package()` | Package name string | +| `dx.find_methods(classname, methodname)` | Matching method analysis objects | +| `dx.get_strings()` | All strings from DEX files | +| `dx.get_classes()` | All class analysis objects | + +## Risk Scoring +| Factor | Max Points | +|--------|-----------| +| Dangerous permissions (8 pts each) | 40 | +| Suspicious API calls (10 pts each) | 30 | +| External IPs (5 pts each) | 15 | +| Obfuscation detected | 15 | + +## Dependencies +- `androguard` >= 3.4.0 +- Isolated analysis environment recommended diff --git a/personas/_shared/skills/analyzing-android-malware-with-apktool/scripts/agent.py b/personas/_shared/skills/analyzing-android-malware-with-apktool/scripts/agent.py new file mode 100644 index 0000000..f2daacc --- /dev/null +++ b/personas/_shared/skills/analyzing-android-malware-with-apktool/scripts/agent.py @@ -0,0 +1,228 @@ +#!/usr/bin/env python3 +"""Agent for static analysis of Android APK malware using androguard.""" + +import json +import re +import argparse +from datetime import datetime + +try: + from androguard.core.apk import APK + from androguard.core.dex import DEX + from androguard.misc import AnalyzeAPK +except ImportError: + APK = None + AnalyzeAPK = None + +DANGEROUS_PERMISSIONS = [ + "android.permission.SEND_SMS", "android.permission.READ_SMS", + "android.permission.RECEIVE_SMS", "android.permission.READ_CONTACTS", + "android.permission.READ_CALL_LOG", "android.permission.RECORD_AUDIO", + "android.permission.CAMERA", "android.permission.ACCESS_FINE_LOCATION", + "android.permission.READ_PHONE_STATE", "android.permission.CALL_PHONE", + "android.permission.WRITE_EXTERNAL_STORAGE", "android.permission.READ_EXTERNAL_STORAGE", + "android.permission.INSTALL_PACKAGES", "android.permission.REQUEST_INSTALL_PACKAGES", + "android.permission.SYSTEM_ALERT_WINDOW", "android.permission.BIND_ACCESSIBILITY_SERVICE", + "android.permission.BIND_DEVICE_ADMIN", "android.permission.RECEIVE_BOOT_COMPLETED", + "android.permission.WRITE_SETTINGS", "android.permission.CHANGE_WIFI_STATE", +] + +SUSPICIOUS_API_PATTERNS = [ + r"Ljava/lang/Runtime;->exec", + r"Ljava/lang/ProcessBuilder;->start", + r"Ldalvik/system/DexClassLoader;->loadClass", + r"Ljava/lang/reflect/Method;->invoke", + r"Ljava/lang/Class;->forName", + r"Ljavax/crypto/Cipher;->getInstance", + r"Landroid/telephony/SmsManager;->sendTextMessage", + r"Landroid/app/admin/DevicePolicyManager;->lockNow", + r"Landroid/content/pm/PackageManager;->setComponentEnabledSetting", + r"Ljava/net/HttpURLConnection;->connect", + r"Lokhttp3/OkHttpClient;->newCall", + r"Landroid/webkit/WebView;->loadUrl", + r"Landroid/os/Build;->SERIAL", + r"Landroid/provider/Settings\$Secure;->getString", +] + + +def analyze_permissions(apk): + """Analyze requested permissions and flag dangerous ones.""" + permissions = apk.get_permissions() + dangerous = [p for p in permissions if p in DANGEROUS_PERMISSIONS] + return { + "total_permissions": len(permissions), + "permissions": permissions, + "dangerous_permissions": dangerous, + "dangerous_count": len(dangerous), + "permission_risk": "CRITICAL" if len(dangerous) >= 8 else "HIGH" if len(dangerous) >= 5 else "MEDIUM" if len(dangerous) >= 2 else "LOW", + } + + +def analyze_manifest(apk): + """Extract manifest components: activities, services, receivers, providers.""" + activities = apk.get_activities() + services = apk.get_services() + receivers = apk.get_receivers() + providers = apk.get_providers() + return { + "package_name": apk.get_package(), + "app_name": apk.get_app_name(), + "version_name": apk.get_androidversion_name(), + "version_code": apk.get_androidversion_code(), + "min_sdk": apk.get_min_sdk_version(), + "target_sdk": apk.get_target_sdk_version(), + "activities": list(activities), + "services": list(services), + "receivers": list(receivers), + "providers": list(providers), + "activity_count": len(activities), + "service_count": len(services), + "receiver_count": len(receivers), + "provider_count": len(providers), + } + + +def scan_suspicious_apis(dx): + """Scan DEX analysis for suspicious API calls.""" + findings = [] + if not dx: + return findings + for pattern in SUSPICIOUS_API_PATTERNS: + class_name = pattern.split(";->")[0] + ";" + method_name = pattern.split(";->")[1] if ";->" in pattern else None + for method in dx.find_methods(classname=class_name, methodname=method_name): + xrefs = list(method.get_xref_from()) + if xrefs: + findings.append({ + "api": pattern, + "callers": len(xrefs), + "first_caller_class": str(xrefs[0][0].name) if xrefs else None, + }) + return findings + + +def extract_strings(dx, apk): + """Extract suspicious strings: URLs, IPs, base64 patterns.""" + url_pattern = re.compile(r'https?://[\w\-._~:/?#\[\]@!$&\'()*+,;=]+', re.IGNORECASE) + ip_pattern = re.compile(r'\b(?:\d{1,3}\.){3}\d{1,3}\b') + b64_pattern = re.compile(r'[A-Za-z0-9+/]{30,}={0,2}') + + urls = set() + ips = set() + b64_strings = [] + + if dx: + for s in dx.get_strings(): + val = str(s) + urls.update(url_pattern.findall(val)) + ips.update(ip_pattern.findall(val)) + b64_matches = b64_pattern.findall(val) + b64_strings.extend(b64_matches[:5]) + + private_ips = {"10.", "192.168.", "172.16.", "127.0."} + external_ips = [ip for ip in ips if not any(ip.startswith(p) for p in private_ips)] + + return { + "urls": sorted(urls)[:30], + "external_ips": sorted(external_ips)[:20], + "suspicious_base64": b64_strings[:10], + "url_count": len(urls), + "external_ip_count": len(external_ips), + } + + +def detect_obfuscation(apk, dx): + """Detect code obfuscation indicators.""" + indicators = [] + if dx: + short_class_names = 0 + for cls in dx.get_classes(): + name = str(cls.name) + parts = name.replace("/", ".").split(".") + if any(len(p) == 1 and p.isalpha() for p in parts): + short_class_names += 1 + if short_class_names > 10: + indicators.append({"type": "single_letter_classes", "count": short_class_names}) + + dex_files = [f for f in apk.get_files() if f.endswith(".dex")] + if len(dex_files) > 1: + indicators.append({"type": "multi_dex", "dex_count": len(dex_files)}) + + native_libs = [f for f in apk.get_files() if f.endswith(".so")] + if native_libs: + indicators.append({"type": "native_libraries", "libs": native_libs[:10]}) + + return { + "obfuscation_indicators": indicators, + "likely_obfuscated": len(indicators) > 0, + } + + +def full_analysis(apk_path): + """Run comprehensive APK malware analysis.""" + if not APK or not AnalyzeAPK: + return {"error": "androguard not installed: pip install androguard"} + + a, d, dx = AnalyzeAPK(apk_path) + + perm_analysis = analyze_permissions(a) + manifest = analyze_manifest(a) + suspicious_apis = scan_suspicious_apis(dx) + strings = extract_strings(dx, a) + obfuscation = detect_obfuscation(a, dx) + + risk_score = 0 + risk_score += min(perm_analysis["dangerous_count"] * 8, 40) + risk_score += min(len(suspicious_apis) * 10, 30) + risk_score += min(strings["external_ip_count"] * 5, 15) + risk_score += 15 if obfuscation["likely_obfuscated"] else 0 + risk_score = min(risk_score, 100) + + return { + "analysis_type": "Android APK Static Analysis", + "timestamp": datetime.utcnow().isoformat(), + "file": apk_path, + "manifest": manifest, + "permissions": perm_analysis, + "suspicious_apis": suspicious_apis[:20], + "strings": strings, + "obfuscation": obfuscation, + "risk_score": risk_score, + "risk_level": "CRITICAL" if risk_score >= 70 else "HIGH" if risk_score >= 50 else "MEDIUM" if risk_score >= 25 else "LOW", + "mitre_techniques": [ + {"id": "T1418", "name": "Software Discovery"} if manifest["service_count"] > 5 else None, + {"id": "T1417", "name": "Input Capture"} if "android.permission.BIND_ACCESSIBILITY_SERVICE" in perm_analysis["permissions"] else None, + {"id": "T1582", "name": "SMS Control"} if "android.permission.SEND_SMS" in perm_analysis["permissions"] else None, + {"id": "T1404", "name": "Exploitation for Privilege Escalation"} if any("DevicePolicyManager" in a.get("api", "") for a in suspicious_apis) else None, + ], + } + + +def main(): + parser = argparse.ArgumentParser(description="Android APK Malware Analysis Agent") + parser.add_argument("apk", help="Path to APK file") + sub = parser.add_subparsers(dest="command") + sub.add_parser("permissions", help="Analyze permissions") + sub.add_parser("manifest", help="Extract manifest components") + sub.add_parser("apis", help="Scan for suspicious API calls") + sub.add_parser("strings", help="Extract URLs, IPs, and encoded strings") + sub.add_parser("full", help="Full malware analysis") + args = parser.parse_args() + + if args.command == "full" or args.command is None: + result = full_analysis(args.apk) + else: + a, d, dx = AnalyzeAPK(args.apk) + if args.command == "permissions": + result = analyze_permissions(a) + elif args.command == "manifest": + result = analyze_manifest(a) + elif args.command == "apis": + result = scan_suspicious_apis(dx) + elif args.command == "strings": + result = extract_strings(dx, a) + print(json.dumps(result, indent=2, default=str)) + + +if __name__ == "__main__": + main() diff --git a/personas/_shared/skills/analyzing-api-gateway-access-logs/LICENSE b/personas/_shared/skills/analyzing-api-gateway-access-logs/LICENSE new file mode 100644 index 0000000..d885118 --- /dev/null +++ b/personas/_shared/skills/analyzing-api-gateway-access-logs/LICENSE @@ -0,0 +1,201 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to the Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by the Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding any notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. Please do not remove or change + the license header comment from a contributed file except when + necessary. + + Copyright 2026 mukul975 + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/personas/_shared/skills/analyzing-api-gateway-access-logs/SKILL.md b/personas/_shared/skills/analyzing-api-gateway-access-logs/SKILL.md new file mode 100644 index 0000000..24c08d0 --- /dev/null +++ b/personas/_shared/skills/analyzing-api-gateway-access-logs/SKILL.md @@ -0,0 +1,71 @@ +--- +name: analyzing-api-gateway-access-logs +description: 'Parses API Gateway access logs (AWS API Gateway, Kong, Nginx) to detect BOLA/IDOR attacks, rate limit bypass, + credential scanning, and injection attempts. Uses pandas for statistical analysis of request patterns and anomaly detection. + Use when investigating API abuse or building API-specific threat detection rules. + + ' +domain: cybersecurity +subdomain: security-operations +tags: +- analyzing +- api +- gateway +- access +version: '1.0' +author: mahipal +license: Apache-2.0 +nist_csf: +- DE.CM-01 +- RS.MA-01 +- GV.OV-01 +- DE.AE-02 +--- + +# Analyzing API Gateway Access Logs + + +## When to Use + +- When investigating security incidents that require analyzing api gateway access logs +- When building detection rules or threat hunting queries for this domain +- When SOC analysts need structured procedures for this analysis type +- When validating security monitoring coverage for related attack techniques + +## Prerequisites + +- Familiarity with security operations concepts and tools +- Access to a test or lab environment for safe execution +- Python 3.8+ with required dependencies installed +- Appropriate authorization for any testing activities + +## Instructions + +Parse API gateway access logs to identify attack patterns including broken object +level authorization (BOLA), excessive data exposure, and injection attempts. + +```python +import pandas as pd + +df = pd.read_json("api_gateway_logs.json", lines=True) +# Detect BOLA: same user accessing many different resource IDs +bola = df.groupby(["user_id", "endpoint"]).agg( + unique_ids=("resource_id", "nunique")).reset_index() +suspicious = bola[bola["unique_ids"] > 50] +``` + +Key detection patterns: +1. BOLA/IDOR: sequential resource ID enumeration +2. Rate limit bypass via header manipulation +3. Credential scanning (401 surges from single source) +4. SQL/NoSQL injection in query parameters +5. Unusual HTTP methods (DELETE, PATCH) on read-only endpoints + +## Examples + +```python +# Detect 401 surges indicating credential scanning +auth_failures = df[df["status_code"] == 401] +scanner_ips = auth_failures.groupby("source_ip").size() +scanners = scanner_ips[scanner_ips > 100] +``` diff --git a/personas/_shared/skills/analyzing-api-gateway-access-logs/references/api-reference.md b/personas/_shared/skills/analyzing-api-gateway-access-logs/references/api-reference.md new file mode 100644 index 0000000..54b6239 --- /dev/null +++ b/personas/_shared/skills/analyzing-api-gateway-access-logs/references/api-reference.md @@ -0,0 +1,58 @@ +# API Reference: Analyzing API Gateway Access Logs + +## AWS API Gateway Log Fields + +```json +{ + "requestId": "abc-123", + "ip": "203.0.113.50", + "httpMethod": "GET", + "resourcePath": "/api/users/{id}", + "status": 200, + "requestTime": "2025-03-15T14:00:00Z", + "responseLength": 1024 +} +``` + +## Pandas Log Analysis + +```python +import pandas as pd + +df = pd.read_json("access_logs.json", lines=True) + +# BOLA detection +df.groupby("user_id")["resource_id"].nunique() + +# Auth failure surge +df[df["status_code"] == 401].groupby("source_ip").size() + +# Request velocity +df.set_index("timestamp").resample("1min").size() +``` + +## OWASP API Top 10 Patterns + +| Risk | Detection Pattern | +|------|-------------------| +| BOLA (API1) | User accessing > 50 unique resource IDs | +| Broken Auth (API2) | > 100 401/403 from single IP | +| Excessive Data (API3) | Response size > 10x average | +| Rate Limit (API4) | > 100 req/min from single IP | +| BFLA (API5) | DELETE/PUT on read-only endpoints | +| Injection (API8) | SQL/NoSQL patterns in params | + +## Injection Regex Patterns + +```python +sql = r"union\s+select|drop\s+table|'\s*or\s+'1'" +nosql = r"\$ne|\$gt|\$regex|\$where" +xss = r"= threshold] + for _, row in bola_suspects.iterrows(): + findings.append({ + "user": row[user_col], + "unique_resources_accessed": int(row["unique_resources"]), + "total_requests": int(row["total_requests"]), + "type": "BOLA/IDOR", + "severity": "CRITICAL", + }) + return findings + + +def detect_auth_scanning(df, threshold=100): + """Detect credential scanning via 401/403 response surges.""" + findings = [] + auth_failures = df[df["status_code"].isin([401, 403])] + if auth_failures.empty: + return findings + ip_col = "source_ip" if "source_ip" in df.columns else "client_ip" + ip_failures = auth_failures.groupby(ip_col).agg( + failure_count=("status_code", "count"), + unique_endpoints=("request_path", "nunique") if "request_path" in df.columns + else ("path", "nunique"), + ).reset_index() + scanners = ip_failures[ip_failures["failure_count"] >= threshold] + for _, row in scanners.iterrows(): + findings.append({ + "source_ip": row[ip_col], + "auth_failures": int(row["failure_count"]), + "endpoints_probed": int(row["unique_endpoints"]), + "type": "credential_scanning", + "severity": "HIGH", + }) + return findings + + +def detect_injection_attempts(df): + """Detect SQL/NoSQL injection attempts in request parameters.""" + injection_patterns = [ + r"(?:union\s+select|select\s+.*\s+from|drop\s+table|insert\s+into)", + r"(?:'\s*or\s+'1'\s*=\s*'1|'\s*or\s+1\s*=\s*1)", + r'(?:\$ne|\$gt|\$lt|\$regex|\$where)', + r'(?: threshold] + if len(bursts) > 0: + findings.append({ + "source_ip": ip, + "max_requests_per_min": int(resampled.max()), + "burst_periods": len(bursts), + "type": "rate_limit_bypass", + "severity": "MEDIUM", + }) + return sorted(findings, key=lambda x: x["max_requests_per_min"], reverse=True)[:50] + + +def detect_unusual_methods(df): + """Detect unusual HTTP methods on typically read-only endpoints.""" + findings = [] + dangerous_methods = {"DELETE", "PUT", "PATCH"} + method_col = "method" if "method" in df.columns else "http_method" + path_col = "request_path" if "request_path" in df.columns else "path" + unusual = df[df[method_col].str.upper().isin(dangerous_methods)] + for _, row in unusual.iterrows(): + findings.append({ + "source_ip": row.get("source_ip", row.get("client_ip", "")), + "method": row[method_col], + "path": row[path_col], + "status_code": int(row.get("status_code", 0)), + "type": "unusual_method", + "severity": "MEDIUM", + }) + return findings[:200] + + +def main(): + parser = argparse.ArgumentParser(description="API Gateway Log Analysis Agent") + parser.add_argument("--log-file", required=True, help="API gateway log file") + parser.add_argument("--output", default="api_gateway_report.json") + parser.add_argument("--action", choices=[ + "bola", "auth_scan", "injection", "rate_limit", "full_analysis" + ], default="full_analysis") + args = parser.parse_args() + + df = load_api_logs(args.log_file) + report = {"generated_at": datetime.utcnow().isoformat(), "total_requests": len(df), + "findings": {}} + print(f"[+] Loaded {len(df)} API requests") + + if args.action in ("bola", "full_analysis"): + findings = detect_bola_attacks(df) + report["findings"]["bola"] = findings + print(f"[+] BOLA suspects: {len(findings)}") + + if args.action in ("auth_scan", "full_analysis"): + findings = detect_auth_scanning(df) + report["findings"]["auth_scanning"] = findings + print(f"[+] Auth scanners: {len(findings)}") + + if args.action in ("injection", "full_analysis"): + findings = detect_injection_attempts(df) + report["findings"]["injection_attempts"] = findings + print(f"[+] Injection attempts: {len(findings)}") + + if args.action in ("rate_limit", "full_analysis"): + findings = detect_rate_limit_bypass(df) + report["findings"]["rate_limit_bypass"] = findings + print(f"[+] Rate limit bypasses: {len(findings)}") + + with open(args.output, "w") as f: + json.dump(report, f, indent=2, default=str) + print(f"[+] Report saved to {args.output}") + + +if __name__ == "__main__": + main() diff --git a/personas/_shared/skills/analyzing-apt-group-with-mitre-navigator/LICENSE b/personas/_shared/skills/analyzing-apt-group-with-mitre-navigator/LICENSE new file mode 100644 index 0000000..d885118 --- /dev/null +++ b/personas/_shared/skills/analyzing-apt-group-with-mitre-navigator/LICENSE @@ -0,0 +1,201 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to the Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by the Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding any notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. Please do not remove or change + the license header comment from a contributed file except when + necessary. + + Copyright 2026 mukul975 + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/personas/_shared/skills/analyzing-apt-group-with-mitre-navigator/SKILL.md b/personas/_shared/skills/analyzing-apt-group-with-mitre-navigator/SKILL.md new file mode 100644 index 0000000..9e5bfab --- /dev/null +++ b/personas/_shared/skills/analyzing-apt-group-with-mitre-navigator/SKILL.md @@ -0,0 +1,285 @@ +--- +name: analyzing-apt-group-with-mitre-navigator +description: Analyze advanced persistent threat (APT) group techniques using MITRE ATT&CK Navigator to create layered heatmaps + of adversary TTPs for detection gap analysis and threat-informed defense. +domain: cybersecurity +subdomain: threat-intelligence +tags: +- mitre-attack +- navigator +- apt +- threat-actor +- ttp-analysis +- heatmap +- detection-gap +- threat-intelligence +version: '1.0' +author: mahipal +license: Apache-2.0 +d3fend_techniques: +- Executable Denylisting +- Execution Isolation +- File Metadata Consistency Validation +- Content Format Conversion +- File Content Analysis +nist_csf: +- ID.RA-01 +- ID.RA-05 +- DE.CM-01 +- DE.AE-02 +--- +# Analyzing APT Group with MITRE ATT&CK Navigator + +## Overview + +MITRE ATT&CK Navigator is a web-based tool for annotating and exploring ATT&CK matrices, enabling analysts to visualize threat actor technique coverage, compare multiple APT groups, identify detection gaps, and build threat-informed defense strategies. This skill covers querying ATT&CK data programmatically, mapping APT group TTPs to Navigator layers, creating multi-layer overlays for gap analysis, and generating actionable intelligence reports for detection engineering teams. + + +## When to Use + +- When investigating security incidents that require analyzing apt group with mitre navigator +- When building detection rules or threat hunting queries for this domain +- When SOC analysts need structured procedures for this analysis type +- When validating security monitoring coverage for related attack techniques + +## Prerequisites + +- Python 3.9+ with `attackcti`, `mitreattack-python`, `stix2`, `requests` libraries +- ATT&CK Navigator (https://mitre-attack.github.io/attack-navigator/) or local deployment +- Understanding of ATT&CK Enterprise matrix: 14 Tactics, 200+ Techniques, Sub-techniques +- Access to threat intelligence reports or MISP/OpenCTI for threat actor data +- Familiarity with STIX 2.1 Intrusion Set and Attack Pattern objects + +## Key Concepts + +### ATT&CK Navigator Layers + +Navigator layers are JSON files that annotate ATT&CK techniques with scores, colors, comments, and metadata. Each layer can represent a single APT group's technique usage, a detection capability map, or a combined overlay. Layer version 4.5 supports enterprise-attack, mobile-attack, and ics-attack domains with filtering by platform (Windows, Linux, macOS, Cloud, Azure AD, Office 365, SaaS). + +### APT Group Profiles in ATT&CK + +ATT&CK catalogs over 140 threat groups with documented technique usage. Each group profile includes aliases, targeted sectors, associated campaigns, software used, and technique mappings with procedure-level detail. Groups are identified by G-codes (e.g., G0016 for APT29, G0007 for APT28, G0032 for Lazarus Group). + +### Multi-Layer Analysis + +The Navigator supports loading multiple layers simultaneously, allowing analysts to overlay threat actor TTPs against detection coverage to identify gaps, compare multiple APT groups to find common techniques worth prioritizing, and track technique coverage changes over time. + +## Workflow + +### Step 1: Query ATT&CK Data for APT Group + +```python +from attackcti import attack_client +import json + +lift = attack_client() + +# Get all threat groups +groups = lift.get_groups() +print(f"Total ATT&CK groups: {len(groups)}") + +# Find APT29 (Cozy Bear / Midnight Blizzard) +apt29 = next((g for g in groups if g.get('name') == 'APT29'), None) +if apt29: + print(f"Group: {apt29['name']}") + print(f"Aliases: {apt29.get('aliases', [])}") + print(f"Description: {apt29.get('description', '')[:300]}") + +# Get techniques used by APT29 (G0016) +techniques = lift.get_techniques_used_by_group("G0016") +print(f"APT29 uses {len(techniques)} techniques") + +technique_map = {} +for tech in techniques: + tech_id = "" + for ref in tech.get("external_references", []): + if ref.get("source_name") == "mitre-attack": + tech_id = ref.get("external_id", "") + break + if tech_id: + tactics = [p.get("phase_name", "") for p in tech.get("kill_chain_phases", [])] + technique_map[tech_id] = { + "name": tech.get("name", ""), + "tactics": tactics, + "description": tech.get("description", "")[:500], + "platforms": tech.get("x_mitre_platforms", []), + "data_sources": tech.get("x_mitre_data_sources", []), + } +``` + +### Step 2: Generate Navigator Layer JSON + +```python +def create_navigator_layer(group_name, technique_map, color="#ff6666"): + techniques_list = [] + for tech_id, info in technique_map.items(): + for tactic in info["tactics"]: + techniques_list.append({ + "techniqueID": tech_id, + "tactic": tactic, + "color": color, + "comment": info["name"], + "enabled": True, + "score": 100, + "metadata": [ + {"name": "group", "value": group_name}, + {"name": "platforms", "value": ", ".join(info["platforms"])}, + ], + }) + + layer = { + "name": f"{group_name} TTP Coverage", + "versions": {"attack": "16.1", "navigator": "5.1.0", "layer": "4.5"}, + "domain": "enterprise-attack", + "description": f"Techniques attributed to {group_name}", + "filters": { + "platforms": ["Linux", "macOS", "Windows", "Cloud", + "Azure AD", "Office 365", "SaaS", "Google Workspace"] + }, + "sorting": 0, + "layout": { + "layout": "side", "aggregateFunction": "average", + "showID": True, "showName": True, + "showAggregateScores": False, "countUnscored": False, + }, + "hideDisabled": False, + "techniques": techniques_list, + "gradient": {"colors": ["#ffffff", color], "minValue": 0, "maxValue": 100}, + "legendItems": [ + {"label": f"Used by {group_name}", "color": color}, + {"label": "Not observed", "color": "#ffffff"}, + ], + "showTacticRowBackground": True, + "tacticRowBackground": "#dddddd", + "selectTechniquesAcrossTactics": True, + "selectSubtechniquesWithParent": False, + "selectVisibleTechniques": False, + } + return layer + +layer = create_navigator_layer("APT29", technique_map) +with open("apt29_layer.json", "w") as f: + json.dump(layer, f, indent=2) +print("[+] Layer saved: apt29_layer.json") +``` + +### Step 3: Compare Multiple APT Groups + +```python +groups_to_compare = {"G0016": "APT29", "G0007": "APT28", "G0032": "Lazarus Group"} +group_techniques = {} + +for gid, gname in groups_to_compare.items(): + techs = lift.get_techniques_used_by_group(gid) + tech_ids = set() + for t in techs: + for ref in t.get("external_references", []): + if ref.get("source_name") == "mitre-attack": + tech_ids.add(ref.get("external_id", "")) + group_techniques[gname] = tech_ids + +common_to_all = set.intersection(*group_techniques.values()) +print(f"Techniques common to all groups: {len(common_to_all)}") +for tid in sorted(common_to_all): + print(f" {tid}") + +for gname, techs in group_techniques.items(): + others = set.union(*[t for n, t in group_techniques.items() if n != gname]) + unique = techs - others + print(f"\nUnique to {gname}: {len(unique)} techniques") +``` + +### Step 4: Detection Gap Analysis with Layer Overlay + +```python +# Define your current detection capabilities +detected_techniques = { + "T1059", "T1059.001", "T1071", "T1071.001", "T1566", "T1566.001", + "T1547", "T1547.001", "T1053", "T1053.005", "T1078", "T1027", +} + +actor_techniques = set(technique_map.keys()) +covered = actor_techniques.intersection(detected_techniques) +gaps = actor_techniques - detected_techniques + +print(f"=== Detection Gap Analysis for APT29 ===") +print(f"Actor techniques: {len(actor_techniques)}") +print(f"Detected: {len(covered)} ({len(covered)/len(actor_techniques)*100:.0f}%)") +print(f"Gaps: {len(gaps)} ({len(gaps)/len(actor_techniques)*100:.0f}%)") + +# Create gap layer (red = undetected, green = detected) +gap_techniques = [] +for tech_id in actor_techniques: + info = technique_map.get(tech_id, {}) + for tactic in info.get("tactics", [""]): + color = "#66ff66" if tech_id in detected_techniques else "#ff3333" + gap_techniques.append({ + "techniqueID": tech_id, + "tactic": tactic, + "color": color, + "comment": f"{'DETECTED' if tech_id in detected_techniques else 'GAP'}: {info.get('name', '')}", + "enabled": True, + "score": 100 if tech_id in detected_techniques else 0, + }) + +gap_layer = { + "name": "APT29 Detection Gap Analysis", + "versions": {"attack": "16.1", "navigator": "5.1.0", "layer": "4.5"}, + "domain": "enterprise-attack", + "description": "Green = detected, Red = gap", + "techniques": gap_techniques, + "gradient": {"colors": ["#ff3333", "#66ff66"], "minValue": 0, "maxValue": 100}, + "legendItems": [ + {"label": "Detected", "color": "#66ff66"}, + {"label": "Detection Gap", "color": "#ff3333"}, + ], +} +with open("apt29_gap_layer.json", "w") as f: + json.dump(gap_layer, f, indent=2) +``` + +### Step 5: Tactic Breakdown Analysis + +```python +from collections import defaultdict + +tactic_breakdown = defaultdict(list) +for tech_id, info in technique_map.items(): + for tactic in info["tactics"]: + tactic_breakdown[tactic].append({"id": tech_id, "name": info["name"]}) + +tactic_order = [ + "reconnaissance", "resource-development", "initial-access", + "execution", "persistence", "privilege-escalation", + "defense-evasion", "credential-access", "discovery", + "lateral-movement", "collection", "command-and-control", + "exfiltration", "impact", +] + +print("\n=== APT29 Tactic Breakdown ===") +for tactic in tactic_order: + techs = tactic_breakdown.get(tactic, []) + if techs: + print(f"\n{tactic.upper()} ({len(techs)} techniques):") + for t in techs: + print(f" {t['id']}: {t['name']}") +``` + +## Validation Criteria + +- ATT&CK data queried successfully via TAXII server +- APT group mapped to all documented techniques with procedure examples +- Navigator layer JSON validates and renders correctly in ATT&CK Navigator +- Multi-layer overlay shows threat actor vs. detection coverage +- Detection gap analysis identifies unmonitored techniques with data source recommendations +- Cross-group comparison reveals shared and unique TTPs +- Output is actionable for detection engineering prioritization + +## References + +- [MITRE ATT&CK Navigator](https://mitre-attack.github.io/attack-navigator/) +- [ATT&CK Groups](https://attack.mitre.org/groups/) +- [attackcti Python Library](https://github.com/OTRF/ATTACK-Python-Client) +- [Navigator Layer Format v4.5](https://github.com/mitre-attack/attack-navigator/blob/master/layers/LAYERFORMATv4_5.md) +- [CISA Best Practices for MITRE ATT&CK Mapping](https://www.cisa.gov/sites/default/files/2023-01/Best%20Practices%20for%20MITRE%20ATTCK%20Mapping.pdf) +- [Picus: Leverage MITRE ATT&CK for Threat Intelligence](https://www.picussecurity.com/how-to-leverage-the-mitre-attack-framework-for-threat-intelligence) diff --git a/personas/_shared/skills/analyzing-apt-group-with-mitre-navigator/references/api-reference.md b/personas/_shared/skills/analyzing-apt-group-with-mitre-navigator/references/api-reference.md new file mode 100644 index 0000000..156b957 --- /dev/null +++ b/personas/_shared/skills/analyzing-apt-group-with-mitre-navigator/references/api-reference.md @@ -0,0 +1,97 @@ +# API Reference: MITRE ATT&CK Navigator APT Analysis + +## ATT&CK Navigator Layer Format + +### Layer JSON Structure +```json +{ + "name": "APT29 - TTPs", + "versions": {"attack": "14", "navigator": "4.9.1", "layer": "4.5"}, + "domain": "enterprise-attack", + "techniques": [ + { + "techniqueID": "T1566.001", + "tactic": "initial-access", + "color": "#ff6666", + "score": 100, + "comment": "Used by APT29", + "enabled": true + } + ], + "gradient": {"colors": ["#ffffff", "#ff6666"], "minValue": 0, "maxValue": 100} +} +``` + +## ATT&CK STIX Data Access + +### Download Enterprise ATT&CK Bundle +```bash +curl -o enterprise-attack.json \ + https://raw.githubusercontent.com/mitre/cti/master/enterprise-attack/enterprise-attack.json +``` + +### STIX Object Types +| Type | Description | +|------|-------------| +| `intrusion-set` | APT groups / threat actors | +| `attack-pattern` | Techniques and sub-techniques | +| `relationship` | Links groups to techniques (`uses`) | +| `malware` | Malware families | +| `tool` | Legitimate tools used by adversaries | + +## mitreattack-python Library + +### Installation +```bash +pip install mitreattack-python +``` + +### Query Group Techniques +```python +from mitreattack.stix20 import MitreAttackData + +attack = MitreAttackData("enterprise-attack.json") +groups = attack.get_groups() +for g in groups: + techs = attack.get_techniques_used_by_group(g) + print(f"{g.name}: {len(techs)} techniques") +``` + +### Get Technique Details +```python +technique = attack.get_object_by_attack_id("T1566.001", "attack-pattern") +print(technique.name) # Spearphishing Attachment +print(technique.x_mitre_platforms) # ['Windows', 'macOS', 'Linux'] +``` + +## Navigator CLI (attack-navigator) + +### Export Layer to SVG +```bash +npx attack-navigator-export \ + --layer layer.json \ + --output output.svg \ + --theme dark +``` + +## ATT&CK API (TAXII) +```python +from stix2 import TAXIICollectionSource, Filter +from taxii2client.v20 import Collection + +collection = Collection( + "https://cti-taxii.mitre.org/stix/collections/95ecc380-afe9-11e4-9b6c-751b66dd541e/" +) +tc_source = TAXIICollectionSource(collection) +groups = tc_source.query([Filter("type", "=", "intrusion-set")]) +``` + +## Key APT Groups Reference +| ID | Name | Known Aliases | +|----|------|--------------| +| G0016 | APT29 | Cozy Bear, The Dukes, NOBELIUM | +| G0007 | APT28 | Fancy Bear, Sofacy, Strontium | +| G0022 | APT3 | Gothic Panda, UPS | +| G0032 | Lazarus Group | HIDDEN COBRA, Zinc | +| G0074 | Dragonfly 2.0 | Energetic Bear, Berserk Bear | +| G0010 | Turla | Waterbug, Venomous Bear | diff --git a/personas/_shared/skills/analyzing-apt-group-with-mitre-navigator/scripts/agent.py b/personas/_shared/skills/analyzing-apt-group-with-mitre-navigator/scripts/agent.py new file mode 100644 index 0000000..ec0bf88 --- /dev/null +++ b/personas/_shared/skills/analyzing-apt-group-with-mitre-navigator/scripts/agent.py @@ -0,0 +1,244 @@ +#!/usr/bin/env python3 +"""APT group analysis agent using MITRE ATT&CK Navigator layers. + +Queries ATT&CK data, maps APT techniques to Navigator layers, +performs detection gap analysis, and generates threat-informed reports. +""" + +import json +import os +import sys +from collections import Counter + +try: + import requests + HAS_REQUESTS = True +except ImportError: + HAS_REQUESTS = False + +ATTACK_ENTERPRISE_URL = "https://raw.githubusercontent.com/mitre/cti/master/enterprise-attack/enterprise-attack.json" + +NAVIGATOR_LAYER_TEMPLATE = { + "name": "", + "versions": {"attack": "14", "navigator": "4.9.1", "layer": "4.5"}, + "domain": "enterprise-attack", + "description": "", + "filters": {"platforms": ["Windows", "Linux", "macOS", "Cloud"]}, + "sorting": 0, + "layout": {"layout": "side", "aggregateFunction": "average", "showID": False, + "showName": True, "showAggregateScores": False, "countUnscored": False}, + "hideDisabled": False, + "techniques": [], + "gradient": {"colors": ["#ffffff", "#ff6666"], "minValue": 0, "maxValue": 100}, + "legendItems": [], + "metadata": [], + "links": [], + "showTacticRowBackground": False, + "tacticRowBackground": "#dddddd", + "selectTechniquesAcrossTactics": True, + "selectSubtechniquesWithParent": False, + "selectVisibleTechniques": False, +} + + +def load_attack_data(filepath=None): + """Load ATT&CK STIX bundle from file or download.""" + if filepath and os.path.exists(filepath): + with open(filepath, "r", encoding="utf-8") as f: + return json.load(f) + if HAS_REQUESTS: + print("[*] Downloading ATT&CK Enterprise data...") + resp = requests.get(ATTACK_ENTERPRISE_URL, timeout=60) + resp.raise_for_status() + return resp.json() + return None + + +def extract_groups(bundle): + """Extract intrusion-set (APT group) objects from STIX bundle.""" + groups = {} + for obj in bundle.get("objects", []): + if obj.get("type") == "intrusion-set": + name = obj.get("name", "Unknown") + aliases = obj.get("aliases", []) + ext_refs = obj.get("external_references", []) + attack_id = "" + for ref in ext_refs: + if ref.get("source_name") == "mitre-attack": + attack_id = ref.get("external_id", "") + break + groups[obj["id"]] = { + "name": name, "id": attack_id, "aliases": aliases, + "description": obj.get("description", "")[:200], + } + return groups + + +def extract_techniques(bundle): + """Extract attack-pattern (technique) objects from STIX bundle.""" + techniques = {} + for obj in bundle.get("objects", []): + if obj.get("type") == "attack-pattern" and not obj.get("revoked", False): + ext_refs = obj.get("external_references", []) + attack_id = "" + for ref in ext_refs: + if ref.get("source_name") == "mitre-attack": + attack_id = ref.get("external_id", "") + break + if attack_id: + tactics = [p["phase_name"] for p in obj.get("kill_chain_phases", [])] + techniques[obj["id"]] = { + "id": attack_id, "name": obj.get("name", ""), + "tactics": tactics, "platforms": obj.get("x_mitre_platforms", []), + } + return techniques + + +def map_group_techniques(bundle, group_stix_id, techniques): + """Map techniques used by a specific group via relationship objects.""" + group_techniques = [] + for obj in bundle.get("objects", []): + if (obj.get("type") == "relationship" and + obj.get("relationship_type") == "uses" and + obj.get("source_ref") == group_stix_id and + obj.get("target_ref", "").startswith("attack-pattern--")): + tech_id = obj["target_ref"] + if tech_id in techniques: + group_techniques.append(techniques[tech_id]) + return group_techniques + + +def build_navigator_layer(group_name, group_techniques, color="#ff6666", score=100): + """Build ATT&CK Navigator JSON layer for a group's techniques.""" + layer = json.loads(json.dumps(NAVIGATOR_LAYER_TEMPLATE)) + layer["name"] = f"{group_name} - TTPs" + layer["description"] = f"ATT&CK techniques attributed to {group_name}" + for tech in group_techniques: + entry = { + "techniqueID": tech["id"], + "tactic": tech["tactics"][0] if tech["tactics"] else "", + "color": color, + "comment": f"Used by {group_name}", + "enabled": True, + "metadata": [], + "links": [], + "showSubtechniques": False, + "score": score, + } + layer["techniques"].append(entry) + return layer + + +def detection_gap_analysis(group_techniques, detection_rules): + """Compare group TTPs against existing detection rules to find gaps.""" + covered = set() + for rule in detection_rules: + tech_id = rule.get("technique_id", "") + if tech_id: + covered.add(tech_id) + gaps = [] + for tech in group_techniques: + if tech["id"] not in covered: + gaps.append({ + "technique_id": tech["id"], + "technique_name": tech["name"], + "tactics": tech["tactics"], + "status": "NO DETECTION", + }) + coverage_pct = (len(covered & {t["id"] for t in group_techniques}) / + len(group_techniques) * 100) if group_techniques else 0 + return gaps, round(coverage_pct, 1) + + +def tactic_heatmap(group_techniques): + """Generate tactic-level heatmap showing technique distribution.""" + tactic_counts = Counter() + for tech in group_techniques: + for tactic in tech["tactics"]: + tactic_counts[tactic] += 1 + return dict(tactic_counts.most_common()) + + +def compare_groups(group_a_techs, group_b_techs): + """Compare two groups' technique sets for overlap analysis.""" + set_a = {t["id"] for t in group_a_techs} + set_b = {t["id"] for t in group_b_techs} + overlap = set_a & set_b + only_a = set_a - set_b + only_b = set_b - set_a + jaccard = len(overlap) / len(set_a | set_b) if (set_a | set_b) else 0 + return { + "overlap_count": len(overlap), "overlap_ids": sorted(overlap), + "only_group_a": len(only_a), "only_group_b": len(only_b), + "jaccard_similarity": round(jaccard, 4), + } + + +def save_layer(layer, output_path): + """Save Navigator layer to JSON file.""" + with open(output_path, "w", encoding="utf-8") as f: + json.dump(layer, f, indent=2) + print(f"[+] Layer saved: {output_path}") + + +if __name__ == "__main__": + print("=" * 60) + print("APT Group Analysis Agent - MITRE ATT&CK Navigator") + print("TTP mapping, detection gap analysis, group comparison") + print("=" * 60) + + group_name = sys.argv[1] if len(sys.argv) > 1 else None + attack_file = sys.argv[2] if len(sys.argv) > 2 else None + + bundle = load_attack_data(attack_file) + if not bundle: + print("\n[!] Cannot load ATT&CK data. Provide STIX bundle path or install requests.") + print("[DEMO] Usage:") + print(" python agent.py APT29 enterprise-attack.json") + print(" python agent.py APT28 # downloads from GitHub") + sys.exit(1) + + groups = extract_groups(bundle) + techniques = extract_techniques(bundle) + print(f"[*] Loaded {len(groups)} groups, {len(techniques)} techniques") + + if not group_name: + print("\n--- Available APT Groups (sample) ---") + for gid, g in list(groups.items())[:20]: + print(f" {g['id']:8s} {g['name']:30s} aliases={g['aliases'][:3]}") + sys.exit(0) + + target_group = None + for gid, g in groups.items(): + if (g["name"].lower() == group_name.lower() or + g["id"].lower() == group_name.lower() or + group_name.lower() in [a.lower() for a in g["aliases"]]): + target_group = (gid, g) + break + + if not target_group: + print(f"[!] Group '{group_name}' not found") + sys.exit(1) + + gid, ginfo = target_group + print(f"\n[*] Group: {ginfo['name']} ({ginfo['id']})") + print(f" Aliases: {', '.join(ginfo['aliases'][:5])}") + + group_techs = map_group_techniques(bundle, gid, techniques) + print(f" Techniques: {len(group_techs)}") + + heatmap = tactic_heatmap(group_techs) + print("\n--- Tactic Heatmap ---") + for tactic, count in heatmap.items(): + bar = "#" * count + print(f" {tactic:35s} {count:3d} {bar}") + + layer = build_navigator_layer(ginfo["name"], group_techs) + out_file = f"{ginfo['name'].replace(' ', '_')}_layer.json" + save_layer(layer, out_file) + + sample_rules = [{"technique_id": t["id"]} for t in group_techs[:len(group_techs)//2]] + gaps, coverage = detection_gap_analysis(group_techs, sample_rules) + print(f"\n--- Detection Gap Analysis (demo: {coverage}% coverage) ---") + for gap in gaps[:10]: + print(f" [GAP] {gap['technique_id']:12s} {gap['technique_name']}") diff --git a/personas/_shared/skills/analyzing-azure-activity-logs-for-threats/LICENSE b/personas/_shared/skills/analyzing-azure-activity-logs-for-threats/LICENSE new file mode 100644 index 0000000..d885118 --- /dev/null +++ b/personas/_shared/skills/analyzing-azure-activity-logs-for-threats/LICENSE @@ -0,0 +1,201 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to the Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by the Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding any notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. Please do not remove or change + the license header comment from a contributed file except when + necessary. + + Copyright 2026 mukul975 + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/personas/_shared/skills/analyzing-azure-activity-logs-for-threats/SKILL.md b/personas/_shared/skills/analyzing-azure-activity-logs-for-threats/SKILL.md new file mode 100644 index 0000000..e578391 --- /dev/null +++ b/personas/_shared/skills/analyzing-azure-activity-logs-for-threats/SKILL.md @@ -0,0 +1,78 @@ +--- +name: analyzing-azure-activity-logs-for-threats +description: 'Queries Azure Monitor activity logs and sign-in logs via azure-monitor-query to detect suspicious administrative + operations, impossible travel, privilege escalation, and resource modifications. Builds KQL queries for threat hunting in + Azure environments. Use when investigating suspicious Azure tenant activity or building cloud SIEM detections. + + ' +domain: cybersecurity +subdomain: security-operations +tags: +- analyzing +- azure +- activity +- logs +version: '1.0' +author: mahipal +license: Apache-2.0 +nist_csf: +- DE.CM-01 +- RS.MA-01 +- GV.OV-01 +- DE.AE-02 +--- + +# Analyzing Azure Activity Logs for Threats + + +## When to Use + +- When investigating security incidents that require analyzing azure activity logs for threats +- When building detection rules or threat hunting queries for this domain +- When SOC analysts need structured procedures for this analysis type +- When validating security monitoring coverage for related attack techniques + +## Prerequisites + +- Familiarity with security operations concepts and tools +- Access to a test or lab environment for safe execution +- Python 3.8+ with required dependencies installed +- Appropriate authorization for any testing activities + +## Instructions + +Use azure-monitor-query to execute KQL queries against Azure Log Analytics workspaces, +detecting suspicious admin operations and sign-in anomalies. + +```python +from azure.identity import DefaultAzureCredential +from azure.monitor.query import LogsQueryClient +from datetime import timedelta + +credential = DefaultAzureCredential() +client = LogsQueryClient(credential) + +response = client.query_workspace( + workspace_id="WORKSPACE_ID", + query="AzureActivity | where OperationNameValue has 'MICROSOFT.AUTHORIZATION/ROLEASSIGNMENTS/WRITE' | take 10", + timespan=timedelta(hours=24), +) +``` + +Key detection queries: +1. Role assignment changes (privilege escalation) +2. Resource group and subscription modifications +3. Key vault secret access from new IPs +4. Network security group rule changes +5. Conditional access policy modifications + +## Examples + +```python +# Detect new Global Admin role assignments +query = ''' +AuditLogs +| where OperationName == "Add member to role" +| where TargetResources[0].modifiedProperties[0].newValue has "Global Administrator" +''' +``` diff --git a/personas/_shared/skills/analyzing-azure-activity-logs-for-threats/references/api-reference.md b/personas/_shared/skills/analyzing-azure-activity-logs-for-threats/references/api-reference.md new file mode 100644 index 0000000..69df600 --- /dev/null +++ b/personas/_shared/skills/analyzing-azure-activity-logs-for-threats/references/api-reference.md @@ -0,0 +1,54 @@ +# API Reference: Analyzing Azure Activity Logs for Threats + +## azure-monitor-query + +```python +from azure.identity import DefaultAzureCredential +from azure.monitor.query import LogsQueryClient, LogsQueryStatus +from datetime import timedelta + +credential = DefaultAzureCredential() +client = LogsQueryClient(credential) + +response = client.query_workspace( + workspace_id="WORKSPACE_ID", + query="AzureActivity | take 10", + timespan=timedelta(hours=24), +) +if response.status == LogsQueryStatus.SUCCESS: + for table in response.tables: + columns = [col.name for col in table.columns] + for row in table.rows: + print(dict(zip(columns, row))) +``` + +## Key Azure Log Tables + +| Table | Content | +|-------|---------| +| `AzureActivity` | Control plane operations (ARM) | +| `SigninLogs` | Azure AD sign-in events | +| `AuditLogs` | Azure AD audit trail | +| `AzureDiagnostics` | Resource diagnostics (Key Vault, NSG) | +| `SecurityAlert` | Defender for Cloud alerts | + +## Threat Detection KQL Patterns + +```kql +// Privilege escalation +AzureActivity | where OperationNameValue has "ROLEASSIGNMENTS/WRITE" + +// Impossible travel +SigninLogs | where ResultType == 0 +| extend Distance = geo_distance_2points(...) + +// Mass deletion +AzureActivity | where OperationNameValue endswith "/DELETE" +| summarize count() by Caller, bin(TimeGenerated, 1h) +``` + +### References + +- azure-monitor-query: https://pypi.org/project/azure-monitor-query/ +- KQL reference: https://learn.microsoft.com/en-us/azure/data-explorer/kusto/query/ +- Azure Activity Log schema: https://learn.microsoft.com/en-us/azure/azure-monitor/essentials/activity-log-schema diff --git a/personas/_shared/skills/analyzing-azure-activity-logs-for-threats/scripts/agent.py b/personas/_shared/skills/analyzing-azure-activity-logs-for-threats/scripts/agent.py new file mode 100644 index 0000000..6777f7b --- /dev/null +++ b/personas/_shared/skills/analyzing-azure-activity-logs-for-threats/scripts/agent.py @@ -0,0 +1,178 @@ +#!/usr/bin/env python3 +"""Agent for analyzing Azure activity logs for threat detection.""" + +import os +import json +import argparse +from datetime import datetime, timedelta + +from azure.identity import DefaultAzureCredential, ClientSecretCredential +from azure.monitor.query import LogsQueryClient, LogsQueryStatus + + +def get_credential(tenant_id=None, client_id=None, client_secret=None): + """Get Azure credential.""" + if client_id and client_secret and tenant_id: + return ClientSecretCredential(tenant_id, client_id, client_secret) + return DefaultAzureCredential() + + +def run_kql(credential, workspace_id, query, hours=24): + """Execute KQL query against Log Analytics workspace.""" + client = LogsQueryClient(credential) + response = client.query_workspace( + workspace_id, query, timespan=timedelta(hours=hours) + ) + rows = [] + if response.status == LogsQueryStatus.SUCCESS: + for table in response.tables: + columns = [col.name for col in table.columns] + for row in table.rows: + rows.append(dict(zip(columns, row))) + return rows + + +def detect_privilege_escalation(credential, workspace_id): + """Detect role assignment changes indicating privilege escalation.""" + query = """ + AzureActivity + | where OperationNameValue has_any ( + "MICROSOFT.AUTHORIZATION/ROLEASSIGNMENTS/WRITE", + "MICROSOFT.AUTHORIZATION/ROLEDEFINITIONS/WRITE" + ) + | where ActivityStatusValue == "Success" + | project TimeGenerated, Caller, CallerIpAddress, + OperationNameValue, ResourceGroup, Properties_d + | order by TimeGenerated desc + """ + return run_kql(credential, workspace_id, query) + + +def detect_nsg_changes(credential, workspace_id): + """Detect Network Security Group rule modifications.""" + query = """ + AzureActivity + | where OperationNameValue has_any ( + "MICROSOFT.NETWORK/NETWORKSECURITYGROUPS/SECURITYRULES/WRITE", + "MICROSOFT.NETWORK/NETWORKSECURITYGROUPS/SECURITYRULES/DELETE" + ) + | where ActivityStatusValue == "Success" + | project TimeGenerated, Caller, CallerIpAddress, + OperationNameValue, ResourceGroup + | order by TimeGenerated desc + """ + return run_kql(credential, workspace_id, query) + + +def detect_keyvault_access(credential, workspace_id): + """Detect Key Vault secret access from unusual sources.""" + query = """ + AzureDiagnostics + | where ResourceProvider == "MICROSOFT.KEYVAULT" + | where OperationName in ("SecretGet", "SecretList", "SecretSet") + | summarize AccessCount = count(), DistinctIPs = dcount(CallerIPAddress), + IPList = make_set(CallerIPAddress, 10) + by identity_claim_upn_s, OperationName, Resource + | where DistinctIPs > 2 or AccessCount > 50 + | order by AccessCount desc + """ + return run_kql(credential, workspace_id, query) + + +def detect_impossible_travel(credential, workspace_id): + """Detect sign-ins from geographically distant locations in short time.""" + query = """ + SigninLogs + | where ResultType == 0 + | project TimeGenerated, UserPrincipalName, IPAddress, + Lat = toreal(LocationDetails.geoCoordinates.latitude), + Lon = toreal(LocationDetails.geoCoordinates.longitude) + | sort by UserPrincipalName asc, TimeGenerated asc + | extend PrevLat = prev(Lat), PrevLon = prev(Lon), + PrevTime = prev(TimeGenerated), PrevUser = prev(UserPrincipalName) + | where UserPrincipalName == PrevUser + | extend TimeDiffMin = datetime_diff('minute', TimeGenerated, PrevTime) + | where TimeDiffMin < 60 and TimeDiffMin > 0 + | extend DistKm = geo_distance_2points(Lon, Lat, PrevLon, PrevLat) / 1000 + | where DistKm > 500 + | project TimeGenerated, UserPrincipalName, IPAddress, DistKm, TimeDiffMin + """ + return run_kql(credential, workspace_id, query) + + +def detect_resource_deletion(credential, workspace_id): + """Detect mass resource deletion events.""" + query = """ + AzureActivity + | where OperationNameValue endswith "/DELETE" + | where ActivityStatusValue == "Success" + | summarize DeleteCount = count(), Resources = make_set(Resource, 20) + by Caller, bin(TimeGenerated, 1h) + | where DeleteCount > 10 + | order by DeleteCount desc + """ + return run_kql(credential, workspace_id, query) + + +def detect_conditional_access_changes(credential, workspace_id): + """Detect modifications to Conditional Access policies.""" + query = """ + AuditLogs + | where OperationName has_any ( + "Update conditional access policy", + "Delete conditional access policy" + ) + | project TimeGenerated, InitiatedBy, OperationName, + TargetResources, Result + | order by TimeGenerated desc + """ + return run_kql(credential, workspace_id, query) + + +def main(): + parser = argparse.ArgumentParser(description="Azure Activity Log Threat Detection Agent") + parser.add_argument("--workspace-id", default=os.getenv("AZURE_WORKSPACE_ID")) + parser.add_argument("--tenant-id", default=os.getenv("AZURE_TENANT_ID")) + parser.add_argument("--client-id", default=os.getenv("AZURE_CLIENT_ID")) + parser.add_argument("--client-secret", default=os.getenv("AZURE_CLIENT_SECRET")) + parser.add_argument("--output", default="azure_threat_report.json") + parser.add_argument("--action", choices=[ + "privesc", "nsg", "keyvault", "travel", "deletion", "full_hunt" + ], default="full_hunt") + args = parser.parse_args() + + cred = get_credential(args.tenant_id, args.client_id, args.client_secret) + report = {"generated_at": datetime.utcnow().isoformat(), "findings": {}} + + if args.action in ("privesc", "full_hunt"): + results = detect_privilege_escalation(cred, args.workspace_id) + report["findings"]["privilege_escalation"] = results + print(f"[+] Privilege escalation events: {len(results)}") + + if args.action in ("nsg", "full_hunt"): + results = detect_nsg_changes(cred, args.workspace_id) + report["findings"]["nsg_changes"] = results + print(f"[+] NSG changes: {len(results)}") + + if args.action in ("keyvault", "full_hunt"): + results = detect_keyvault_access(cred, args.workspace_id) + report["findings"]["keyvault_anomalies"] = results + print(f"[+] Key Vault anomalies: {len(results)}") + + if args.action in ("travel", "full_hunt"): + results = detect_impossible_travel(cred, args.workspace_id) + report["findings"]["impossible_travel"] = results + print(f"[+] Impossible travel: {len(results)}") + + if args.action in ("deletion", "full_hunt"): + results = detect_resource_deletion(cred, args.workspace_id) + report["findings"]["mass_deletion"] = results + print(f"[+] Mass deletion events: {len(results)}") + + with open(args.output, "w") as f: + json.dump(report, f, indent=2, default=str) + print(f"[+] Report saved to {args.output}") + + +if __name__ == "__main__": + main() diff --git a/personas/_shared/skills/analyzing-bootkit-and-rootkit-samples/LICENSE b/personas/_shared/skills/analyzing-bootkit-and-rootkit-samples/LICENSE new file mode 100644 index 0000000..d885118 --- /dev/null +++ b/personas/_shared/skills/analyzing-bootkit-and-rootkit-samples/LICENSE @@ -0,0 +1,201 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to the Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by the Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding any notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. Please do not remove or change + the license header comment from a contributed file except when + necessary. + + Copyright 2026 mukul975 + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/personas/_shared/skills/analyzing-bootkit-and-rootkit-samples/SKILL.md b/personas/_shared/skills/analyzing-bootkit-and-rootkit-samples/SKILL.md new file mode 100644 index 0000000..d8f381e --- /dev/null +++ b/personas/_shared/skills/analyzing-bootkit-and-rootkit-samples/SKILL.md @@ -0,0 +1,354 @@ +--- +name: analyzing-bootkit-and-rootkit-samples +description: 'Analyzes bootkit and advanced rootkit malware that infects the Master Boot Record (MBR), Volume Boot Record + (VBR), or UEFI firmware to gain persistence below the operating system. Covers boot sector analysis, UEFI module inspection, + and anti-rootkit detection techniques. Activates for requests involving bootkit analysis, MBR malware investigation, UEFI + persistence analysis, or pre-OS malware detection. + + ' +domain: cybersecurity +subdomain: malware-analysis +tags: +- malware +- bootkit +- rootkit +- UEFI +- MBR-analysis +version: 1.0.0 +author: mahipal +license: Apache-2.0 +nist_csf: +- DE.AE-02 +- RS.AN-03 +- ID.RA-01 +- DE.CM-01 +--- + +# Analyzing Bootkit and Rootkit Samples + +## When to Use + +- A system shows signs of compromise that persist through OS reinstallation +- Antivirus and EDR are unable to detect malware despite clear evidence of compromise +- UEFI Secure Boot has been disabled or shows integrity violations +- Memory forensics reveals rootkit behavior (hidden processes, hooked system calls) +- Investigating nation-state level threats known to deploy bootkits (APT28, APT41, Equation Group) + +**Do not use** for standard user-mode malware; bootkits and rootkits operate at a fundamentally different level requiring specialized analysis techniques. + +## Prerequisites + +- Disk imaging tools (dd, FTK Imager) for acquiring MBR/VBR sectors +- UEFITool for UEFI firmware volume analysis and module extraction +- chipsec for hardware-level firmware security assessment +- Ghidra with x86 real-mode and 16-bit support for MBR code analysis +- Volatility 3 for kernel-level rootkit artifact detection +- Bootable Linux live USB for offline system analysis + +## Workflow + +### Step 1: Acquire Boot Sectors and Firmware + +Extract MBR, VBR, and UEFI firmware for offline analysis: + +```bash +# Acquire MBR (first 512 bytes of disk) +dd if=/dev/sda of=mbr.bin bs=512 count=1 + +# Acquire first track (usually contains bootkit code beyond MBR) +dd if=/dev/sda of=first_track.bin bs=512 count=63 + +# Acquire VBR (Volume Boot Record - first sector of partition) +dd if=/dev/sda1 of=vbr.bin bs=512 count=1 + +# Acquire UEFI System Partition +mkdir /mnt/efi +mount /dev/sda1 /mnt/efi +cp -r /mnt/efi/EFI /analysis/efi_backup/ + +# Dump UEFI firmware (requires chipsec or flashrom) +# Using chipsec: +python chipsec_util.py spi dump firmware.rom + +# Using flashrom: +flashrom -p internal -r firmware.rom + +# Verify firmware dump integrity +sha256sum firmware.rom +``` + +### Step 2: Analyze MBR/VBR for Bootkit Code + +Examine boot sector code for malicious modifications: + +```bash +# Disassemble MBR code (16-bit real mode) +ndisasm -b16 mbr.bin > mbr_disasm.txt + +# Compare MBR with known-good Windows MBR +# Standard Windows MBR begins with: EB 5A 90 (JMP 0x5C, NOP) +# Standard Windows 10 MBR: 33 C0 8E D0 BC 00 7C (XOR AX,AX; MOV SS,AX; MOV SP,7C00h) + +python3 << 'PYEOF' +with open("mbr.bin", "rb") as f: + mbr = f.read() + +# Check MBR signature (bytes 510-511 should be 0x55AA) +if mbr[510:512] == b'\x55\xAA': + print("[*] Valid MBR signature (0x55AA)") +else: + print("[!] Invalid MBR signature") + +# Check for known bootkit signatures +bootkit_sigs = { + b'\xE8\x00\x00\x5E\x81\xEE': "TDL4/Alureon bootkit", + b'\xFA\x33\xC0\x8E\xD0\xBC\x00\x7C\x8B\xF4\x50\x07': "Standard Windows MBR (clean)", + b'\xEB\x5A\x90\x4E\x54\x46\x53': "Standard NTFS VBR (clean)", +} + +for sig, name in bootkit_sigs.items(): + if sig in mbr: + print(f"[{'!' if 'clean' not in name else '*'}] Signature match: {name}") + +# Check partition table entries +print("\nPartition Table:") +for i in range(4): + offset = 446 + (i * 16) + entry = mbr[offset:offset+16] + if entry != b'\x00' * 16: + boot_flag = "Active" if entry[0] == 0x80 else "Inactive" + part_type = entry[4] + start_lba = int.from_bytes(entry[8:12], 'little') + size_lba = int.from_bytes(entry[12:16], 'little') + print(f" Partition {i+1}: Type=0x{part_type:02X} {boot_flag} Start=LBA {start_lba} Size={size_lba} sectors") +PYEOF +``` + +### Step 3: Analyze UEFI Firmware for Implants + +Inspect UEFI firmware volumes for unauthorized modules: + +```bash +# Extract UEFI firmware components with UEFITool +# GUI: Open firmware.rom -> Inspect firmware volumes +# CLI: +UEFIExtract firmware.rom all + +# List all DXE drivers (most common target for UEFI implants) +find firmware.rom.dump -name "*.efi" -exec file {} \; + +# Compare against known-good firmware module list +# Each UEFI module has a GUID - compare against vendor baseline + +# Verify Secure Boot configuration +python chipsec_main.py -m common.secureboot.variables + +# Check SPI flash write protection +python chipsec_main.py -m common.bios_wp + +# Check for known UEFI malware patterns +yara -r uefi_malware.yar firmware.rom +``` + +``` +Known UEFI Bootkit Detection Points: +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +LoJax (APT28): + - Modified SPI flash + - Added DXE driver that drops agent to Windows + - Persists through OS reinstall and disk replacement + +BlackLotus: + - Exploits CVE-2022-21894 to bypass Secure Boot + - Modifies EFI System Partition bootloader + - Installs kernel driver during boot + +CosmicStrand: + - Modifies CORE_DXE firmware module + - Hooks kernel initialization during boot + - Drops shellcode into Windows kernel memory + +MoonBounce: + - SPI flash implant in CORE_DXE module + - Modified GetVariable() function + - Deploys user-mode implant through boot chain + +ESPecter: + - Modifies Windows Boot Manager on ESP + - Patches winload.efi to disable DSE + - Loads unsigned kernel driver +``` + +### Step 4: Detect Kernel-Level Rootkit Behavior + +Analyze the running system for rootkit artifacts: + +```bash +# Memory forensics for rootkit detection +# SSDT hook detection +vol3 -f memory.dmp windows.ssdt | grep -v "ntoskrnl\|win32k" + +# Hidden processes (DKOM) +vol3 -f memory.dmp windows.psscan > psscan.txt +vol3 -f memory.dmp windows.pslist > pslist.txt +# Diff to find hidden processes + +# Kernel callback registration (rootkits register callbacks for filtering) +vol3 -f memory.dmp windows.callbacks + +# Driver analysis +vol3 -f memory.dmp windows.driverscan +vol3 -f memory.dmp windows.modules + +# Check for unsigned drivers +vol3 -f memory.dmp windows.driverscan | while read line; do + driver_path=$(echo "$line" | awk '{print $NF}') + if [ -f "$driver_path" ]; then + sigcheck -nobanner "$driver_path" 2>/dev/null | grep "Unsigned" + fi +done + +# IDT hook detection +vol3 -f memory.dmp windows.idt +``` + +### Step 5: Boot Process Integrity Verification + +Verify the integrity of the entire boot chain: + +```bash +# Verify Windows Boot Manager signature +sigcheck -a C:\Windows\Boot\EFI\bootmgfw.efi + +# Verify winload.efi +sigcheck -a C:\Windows\System32\winload.efi + +# Verify ntoskrnl.exe +sigcheck -a C:\Windows\System32\ntoskrnl.exe + +# Check Measured Boot logs (if TPM is available) +# Windows: BCDEdit /enum firmware +bcdedit /enum firmware + +# Verify Secure Boot state +Confirm-SecureBootUEFI # PowerShell cmdlet + +# Check boot configuration for tampering +bcdedit /v + +# Look for boot configuration changes +# testsigning: should be No +# nointegritychecks: should be No +# debug: should be No +bcdedit | findstr /i "testsigning nointegritychecks debug" +``` + +### Step 6: Document Bootkit/Rootkit Analysis + +Compile comprehensive analysis findings: + +``` +Analysis should document: +- Boot sector (MBR/VBR) integrity status with hex comparison +- UEFI firmware module inventory and integrity verification +- Secure Boot status and any bypass mechanisms detected +- Kernel-level hooks (SSDT, IDT, IRP, inline) identified +- Hidden processes, drivers, and files discovered +- Persistence mechanism (SPI flash, ESP, MBR, kernel driver) +- Boot chain integrity verification results +- Attribution to known bootkit families if possible +- Remediation steps (reflash firmware, rebuild MBR, replace hardware) +``` + +## Key Concepts + +| Term | Definition | +|------|------------| +| **Bootkit** | Malware that infects the boot process (MBR, VBR, UEFI) to execute before the operating system loads, gaining persistent low-level control | +| **MBR (Master Boot Record)** | First 512 bytes of a disk containing bootstrap code and partition table; MBR bootkits replace this code with malicious loaders | +| **UEFI (Unified Extensible Firmware Interface)** | Modern firmware interface replacing BIOS; UEFI bootkits implant malicious modules in firmware volumes or modify the ESP | +| **Secure Boot** | UEFI security feature verifying digital signatures of boot components; bootkits like BlackLotus exploit vulnerabilities to bypass it | +| **SPI Flash** | Flash memory chip storing UEFI firmware; advanced bootkits like LoJax and MoonBounce modify SPI flash for firmware-level persistence | +| **DKOM (Direct Kernel Object Manipulation)** | Rootkit technique modifying kernel structures to hide processes, files, and network connections without hooking functions | +| **Driver Signature Enforcement (DSE)** | Windows security feature requiring kernel drivers to be digitally signed; bootkits disable DSE during boot to load unsigned rootkit drivers | + +## Tools & Systems + +- **UEFITool**: Open-source UEFI firmware image editor and parser for inspecting firmware volumes, drivers, and modules +- **chipsec**: Intel hardware security assessment framework for verifying SPI flash protection, Secure Boot, and UEFI configuration +- **Volatility**: Memory forensics framework with SSDT, IDT, callback, and driver analysis plugins for kernel rootkit detection +- **GMER**: Windows rootkit detection tool scanning for SSDT hooks, IDT hooks, hidden processes, and modified kernel modules +- **Bootkits Analyzer**: Specialized tool for analyzing MBR/VBR code including disassembly and comparison against known-good baselines + +## Common Scenarios + +### Scenario: Investigating Persistent Compromise Surviving OS Reinstallation + +**Context**: An organization reimaged a compromised workstation, but the same C2 beaconing resumed within hours. Standard disk forensics finds no malware. UEFI bootkit is suspected. + +**Approach**: +1. Boot from a Linux live USB to avoid executing any compromised OS components +2. Dump the SPI flash firmware using chipsec or flashrom for offline analysis +3. Dump the MBR and VBR sectors with dd for boot sector analysis +4. Copy the EFI System Partition for bootloader integrity verification +5. Open the SPI dump in UEFITool and compare module GUIDs against vendor-provided firmware +6. Look for additional or modified DXE drivers that should not be present +7. Analyze any suspicious modules with Ghidra (x86_64 UEFI module format) +8. Verify Secure Boot configuration and check for exploit-based bypasses + +**Pitfalls**: +- Analyzing the system while the compromised OS is running (rootkit may hide from live analysis) +- Not checking SPI flash (only analyzing disk-based boot components misses firmware-level implants) +- Assuming Secure Boot prevents all bootkits (known bypasses exist, e.g., CVE-2022-21894) +- Not preserving the original firmware dump before reflashing (critical evidence for attribution) + +## Output Format + +``` +BOOTKIT / ROOTKIT ANALYSIS REPORT +==================================== +System: Dell OptiPlex 7090 (UEFI, TPM 2.0) +Firmware Version: 1.15.0 (Dell) +Secure Boot: ENABLED (but bypassed) +Capture Method: Linux Live USB + chipsec SPI dump + +MBR/VBR ANALYSIS +MBR Signature: Valid (0x55AA) +MBR Code: MATCHES standard Windows 10 MBR (clean) +VBR Code: MATCHES standard NTFS VBR (clean) + +UEFI FIRMWARE ANALYSIS +Total Modules: 287 +Vendor Expected: 285 +Extra Modules: 2 UNAUTHORIZED + [!] DXE Driver GUID: {ABCD1234-...} "SmmAccessDxe_mod" (MODIFIED) + Original Size: 12,288 bytes + Current Size: 45,056 bytes (32KB ADDED) + Entropy: 7.82 (HIGH - encrypted payload) + + [!] DXE Driver GUID: {EFGH5678-...} "UefiPayloadDxe" (NEW - not in vendor firmware) + Size: 28,672 bytes + Function: Drops persistence agent during boot + +BOOT CHAIN INTEGRITY +bootmgfw.efi: MODIFIED (hash mismatch, Secure Boot bypass via CVE-2022-21894) +winload.efi: MODIFIED (DSE disabled at load time) +ntoskrnl.exe: CLEAN (but unsigned driver loaded after boot) + +KERNEL ROOTKIT COMPONENTS +Driver: C:\Windows\System32\drivers\null_mod.sys (unsigned, hidden) +SSDT Hooks: 3 (NtQuerySystemInformation, NtQueryDirectoryFile, NtDeviceIoControlFile) +Hidden Processes: 2 (PID 6784: beacon.exe, PID 6812: keylog.exe) +Hidden Files: C:\Windows\System32\drivers\null_mod.sys + +ATTRIBUTION +Family: BlackLotus variant +Confidence: HIGH (CVE-2022-21894 exploit, ESP modification pattern matches) + +REMEDIATION +1. Reflash SPI firmware with clean vendor image via hardware programmer +2. Rebuild EFI System Partition from clean Windows installation media +3. Reinstall OS from verified media +4. Enable all firmware write protections +5. Update firmware to latest version (patches CVE-2022-21894) +``` diff --git a/personas/_shared/skills/analyzing-bootkit-and-rootkit-samples/references/api-reference.md b/personas/_shared/skills/analyzing-bootkit-and-rootkit-samples/references/api-reference.md new file mode 100644 index 0000000..4bd7ceb --- /dev/null +++ b/personas/_shared/skills/analyzing-bootkit-and-rootkit-samples/references/api-reference.md @@ -0,0 +1,97 @@ +# API Reference: Bootkit and Rootkit Analysis Tools + +## dd - Boot Sector Extraction + +### Syntax +```bash +dd if=/dev/sda of=mbr.bin bs=512 count=1 # MBR +dd if=/dev/sda of=first_track.bin bs=512 count=63 # First track +dd if=/dev/sda1 of=vbr.bin bs=512 count=1 # VBR +``` + +## ndisasm - 16-bit Disassembly + +### Syntax +```bash +ndisasm -b16 mbr.bin > mbr_disasm.txt +ndisasm -b16 -o 0x7C00 mbr.bin # Set origin to MBR load address +``` + +### Key Flags +| Flag | Description | +|------|-------------| +| `-b16` | 16-bit real-mode disassembly | +| `-b32` | 32-bit protected-mode | +| `-o` | Origin address offset | + +## UEFITool - Firmware Analysis + +### CLI Syntax +```bash +UEFIExtract firmware.rom all # Extract all modules +UEFIExtract firmware.rom body # Extract specific module body +``` + +### Output +Extracts firmware volumes into a directory tree with each DXE driver, PEI module, and option ROM as separate files identified by GUID. + +## chipsec - Hardware Security Assessment + +### Syntax +```bash +python chipsec_main.py -m common.secureboot.variables # Check Secure Boot +python chipsec_main.py -m common.bios_wp # SPI write protection +python chipsec_main.py -m common.spi_lock # SPI lock status +python chipsec_util.py spi dump firmware.rom # Dump SPI flash +``` + +### Key Modules +| Module | Purpose | +|--------|---------| +| `common.secureboot.variables` | Verify Secure Boot configuration | +| `common.bios_wp` | Check BIOS write protection | +| `common.spi_lock` | Verify SPI flash lock bits | +| `common.smm` | SMM protection verification | + +## Volatility 3 - Rootkit Detection Plugins + +### Syntax +```bash +vol3 -f memory.dmp +``` + +### Rootkit Detection Plugins +| Plugin | Purpose | +|--------|---------| +| `windows.ssdt` | System Service Descriptor Table hooks | +| `windows.callbacks` | Kernel callback registrations | +| `windows.driverscan` | Scan for driver objects | +| `windows.modules` | List loaded kernel modules | +| `windows.psscan` | Pool-tag scan for processes (finds hidden) | +| `windows.pslist` | Active process list (DKOM-affected) | +| `windows.idt` | Interrupt Descriptor Table hooks | + +### Output Format +``` +Offset Order Module Section Owner +------- ----- ------ ------- ----- +0x... 0 ntoskrnl.exe .text ntoskrnl.exe +0x... 73 UNKNOWN - rootkit.sys ← suspicious +``` + +## flashrom - SPI Flash Dumping + +### Syntax +```bash +flashrom -p internal -r firmware.rom # Read/dump +flashrom -p internal -w clean.rom # Write/reflash +flashrom -p internal --verify clean.rom # Verify flash contents +``` + +## YARA - Firmware Pattern Scanning + +### Syntax +```bash +yara -r uefi_malware.yar firmware.rom +yara -s -r rules.yar firmware.rom # Show matching strings +``` diff --git a/personas/_shared/skills/analyzing-bootkit-and-rootkit-samples/scripts/agent.py b/personas/_shared/skills/analyzing-bootkit-and-rootkit-samples/scripts/agent.py new file mode 100644 index 0000000..1f0b36a --- /dev/null +++ b/personas/_shared/skills/analyzing-bootkit-and-rootkit-samples/scripts/agent.py @@ -0,0 +1,199 @@ +#!/usr/bin/env python3 +"""Bootkit and rootkit analysis agent for MBR/VBR/UEFI inspection and rootkit detection.""" + +import struct +import hashlib +import os +import sys +import subprocess +import math +from collections import Counter + + +def read_mbr(disk_path_or_file): + """Read and parse the first 512 bytes (MBR) from a disk image or device.""" + with open(disk_path_or_file, "rb") as f: + mbr = f.read(512) + return mbr + + +def validate_mbr_signature(mbr_data): + """Check the MBR boot signature at bytes 510-511 (should be 0x55AA).""" + sig = mbr_data[510:512] + valid = sig == b"\x55\xAA" + return valid, sig.hex() + + +def parse_partition_table(mbr_data): + """Parse the four 16-byte partition table entries starting at offset 446.""" + partitions = [] + for i in range(4): + offset = 446 + (i * 16) + entry = mbr_data[offset:offset + 16] + if entry == b"\x00" * 16: + continue + boot_flag = entry[0] + part_type = entry[4] + start_lba = struct.unpack_from(" 6.5, + "suspicious_patterns": suspicious_patterns, + } + + +def run_volatility_rootkit_scan(memory_dump, plugin): + """Run a Volatility 3 plugin for rootkit detection via subprocess.""" + result = subprocess.run( + ["vol3", "-f", memory_dump, plugin], + capture_output=True, text=True, + timeout=120, + ) + return result.stdout, result.stderr, result.returncode + + +def detect_kernel_rootkit(memory_dump): + """Run multiple Volatility plugins to detect kernel-level rootkit artifacts.""" + plugins = [ + "windows.ssdt", + "windows.callbacks", + "windows.driverscan", + "windows.modules", + "windows.psscan", + "windows.pslist", + ] + results = {} + for plugin in plugins: + stdout, stderr, rc = run_volatility_rootkit_scan(memory_dump, plugin) + results[plugin] = {"output": stdout, "error": stderr, "return_code": rc} + return results + + +def compare_process_lists(pslist_output, psscan_output): + """Compare pslist and psscan output to find hidden processes (DKOM).""" + pslist_pids = set() + psscan_pids = set() + for line in pslist_output.splitlines(): + parts = line.split() + if len(parts) >= 2 and parts[1].isdigit(): + pslist_pids.add(int(parts[1])) + for line in psscan_output.splitlines(): + parts = line.split() + if len(parts) >= 2 and parts[1].isdigit(): + psscan_pids.add(int(parts[1])) + hidden = psscan_pids - pslist_pids + return hidden + + +if __name__ == "__main__": + print("=" * 60) + print("Bootkit & Rootkit Analysis Agent") + print("MBR/VBR inspection, UEFI firmware analysis, rootkit detection") + print("=" * 60) + + # Demo with a sample MBR file if available + demo_mbr = "mbr.bin" + if len(sys.argv) > 1: + demo_mbr = sys.argv[1] + + if os.path.exists(demo_mbr): + print(f"\n[*] Analyzing: {demo_mbr}") + mbr = read_mbr(demo_mbr) + valid, sig_hex = validate_mbr_signature(mbr) + print(f"[*] MBR Signature: 0x{sig_hex.upper()} ({'Valid' if valid else 'INVALID'})") + + partitions = parse_partition_table(mbr) + print(f"[*] Partition entries: {len(partitions)}") + for p in partitions: + active = "Active" if p["active"] else "Inactive" + print(f" Part {p['index']}: Type={p['type_id']} {active} " + f"Start=LBA {p['start_lba']} Size={p['size_mb']} MB") + + sigs = scan_bootkit_signatures(mbr) + for s in sigs: + tag = "[*]" if s["clean"] else "[!]" + print(f"{tag} Signature match: {s['signature']} at offset {s['offset']}") + + analysis = analyze_boot_code(mbr) + print(f"[*] Boot code entropy: {analysis['entropy']}" + f" ({'HIGH - possible encryption' if analysis['high_entropy'] else 'Normal'})") + print(f"[*] Boot code SHA-256: {analysis['sha256']}") + for pat in analysis["suspicious_patterns"]: + print(f"[!] {pat}") + else: + print(f"\n[DEMO] No MBR file provided. Usage: {sys.argv[0]} ") + print("[DEMO] Provide a 512-byte MBR dump or disk device for analysis.") + print("\n[*] Supported analysis:") + print(" - MBR/VBR signature validation and bootkit detection") + print(" - Partition table parsing and anomaly detection") + print(" - Boot code entropy and pattern analysis") + print(" - Volatility-based kernel rootkit detection (SSDT, callbacks, DKOM)") + print(" - UEFI firmware module inspection via chipsec subprocess") diff --git a/personas/_shared/skills/analyzing-browser-forensics-with-hindsight/LICENSE b/personas/_shared/skills/analyzing-browser-forensics-with-hindsight/LICENSE new file mode 100644 index 0000000..d885118 --- /dev/null +++ b/personas/_shared/skills/analyzing-browser-forensics-with-hindsight/LICENSE @@ -0,0 +1,201 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to the Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by the Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding any notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. Please do not remove or change + the license header comment from a contributed file except when + necessary. + + Copyright 2026 mukul975 + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/personas/_shared/skills/analyzing-browser-forensics-with-hindsight/SKILL.md b/personas/_shared/skills/analyzing-browser-forensics-with-hindsight/SKILL.md new file mode 100644 index 0000000..b6a9400 --- /dev/null +++ b/personas/_shared/skills/analyzing-browser-forensics-with-hindsight/SKILL.md @@ -0,0 +1,296 @@ +--- +name: analyzing-browser-forensics-with-hindsight +description: Analyze Chromium-based browser artifacts using Hindsight to extract browsing history, downloads, cookies, cached + content, autofill data, saved passwords, and browser extensions from Chrome, Edge, Brave, and Opera for forensic investigation. +domain: cybersecurity +subdomain: digital-forensics +tags: +- browser-forensics +- hindsight +- chrome-forensics +- chromium +- edge +- browsing-history +- cookies +- downloads +- cache +- web-artifacts +version: '1.0' +author: mahipal +license: Apache-2.0 +nist_csf: +- RS.AN-01 +- RS.AN-03 +- DE.AE-02 +- RS.MA-01 +--- + +# Analyzing Browser Forensics with Hindsight + +## Overview + +Hindsight is an open-source browser forensics tool designed to parse artifacts from Google Chrome and other Chromium-based browsers (Microsoft Edge, Brave, Opera, Vivaldi). It extracts and correlates data from multiple browser database files to create a unified timeline of web activity. Hindsight can parse URLs, download history, cache records, bookmarks, autofill records, saved passwords, preferences, browser extensions, HTTP cookies, Local Storage (HTML5 cookies), login data, and session/tab information. The tool produces chronological timelines in multiple output formats (XLSX, JSON, SQLite) that enable investigators to reconstruct user web activity for incident response, insider threat investigations, and criminal cases. + + +## When to Use + +- When investigating security incidents that require analyzing browser forensics with hindsight +- When building detection rules or threat hunting queries for this domain +- When SOC analysts need structured procedures for this analysis type +- When validating security monitoring coverage for related attack techniques + +## Prerequisites + +- Python 3.8+ with Hindsight installed (`pip install pyhindsight`) +- Access to browser profile directories from forensic image +- Browser profile data (not encrypted with OS-level encryption) +- Timeline Explorer or spreadsheet application for analysis + +## Browser Profile Locations + +| Browser | Windows Profile Path | +|---------|---------------------| +| Chrome | %LOCALAPPDATA%\Google\Chrome\User Data\Default\ | +| Edge | %LOCALAPPDATA%\Microsoft\Edge\User Data\Default\ | +| Brave | %LOCALAPPDATA%\BraveSoftware\Brave-Browser\User Data\Default\ | +| Opera | %APPDATA%\Opera Software\Opera Stable\ | +| Vivaldi | %LOCALAPPDATA%\Vivaldi\User Data\Default\ | +| Chrome (macOS) | ~/Library/Application Support/Google/Chrome/Default/ | +| Chrome (Linux) | ~/.config/google-chrome/Default/ | + +## Key Artifact Files + +| File | Contents | +|------|----------| +| History | URL visits, downloads, keyword searches | +| Cookies | HTTP cookies with domain, expiry, values | +| Web Data | Autofill entries, saved credit cards | +| Login Data | Saved usernames/passwords (encrypted) | +| Bookmarks | JSON bookmark tree | +| Preferences | Browser configuration and extensions | +| Local Storage/ | HTML5 Local Storage per domain | +| Session Storage/ | Session-specific storage per domain | +| Network Action Predictor | Previously typed URLs | +| Shortcuts | Omnibox shortcuts and predictions | +| Top Sites | Frequently visited sites | + +## Running Hindsight + +### Command Line + +```bash +# Basic analysis of a Chrome profile +hindsight.exe -i "C:\Evidence\Users\suspect\AppData\Local\Google\Chrome\User Data\Default" -o C:\Output\chrome_analysis + +# Specify browser type +hindsight.exe -i "/path/to/profile" -o /output/analysis -b Chrome + +# JSON output format +hindsight.exe -i "C:\Evidence\Chrome\Default" -o C:\Output\chrome --format jsonl + +# With cache parsing (slower but more complete) +hindsight.exe -i "C:\Evidence\Chrome\Default" -o C:\Output\chrome --cache +``` + +### Web UI + +```bash +# Start Hindsight web interface +hindsight_gui.exe +# Navigate to http://localhost:8080 +# Upload or point to browser profile directory +# Configure output format and analysis options +# Generate and download report +``` + +## Artifact Analysis Details + +### URL History and Visits + +```sql +-- Chrome History database schema (key tables) +-- urls table: id, url, title, visit_count, typed_count, last_visit_time +-- visits table: id, url, visit_time, from_visit, transition, segment_id + +-- Timestamps are Chrome/WebKit format: microseconds since 1601-01-01 +-- Convert: datetime((visit_time/1000000)-11644473600, 'unixepoch') +``` + +### Download History + +```sql +-- downloads table: id, current_path, target_path, start_time, end_time, +-- received_bytes, total_bytes, state, danger_type, interrupt_reason, +-- url, referrer, tab_url, mime_type, original_mime_type +``` + +### Cookie Analysis + +```sql +-- cookies table: creation_utc, host_key, name, value, encrypted_value, +-- path, expires_utc, is_secure, is_httponly, last_access_utc, +-- has_expires, is_persistent, priority, samesite +``` + +## Python Analysis Script + +```python +import sqlite3 +import os +import json +import sys +from datetime import datetime, timedelta + + +CHROME_EPOCH = datetime(1601, 1, 1) + + +def chrome_time_to_datetime(chrome_ts: int): + """Convert Chrome timestamp to datetime.""" + if chrome_ts == 0: + return None + try: + return CHROME_EPOCH + timedelta(microseconds=chrome_ts) + except (OverflowError, OSError): + return None + + +def analyze_chrome_history(profile_path: str, output_dir: str) -> dict: + """Analyze Chrome History database for forensic evidence.""" + history_db = os.path.join(profile_path, "History") + if not os.path.exists(history_db): + return {"error": "History database not found"} + + os.makedirs(output_dir, exist_ok=True) + conn = sqlite3.connect(f"file:{history_db}?mode=ro", uri=True) + + # URL visits with timestamps + cursor = conn.cursor() + cursor.execute(""" + SELECT u.url, u.title, v.visit_time, u.visit_count, + v.transition & 0xFF as transition_type + FROM visits v JOIN urls u ON v.url = u.id + ORDER BY v.visit_time DESC LIMIT 5000 + """) + visits = [{ + "url": r[0], "title": r[1], + "visit_time": str(chrome_time_to_datetime(r[2])), + "total_visits": r[3], "transition": r[4] + } for r in cursor.fetchall()] + + # Downloads + cursor.execute(""" + SELECT target_path, tab_url, start_time, end_time, + received_bytes, total_bytes, mime_type, state + FROM downloads ORDER BY start_time DESC LIMIT 1000 + """) + downloads = [{ + "path": r[0], "source_url": r[1], + "start_time": str(chrome_time_to_datetime(r[2])), + "end_time": str(chrome_time_to_datetime(r[3])), + "received_bytes": r[4], "total_bytes": r[5], + "mime_type": r[6], "state": r[7] + } for r in cursor.fetchall()] + + # Keyword searches + cursor.execute(""" + SELECT k.term, u.url, k.url_id + FROM keyword_search_terms k JOIN urls u ON k.url_id = u.id + ORDER BY u.last_visit_time DESC LIMIT 1000 + """) + searches = [{"term": r[0], "url": r[1]} for r in cursor.fetchall()] + + conn.close() + + report = { + "analysis_timestamp": datetime.now().isoformat(), + "profile_path": profile_path, + "total_visits": len(visits), + "total_downloads": len(downloads), + "total_searches": len(searches), + "visits": visits, + "downloads": downloads, + "searches": searches + } + + report_path = os.path.join(output_dir, "browser_forensics.json") + with open(report_path, "w") as f: + json.dump(report, f, indent=2) + + return report + + +def main(): + if len(sys.argv) < 3: + print("Usage: python process.py ") + sys.exit(1) + analyze_chrome_history(sys.argv[1], sys.argv[2]) + + +if __name__ == "__main__": + main() +``` + +## References + +- Hindsight GitHub: https://github.com/obsidianforensics/hindsight +- Chrome Forensics Guide: https://allenace.medium.com/hindsight-chrome-forensics-made-simple-425db99fa5ed +- Browser Forensics Tools: https://www.cyberforensicacademy.com/blog/browser-forensics-tools-how-to-extract-user-activity +- Chromium Source (History): https://source.chromium.org/chromium/chromium/src/+/main:components/history/ + +## Example Output + +```text +$ python hindsight.py -i /evidence/chrome-profile -o /analysis/hindsight_output + +Hindsight v2024.01 - Chrome/Chromium Browser Forensic Analysis +================================================================ + +Profile: /evidence/chrome-profile (Chrome 120.0.6099.130) +OS: Windows 10 + +[+] Parsing History database... + URL records: 12,456 + Download records: 234 + Search terms: 567 + +[+] Parsing Cookies database... + Cookie records: 8,923 + Encrypted cookies: 6,712 + +[+] Parsing Web Data (Autofill)... + Autofill entries: 1,234 + Credit card entries: 2 (encrypted) + +[+] Parsing Login Data... + Saved credentials: 45 (encrypted) + +[+] Parsing Bookmarks... + Bookmark entries: 189 + +--- Browsing History (Last 10 Entries) --- +Timestamp (UTC) | URL | Title | Visit Count +2024-01-15 14:32:05.123 | https://mail.corporate.com/inbox | Corporate Mail | 45 +2024-01-15 14:33:12.456 | https://drive.google.com/file/d/1aBcDe... | Q4_Financial_Report.xlsx | 1 +2024-01-15 14:35:44.789 | https://mega.nz/folder/xYz123 | MEGA - Secure Cloud | 3 +2024-01-15 14:36:01.234 | https://mega.nz/folder/xYz123#upload | MEGA - Upload | 8 +2024-01-15 14:42:15.567 | https://pastebin.com/raw/kL9mN2pQ | Pastebin (raw) | 1 +2024-01-15 15:01:33.890 | https://192.168.1.50:8443/admin | Admin Panel | 12 +2024-01-15 15:15:22.111 | https://transfer.sh/upload | transfer.sh | 2 +2024-01-15 15:30:45.222 | https://vpn-gateway.corporate.com | VPN Login | 5 +2024-01-15 16:00:00.333 | https://whatismyipaddress.com | What Is My IP | 1 +2024-01-15 16:05:12.444 | https://protonmail.com/inbox | ProtonMail | 3 + +--- Downloads (Suspicious) --- +Timestamp (UTC) | Filename | URL Source | Size +2024-01-15 14:33:15.000 | Q4_Financial_Report.xlsm | https://phish-domain.com/docs/report | 245 KB +2024-01-15 14:34:02.000 | update_client.exe | https://cdn.evil-updates.com/client.exe | 1.2 MB + +--- Cookies (Session Tokens) --- +Domain | Name | Expires | Secure | HttpOnly +.corporate.com | SESSION_ID | 2024-01-16 14:32 | Yes | Yes +.mega.nz | session | Session | Yes | Yes +.protonmail.com | AUTH-TOKEN | 2024-02-15 00:00 | Yes | Yes + +Report saved to: /analysis/hindsight_output/Hindsight_Report.xlsx +``` diff --git a/personas/_shared/skills/analyzing-browser-forensics-with-hindsight/assets/template.md b/personas/_shared/skills/analyzing-browser-forensics-with-hindsight/assets/template.md new file mode 100644 index 0000000..e00e520 --- /dev/null +++ b/personas/_shared/skills/analyzing-browser-forensics-with-hindsight/assets/template.md @@ -0,0 +1,22 @@ +# Browser Forensics Report +## Case Info +| Field | Value | +|-------|-------| +| Case Number | | +| Browser | | +| Profile Path | | +## Activity Summary +| Metric | Count | +|--------|-------| +| URL Visits | | +| Downloads | | +| Saved Passwords | | +| Cookies | | +## Notable URLs +| Timestamp | URL | Title | +|-----------|-----|-------| +| | | | +## Downloads +| Timestamp | File | Source URL | Size | +|-----------|------|-----------|------| +| | | | | diff --git a/personas/_shared/skills/analyzing-browser-forensics-with-hindsight/references/api-reference.md b/personas/_shared/skills/analyzing-browser-forensics-with-hindsight/references/api-reference.md new file mode 100644 index 0000000..85fe5f6 --- /dev/null +++ b/personas/_shared/skills/analyzing-browser-forensics-with-hindsight/references/api-reference.md @@ -0,0 +1,92 @@ +# API Reference: Browser Forensics with Hindsight + +## Hindsight CLI + +### Syntax +```bash +hindsight.py -i # Analyze Chrome profile +hindsight.py -i -o # Save results +hindsight.py -i -f xlsx # Export as Excel +hindsight.py -i -f sqlite # Export as SQLite +hindsight.py -i -b # Specify browser type +``` + +### Browser Types +| Flag | Browser | +|------|---------| +| `Chrome` | Google Chrome | +| `Edge` | Microsoft Edge (Chromium) | +| `Brave` | Brave Browser | +| `Opera` | Opera (Chromium) | + +### Output Artifacts +| Table | Description | +|-------|-------------| +| `urls` | Browsing history with visit counts | +| `downloads` | File downloads with source URLs | +| `cookies` | Cookie values, domains, expiry | +| `autofill` | Form autofill entries | +| `bookmarks` | Saved bookmarks | +| `preferences` | Browser configuration | +| `local_storage` | Site local storage data | +| `login_data` | Saved credential metadata | +| `extensions` | Installed extensions with permissions | + +## Chrome SQLite Databases + +### History Database +```sql +-- Browsing history +SELECT u.url, u.title, v.visit_time, v.transition +FROM visits v JOIN urls u ON v.url = u.id +ORDER BY v.visit_time DESC; + +-- Downloads +SELECT target_path, tab_url, total_bytes, start_time, danger_type, mime_type +FROM downloads ORDER BY start_time DESC; +``` + +### Cookies Database +```sql +SELECT host_key, name, value, creation_utc, expires_utc, is_secure, is_httponly +FROM cookies ORDER BY creation_utc DESC; +``` + +### Web Data Database (Autofill) +```sql +SELECT name, value, count, date_created, date_last_used +FROM autofill ORDER BY date_last_used DESC; +``` + +## Chrome Timestamp Conversion + +### Format +Microseconds since January 1, 1601 (Windows FILETIME base) + +### Python Conversion +```python +import datetime +def chrome_to_datetime(chrome_time): + epoch = datetime.datetime(1601, 1, 1) + return epoch + datetime.timedelta(microseconds=chrome_time) +``` + +## Browser Profile Paths + +| OS | Browser | Default Path | +|----|---------|-------------| +| Windows | Chrome | `%LOCALAPPDATA%\Google\Chrome\User Data\Default` | +| Windows | Edge | `%LOCALAPPDATA%\Microsoft\Edge\User Data\Default` | +| Linux | Chrome | `~/.config/google-chrome/Default` | +| macOS | Chrome | `~/Library/Application Support/Google/Chrome/Default` | + +## Transition Types (visit_transition & 0xFF) +| Value | Type | Description | +|-------|------|-------------| +| 0 | LINK | Clicked a link | +| 1 | TYPED | Typed URL in address bar | +| 2 | AUTO_BOOKMARK | Via bookmark | +| 3 | AUTO_SUBFRAME | Subframe navigation | +| 5 | GENERATED | Generated (e.g., search) | +| 7 | FORM_SUBMIT | Form submission | +| 8 | RELOAD | Page reload | diff --git a/personas/_shared/skills/analyzing-browser-forensics-with-hindsight/references/standards.md b/personas/_shared/skills/analyzing-browser-forensics-with-hindsight/references/standards.md new file mode 100644 index 0000000..c54b46c --- /dev/null +++ b/personas/_shared/skills/analyzing-browser-forensics-with-hindsight/references/standards.md @@ -0,0 +1,15 @@ +# Standards - Browser Forensics with Hindsight +## Tools +- Hindsight: https://github.com/obsidianforensics/hindsight +- DB Browser for SQLite: Chrome database inspection +- ChromeCacheView (NirSoft): Cache analysis +## Browser Databases +- History: URL visits, downloads, keyword searches +- Cookies: HTTP cookies per domain +- Web Data: Autofill, credit cards +- Login Data: Saved credentials (encrypted) +- Bookmarks: JSON bookmark tree +## Timestamp Formats +- Chrome/WebKit: microseconds since 1601-01-01 UTC +- Firefox/Mozilla: microseconds since Unix epoch +- Safari/Mac: seconds since 2001-01-01 UTC diff --git a/personas/_shared/skills/analyzing-browser-forensics-with-hindsight/references/workflows.md b/personas/_shared/skills/analyzing-browser-forensics-with-hindsight/references/workflows.md new file mode 100644 index 0000000..793f637 --- /dev/null +++ b/personas/_shared/skills/analyzing-browser-forensics-with-hindsight/references/workflows.md @@ -0,0 +1,19 @@ +# Workflows - Browser Forensics +## Workflow: Chrome Profile Analysis +``` +Locate browser profile directory + | +Run Hindsight against profile path + | +Review generated timeline (XLSX/JSON) + | +Analyze URL history for suspicious sites + | +Check downloads for malware/exfiltrated data + | +Review cookies for session hijacking evidence + | +Examine autofill and saved credentials + | +Correlate browser activity with system timeline +``` diff --git a/personas/_shared/skills/analyzing-browser-forensics-with-hindsight/scripts/agent.py b/personas/_shared/skills/analyzing-browser-forensics-with-hindsight/scripts/agent.py new file mode 100644 index 0000000..b3efbe7 --- /dev/null +++ b/personas/_shared/skills/analyzing-browser-forensics-with-hindsight/scripts/agent.py @@ -0,0 +1,254 @@ +#!/usr/bin/env python3 +"""Browser forensics analysis agent using Hindsight concepts. + +Parses Chromium-based browser artifacts (Chrome, Edge, Brave) including +history, downloads, cookies, autofill, and extensions from SQLite databases. +""" + +import os +import sys +import json +import sqlite3 +import datetime + + +def chrome_time_to_datetime(chrome_time): + """Convert Chrome timestamp (microseconds since 1601-01-01) to datetime.""" + if not chrome_time or chrome_time == 0: + return None + try: + epoch = datetime.datetime(1601, 1, 1) + delta = datetime.timedelta(microseconds=chrome_time) + return (epoch + delta).isoformat() + "Z" + except (OverflowError, OSError): + return None + + +def find_browser_profiles(base_path=None): + """Locate Chromium-based browser profile directories.""" + if base_path and os.path.isdir(base_path): + return [base_path] + profiles = [] + home = os.path.expanduser("~") + candidates = [ + os.path.join(home, "AppData", "Local", "Google", "Chrome", "User Data", "Default"), + os.path.join(home, "AppData", "Local", "Microsoft", "Edge", "User Data", "Default"), + os.path.join(home, "AppData", "Local", "BraveSoftware", "Brave-Browser", "User Data", "Default"), + os.path.join(home, ".config", "google-chrome", "Default"), + os.path.join(home, ".config", "chromium", "Default"), + os.path.join(home, ".config", "microsoft-edge", "Default"), + ] + for path in candidates: + if os.path.isdir(path): + profiles.append(path) + return profiles + + +def parse_history(profile_path): + """Parse browsing history from History SQLite database.""" + db_path = os.path.join(profile_path, "History") + if not os.path.exists(db_path): + return [] + entries = [] + try: + conn = sqlite3.connect(f"file:{db_path}?mode=ro", uri=True) + cursor = conn.cursor() + cursor.execute(""" + SELECT u.url, u.title, v.visit_time, v.transition, u.visit_count + FROM visits v JOIN urls u ON v.url = u.id + ORDER BY v.visit_time DESC LIMIT 5000 + """) + for url, title, visit_time, transition, count in cursor.fetchall(): + entries.append({ + "url": url, "title": title or "", + "visit_time": chrome_time_to_datetime(visit_time), + "transition": transition & 0xFF, + "visit_count": count, + }) + conn.close() + except sqlite3.Error as e: + entries.append({"error": str(e)}) + return entries + + +def parse_downloads(profile_path): + """Parse download history from History database.""" + db_path = os.path.join(profile_path, "History") + if not os.path.exists(db_path): + return [] + downloads = [] + try: + conn = sqlite3.connect(f"file:{db_path}?mode=ro", uri=True) + cursor = conn.cursor() + cursor.execute(""" + SELECT target_path, tab_url, total_bytes, start_time, end_time, + danger_type, interrupt_reason, mime_type + FROM downloads ORDER BY start_time DESC LIMIT 1000 + """) + for row in cursor.fetchall(): + downloads.append({ + "target_path": row[0], "source_url": row[1], + "total_bytes": row[2], + "start_time": chrome_time_to_datetime(row[3]), + "end_time": chrome_time_to_datetime(row[4]), + "danger_type": row[5], "interrupt_reason": row[6], + "mime_type": row[7], + }) + conn.close() + except sqlite3.Error as e: + downloads.append({"error": str(e)}) + return downloads + + +def parse_cookies(profile_path): + """Parse cookies from Cookies database.""" + db_path = os.path.join(profile_path, "Cookies") + if not os.path.exists(db_path): + db_path = os.path.join(profile_path, "Network", "Cookies") + if not os.path.exists(db_path): + return [] + cookies = [] + try: + conn = sqlite3.connect(f"file:{db_path}?mode=ro", uri=True) + cursor = conn.cursor() + cursor.execute(""" + SELECT host_key, name, path, creation_utc, expires_utc, + is_secure, is_httponly, samesite + FROM cookies ORDER BY creation_utc DESC LIMIT 2000 + """) + for row in cursor.fetchall(): + cookies.append({ + "host": row[0], "name": row[1], "path": row[2], + "created": chrome_time_to_datetime(row[3]), + "expires": chrome_time_to_datetime(row[4]), + "secure": bool(row[5]), "httponly": bool(row[6]), + "samesite": row[7], + }) + conn.close() + except sqlite3.Error as e: + cookies.append({"error": str(e)}) + return cookies + + +def parse_autofill(profile_path): + """Parse autofill data from Web Data database.""" + db_path = os.path.join(profile_path, "Web Data") + if not os.path.exists(db_path): + return [] + entries = [] + try: + conn = sqlite3.connect(f"file:{db_path}?mode=ro", uri=True) + cursor = conn.cursor() + cursor.execute(""" + SELECT name, value, count, date_created, date_last_used + FROM autofill ORDER BY date_last_used DESC LIMIT 500 + """) + for row in cursor.fetchall(): + entries.append({ + "field_name": row[0], "value": row[1][:50] + "..." if len(row[1]) > 50 else row[1], + "usage_count": row[2], + "created": chrome_time_to_datetime(row[3] * 1000000 if row[3] else 0), + "last_used": chrome_time_to_datetime(row[4] * 1000000 if row[4] else 0), + }) + conn.close() + except sqlite3.Error as e: + entries.append({"error": str(e)}) + return entries + + +def parse_extensions(profile_path): + """Parse installed browser extensions.""" + ext_dir = os.path.join(profile_path, "Extensions") + extensions = [] + if not os.path.isdir(ext_dir): + return extensions + for ext_id in os.listdir(ext_dir): + ext_path = os.path.join(ext_dir, ext_id) + if os.path.isdir(ext_path): + versions = sorted(os.listdir(ext_path)) + manifest_path = os.path.join(ext_path, versions[-1], "manifest.json") if versions else None + name = ext_id + if manifest_path and os.path.exists(manifest_path): + try: + with open(manifest_path, "r", encoding="utf-8") as f: + manifest = json.load(f) + name = manifest.get("name", ext_id) + extensions.append({ + "id": ext_id, "name": name, + "version": manifest.get("version", "?"), + "permissions": manifest.get("permissions", [])[:10], + }) + except (json.JSONDecodeError, IOError): + extensions.append({"id": ext_id, "name": name, "version": "unknown"}) + return extensions + + +def detect_suspicious_activity(history, downloads): + """Flag suspicious browsing and download patterns.""" + findings = [] + suspicious_domains = ["pastebin.com", "ngrok.io", "raw.githubusercontent.com", + "transfer.sh", "file.io", "temp.sh", "anonfiles.com"] + for entry in history: + url = entry.get("url", "").lower() + for domain in suspicious_domains: + if domain in url: + findings.append({ + "type": "suspicious_url", "url": entry["url"], + "domain": domain, "time": entry.get("visit_time"), + }) + dangerous_mimes = ["application/x-msdownload", "application/x-msdos-program", + "application/x-executable", "application/vnd.ms-excel.sheet.macroEnabled"] + for dl in downloads: + if dl.get("danger_type", 0) > 0: + findings.append({ + "type": "dangerous_download", "path": dl.get("target_path"), + "source": dl.get("source_url"), "danger_type": dl.get("danger_type"), + }) + if dl.get("mime_type", "") in dangerous_mimes: + findings.append({ + "type": "suspicious_mime", "mime": dl.get("mime_type"), + "path": dl.get("target_path"), + }) + return findings + + +if __name__ == "__main__": + print("=" * 60) + print("Browser Forensics Analysis Agent") + print("Chromium history, downloads, cookies, extensions") + print("=" * 60) + + target = sys.argv[1] if len(sys.argv) > 1 else None + profiles = find_browser_profiles(target) + + if not profiles: + print("\n[!] No browser profiles found.") + print("[DEMO] Usage: python agent.py ") + print(" e.g. python agent.py ~/AppData/Local/Google/Chrome/User\\ Data/Default") + sys.exit(0) + + for profile in profiles: + print(f"\n[*] Profile: {profile}") + + history = parse_history(profile) + print(f" History entries: {len(history)}") + for h in history[:5]: + print(f" {h.get('visit_time', '?')} | {h.get('title', '')[:50]} | {h.get('url', '')[:60]}") + + downloads = parse_downloads(profile) + print(f" Downloads: {len(downloads)}") + for d in downloads[:5]: + print(f" {d.get('start_time', '?')} | {d.get('mime_type', '?')} | {os.path.basename(d.get('target_path', ''))}") + + cookies = parse_cookies(profile) + print(f" Cookies: {len(cookies)}") + + extensions = parse_extensions(profile) + print(f" Extensions: {len(extensions)}") + for ext in extensions[:5]: + print(f" {ext.get('name', '?')} v{ext.get('version', '?')} [{ext.get('id', '')[:20]}]") + + findings = detect_suspicious_activity(history, downloads) + print(f"\n --- Suspicious Activity: {len(findings)} findings ---") + for f in findings[:10]: + print(f" [{f['type']}] {f.get('url', f.get('path', ''))}") diff --git a/personas/_shared/skills/analyzing-browser-forensics-with-hindsight/scripts/process.py b/personas/_shared/skills/analyzing-browser-forensics-with-hindsight/scripts/process.py new file mode 100644 index 0000000..b204e2e --- /dev/null +++ b/personas/_shared/skills/analyzing-browser-forensics-with-hindsight/scripts/process.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python3 +"""Browser Forensics Analyzer - Parses Chrome History SQLite for investigation.""" +import sqlite3, json, os, sys +from datetime import datetime, timedelta + +CHROME_EPOCH = datetime(1601, 1, 1) + +def chrome_ts(ts): + if not ts: return None + try: return str(CHROME_EPOCH + timedelta(microseconds=ts)) + except: return None + +def analyze_chrome(profile: str, output_dir: str) -> str: + os.makedirs(output_dir, exist_ok=True) + history_db = os.path.join(profile, "History") + conn = sqlite3.connect(f"file:{history_db}?mode=ro", uri=True) + c = conn.cursor() + c.execute("SELECT u.url, u.title, v.visit_time, u.visit_count FROM visits v JOIN urls u ON v.url=u.id ORDER BY v.visit_time DESC LIMIT 2000") + visits = [{"url": r[0], "title": r[1], "time": chrome_ts(r[2]), "count": r[3]} for r in c.fetchall()] + c.execute("SELECT target_path, tab_url, start_time, total_bytes, mime_type FROM downloads ORDER BY start_time DESC LIMIT 500") + downloads = [{"path": r[0], "url": r[1], "time": chrome_ts(r[2]), "size": r[3], "mime": r[4]} for r in c.fetchall()] + conn.close() + report = {"visits": len(visits), "downloads": len(downloads), "visit_data": visits, "download_data": downloads} + out = os.path.join(output_dir, "browser_forensics.json") + with open(out, "w") as f: json.dump(report, f, indent=2) + print(f"[*] Visits: {len(visits)}, Downloads: {len(downloads)}") + return out + +if __name__ == "__main__": + if len(sys.argv) < 3: print("Usage: process.py "); sys.exit(1) + analyze_chrome(sys.argv[1], sys.argv[2]) diff --git a/personas/_shared/skills/analyzing-campaign-attribution-evidence/LICENSE b/personas/_shared/skills/analyzing-campaign-attribution-evidence/LICENSE new file mode 100644 index 0000000..d885118 --- /dev/null +++ b/personas/_shared/skills/analyzing-campaign-attribution-evidence/LICENSE @@ -0,0 +1,201 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to the Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by the Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding any notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. Please do not remove or change + the license header comment from a contributed file except when + necessary. + + Copyright 2026 mukul975 + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/personas/_shared/skills/analyzing-campaign-attribution-evidence/SKILL.md b/personas/_shared/skills/analyzing-campaign-attribution-evidence/SKILL.md new file mode 100644 index 0000000..da9756c --- /dev/null +++ b/personas/_shared/skills/analyzing-campaign-attribution-evidence/SKILL.md @@ -0,0 +1,237 @@ +--- +name: analyzing-campaign-attribution-evidence +description: Campaign attribution analysis involves systematically evaluating evidence to determine which threat actor or + group is responsible for a cyber operation. This skill covers collecting and weighting attr +domain: cybersecurity +subdomain: threat-intelligence +tags: +- threat-intelligence +- cti +- ioc +- mitre-attack +- stix +- attribution +- campaign-analysis +version: '1.0' +author: mahipal +license: Apache-2.0 +nist_csf: +- ID.RA-01 +- ID.RA-05 +- DE.CM-01 +- DE.AE-02 +--- +# Analyzing Campaign Attribution Evidence + +## Overview + +Campaign attribution analysis involves systematically evaluating evidence to determine which threat actor or group is responsible for a cyber operation. This skill covers collecting and weighting attribution indicators using the Diamond Model and ACH (Analysis of Competing Hypotheses), analyzing infrastructure overlaps, TTP consistency, malware code similarities, operational timing patterns, and language artifacts to build confidence-weighted attribution assessments. + + +## When to Use + +- When investigating security incidents that require analyzing campaign attribution evidence +- When building detection rules or threat hunting queries for this domain +- When SOC analysts need structured procedures for this analysis type +- When validating security monitoring coverage for related attack techniques + +## Prerequisites + +- Python 3.9+ with `attackcti`, `stix2`, `networkx` libraries +- Access to threat intelligence platforms (MISP, OpenCTI) +- Understanding of Diamond Model of Intrusion Analysis +- Familiarity with MITRE ATT&CK threat group profiles +- Knowledge of malware analysis and infrastructure tracking techniques + +## Key Concepts + +### Attribution Evidence Categories +1. **Infrastructure Overlap**: Shared C2 servers, domains, IP ranges, hosting providers +2. **TTP Consistency**: Matching ATT&CK techniques and sub-techniques across campaigns +3. **Malware Code Similarity**: Shared code bases, compilers, PDB paths, encryption routines +4. **Operational Patterns**: Timing (working hours, time zones), targeting patterns, operational tempo +5. **Language Artifacts**: Embedded strings, variable names, error messages in specific languages +6. **Victimology**: Target sector, geography, and organizational profile consistency + +### Confidence Levels +- **High Confidence**: Multiple independent evidence categories converge on same actor +- **Moderate Confidence**: Several evidence categories match, some ambiguity remains +- **Low Confidence**: Limited evidence, possible false flags or shared tooling + +### Analysis of Competing Hypotheses (ACH) +Structured analytical method that evaluates evidence against multiple competing hypotheses. Each piece of evidence is scored as consistent, inconsistent, or neutral with respect to each hypothesis. The hypothesis with the least inconsistent evidence is favored. + +## Workflow + +### Step 1: Collect Attribution Evidence + +```python +from stix2 import MemoryStore, Filter +from collections import defaultdict + +class AttributionAnalyzer: + def __init__(self): + self.evidence = [] + self.hypotheses = {} + + def add_evidence(self, category, description, value, confidence): + self.evidence.append({ + "category": category, + "description": description, + "value": value, + "confidence": confidence, + "timestamp": None, + }) + + def add_hypothesis(self, actor_name, actor_id=""): + self.hypotheses[actor_name] = { + "actor_id": actor_id, + "consistent_evidence": [], + "inconsistent_evidence": [], + "neutral_evidence": [], + "score": 0, + } + + def evaluate_evidence(self, evidence_idx, actor_name, assessment): + """Assess evidence against a hypothesis: consistent/inconsistent/neutral.""" + if assessment == "consistent": + self.hypotheses[actor_name]["consistent_evidence"].append(evidence_idx) + self.hypotheses[actor_name]["score"] += self.evidence[evidence_idx]["confidence"] + elif assessment == "inconsistent": + self.hypotheses[actor_name]["inconsistent_evidence"].append(evidence_idx) + self.hypotheses[actor_name]["score"] -= self.evidence[evidence_idx]["confidence"] * 2 + else: + self.hypotheses[actor_name]["neutral_evidence"].append(evidence_idx) + + def rank_hypotheses(self): + """Rank hypotheses by attribution score.""" + ranked = sorted( + self.hypotheses.items(), + key=lambda x: x[1]["score"], + reverse=True, + ) + return [ + { + "actor": name, + "score": data["score"], + "consistent": len(data["consistent_evidence"]), + "inconsistent": len(data["inconsistent_evidence"]), + "confidence": self._score_to_confidence(data["score"]), + } + for name, data in ranked + ] + + def _score_to_confidence(self, score): + if score >= 80: + return "HIGH" + elif score >= 40: + return "MODERATE" + else: + return "LOW" +``` + +### Step 2: Infrastructure Overlap Analysis + +```python +def analyze_infrastructure_overlap(campaign_a_infra, campaign_b_infra): + """Compare infrastructure between two campaigns for attribution.""" + overlap = { + "shared_ips": set(campaign_a_infra.get("ips", [])).intersection( + campaign_b_infra.get("ips", []) + ), + "shared_domains": set(campaign_a_infra.get("domains", [])).intersection( + campaign_b_infra.get("domains", []) + ), + "shared_asns": set(campaign_a_infra.get("asns", [])).intersection( + campaign_b_infra.get("asns", []) + ), + "shared_registrars": set(campaign_a_infra.get("registrars", [])).intersection( + campaign_b_infra.get("registrars", []) + ), + } + + overlap_score = 0 + if overlap["shared_ips"]: + overlap_score += 30 + if overlap["shared_domains"]: + overlap_score += 25 + if overlap["shared_asns"]: + overlap_score += 15 + if overlap["shared_registrars"]: + overlap_score += 10 + + return { + "overlap": {k: list(v) for k, v in overlap.items()}, + "overlap_score": overlap_score, + "assessment": "STRONG" if overlap_score >= 40 else "MODERATE" if overlap_score >= 20 else "WEAK", + } +``` + +### Step 3: TTP Comparison Across Campaigns + +```python +from attackcti import attack_client + +def compare_campaign_ttps(campaign_techniques, known_actor_techniques): + """Compare campaign TTPs against known threat actor profiles.""" + campaign_set = set(campaign_techniques) + actor_set = set(known_actor_techniques) + + common = campaign_set.intersection(actor_set) + unique_campaign = campaign_set - actor_set + unique_actor = actor_set - campaign_set + + jaccard = len(common) / len(campaign_set.union(actor_set)) if campaign_set.union(actor_set) else 0 + + return { + "common_techniques": sorted(common), + "common_count": len(common), + "unique_to_campaign": sorted(unique_campaign), + "unique_to_actor": sorted(unique_actor), + "jaccard_similarity": round(jaccard, 3), + "overlap_percentage": round(len(common) / len(campaign_set) * 100, 1) if campaign_set else 0, + } +``` + +### Step 4: Generate Attribution Report + +```python +def generate_attribution_report(analyzer): + """Generate structured attribution assessment report.""" + rankings = analyzer.rank_hypotheses() + + report = { + "assessment_date": "2026-02-23", + "total_evidence_items": len(analyzer.evidence), + "hypotheses_evaluated": len(analyzer.hypotheses), + "rankings": rankings, + "primary_attribution": rankings[0] if rankings else None, + "evidence_summary": [ + { + "index": i, + "category": e["category"], + "description": e["description"], + "confidence": e["confidence"], + } + for i, e in enumerate(analyzer.evidence) + ], + } + + return report +``` + +## Validation Criteria + +- Evidence collection covers all six attribution categories +- ACH matrix properly evaluates evidence against competing hypotheses +- Infrastructure overlap analysis identifies shared indicators +- TTP comparison uses ATT&CK technique IDs for precision +- Attribution confidence levels are properly justified +- Report includes alternative hypotheses and false flag considerations + +## References + +- [Diamond Model of Intrusion Analysis](https://www.activeresponse.org/wp-content/uploads/2013/07/diamond.pdf) +- [MITRE ATT&CK Groups](https://attack.mitre.org/groups/) +- [Analysis of Competing Hypotheses](https://www.cia.gov/static/9a5f1162fd0932c29e985f0159f56c07/Tradecraft-Primer-apr09.pdf) +- [Threat Attribution Framework](https://www.mandiant.com/resources/reports) diff --git a/personas/_shared/skills/analyzing-campaign-attribution-evidence/assets/template.md b/personas/_shared/skills/analyzing-campaign-attribution-evidence/assets/template.md new file mode 100644 index 0000000..c43da08 --- /dev/null +++ b/personas/_shared/skills/analyzing-campaign-attribution-evidence/assets/template.md @@ -0,0 +1,39 @@ +# Campaign Attribution Analysis Report Template + +## Report Metadata +| Field | Value | +|-------|-------| +| Report ID | CTI-YYYY-NNNN | +| Date | YYYY-MM-DD | +| Classification | TLP:AMBER | +| Analyst | [Name] | +| Confidence | High/Moderate/Low | + +## Executive Summary +[Brief overview of key findings and their significance] + +## Key Findings +1. [Finding 1 with supporting evidence] +2. [Finding 2 with supporting evidence] +3. [Finding 3 with supporting evidence] + +## Detailed Analysis +### Finding 1 +- **Evidence**: [Description of evidence] +- **Confidence**: High/Moderate/Low +- **MITRE ATT&CK**: [Relevant technique IDs] +- **Impact Assessment**: [Potential impact to organization] + +## Indicators of Compromise +| Type | Value | Context | Confidence | +|------|-------|---------|-----------| +| | | | | + +## Recommendations +1. **Immediate**: [Actions requiring immediate attention] +2. **Short-term**: [Actions within 1-2 weeks] +3. **Long-term**: [Strategic improvements] + +## References +- [Source 1] +- [Source 2] diff --git a/personas/_shared/skills/analyzing-campaign-attribution-evidence/references/api-reference.md b/personas/_shared/skills/analyzing-campaign-attribution-evidence/references/api-reference.md new file mode 100644 index 0000000..96ccc8a --- /dev/null +++ b/personas/_shared/skills/analyzing-campaign-attribution-evidence/references/api-reference.md @@ -0,0 +1,110 @@ +# API Reference: Campaign Attribution Evidence Analysis + +## Diamond Model of Intrusion Analysis + +### Four Core Features +| Feature | Description | Attribution Value | +|---------|-------------|-------------------| +| Adversary | Threat actor identity | Direct attribution | +| Capability | Malware, exploits, tools | Indirect - shared tooling | +| Infrastructure | C2, domains, IPs | Strong - operational overlap | +| Victim | Targets, sectors, regions | Contextual - targeting pattern | + +### Pivot Analysis +``` +Adversary ←→ Capability ←→ Infrastructure ←→ Victim + ↕ ↕ ↕ ↕ + (HUMINT) (Malware DB) (WHOIS/DNS) (Victimology) +``` + +## Analysis of Competing Hypotheses (ACH) + +### Matrix Format +``` +Evidence \ Hypothesis | APT28 | APT29 | Lazarus | Unknown +----------------------------------------------------------------- +Infrastructure overlap | ++ | - | - | N +TTP consistency | ++ | ++ | - | N +Malware similarity | + | - | - | N +Timing (UTC+3) | ++ | ++ | - | N +Language (Russian) | ++ | ++ | - | N +``` + +### Scoring +| Symbol | Meaning | Weight | +|--------|---------|--------| +| `++` | Strongly consistent | +2 | +| `+` | Consistent | +1 | +| `N` | Neutral | 0 | +| `-` | Inconsistent | -1 | +| `--` | Strongly inconsistent | -2 | + +## MITRE ATT&CK Group Queries + +### Python (mitreattack-python) +```python +from mitreattack.stix20 import MitreAttackData +attack = MitreAttackData("enterprise-attack.json") +group = attack.get_group_by_alias("APT29") +techniques = attack.get_techniques_used_by_group(group.id) +``` + +### STIX2 Relationship Query +```python +from stix2 import Filter +relationships = src.query([ + Filter("type", "=", "relationship"), + Filter("source_ref", "=", group_id), + Filter("relationship_type", "=", "uses"), +]) +``` + +## Infrastructure Overlap Tools + +### PassiveTotal / RiskIQ +```bash +# WHOIS history +curl -u user:key "https://api.passivetotal.org/v2/whois?query=domain.com" + +# Passive DNS +curl -u user:key "https://api.passivetotal.org/v2/dns/passive?query=1.2.3.4" +``` + +### VirusTotal Relations +```bash +curl -H "x-apikey: KEY" \ + "https://www.virustotal.com/api/v3/domains/example.com/communicating_files" +``` + +## Confidence Assessment Framework + +| Level | Score Range | Criteria | +|-------|------------|---------| +| HIGH | 0.8-1.0 | Multiple independent evidence types converge | +| MEDIUM | 0.5-0.8 | Significant evidence with some gaps | +| LOW | 0.2-0.5 | Limited evidence, alternative hypotheses remain | +| NEGLIGIBLE | 0.0-0.2 | Insufficient evidence for attribution | + +## STIX Attribution Objects + +### Campaign Object +```json +{ + "type": "campaign", + "name": "Operation DarkShadow", + "first_seen": "2024-01-15T00:00:00Z", + "last_seen": "2024-03-20T00:00:00Z", + "objective": "Espionage targeting defense sector" +} +``` + +### Attribution Relationship +```json +{ + "type": "relationship", + "relationship_type": "attributed-to", + "source_ref": "campaign--abc123", + "target_ref": "intrusion-set--def456", + "confidence": 75 +} +``` diff --git a/personas/_shared/skills/analyzing-campaign-attribution-evidence/references/standards.md b/personas/_shared/skills/analyzing-campaign-attribution-evidence/references/standards.md new file mode 100644 index 0000000..fb9c60d --- /dev/null +++ b/personas/_shared/skills/analyzing-campaign-attribution-evidence/references/standards.md @@ -0,0 +1,24 @@ +# Standards and Frameworks Reference + +## Applicable Standards +- **STIX 2.1**: Structured Threat Information eXpression for CTI data representation +- **TAXII 2.1**: Transport protocol for sharing CTI over HTTPS +- **MITRE ATT&CK**: Adversary tactics, techniques, and procedures taxonomy +- **Diamond Model**: Intrusion analysis framework (Adversary, Capability, Infrastructure, Victim) +- **Traffic Light Protocol (TLP)**: Information sharing classification (CLEAR, GREEN, AMBER, RED) + +## MITRE ATT&CK Relevance +- Technique mapping for threat actor behavior classification +- Data sources for detection capability assessment +- Mitigation strategies linked to specific techniques + +## Industry Frameworks +- NIST Cybersecurity Framework (CSF) 2.0 - Identify function +- ISO 27001:2022 - A.5.7 Threat Intelligence +- FIRST Standards - TLP, CSIRT, vulnerability coordination + +## References +- [STIX 2.1 Specification](https://docs.oasis-open.org/cti/stix/v2.1/stix-v2.1.html) +- [MITRE ATT&CK](https://attack.mitre.org/) +- [Diamond Model Paper](https://www.activeresponse.org/wp-content/uploads/2013/07/diamond.pdf) +- [NIST CSF 2.0](https://www.nist.gov/cyberframework) diff --git a/personas/_shared/skills/analyzing-campaign-attribution-evidence/references/workflows.md b/personas/_shared/skills/analyzing-campaign-attribution-evidence/references/workflows.md new file mode 100644 index 0000000..8630223 --- /dev/null +++ b/personas/_shared/skills/analyzing-campaign-attribution-evidence/references/workflows.md @@ -0,0 +1,31 @@ +# Campaign Attribution Analysis Workflows + +## Workflow 1: Collection and Analysis +``` +[Intelligence Sources] --> [Data Collection] --> [Analysis] --> [Reporting] + | | | | + v v v v + OSINT/HUMINT/SIGINT Normalize/Enrich Assess/Correlate Disseminate +``` + +### Steps: +1. **Planning**: Define intelligence requirements and collection priorities +2. **Collection**: Gather data from relevant sources +3. **Processing**: Normalize data formats and filter noise +4. **Analysis**: Apply analytical frameworks and correlate findings +5. **Production**: Generate intelligence products and reports +6. **Dissemination**: Share with stakeholders via appropriate channels +7. **Feedback**: Collect consumer feedback to refine future collection + +## Workflow 2: Continuous Monitoring +``` +[Watchlist] --> [Automated Monitoring] --> [Change Detection] --> [Alert/Update] +``` + +### Steps: +1. **Define Watchlist**: Identify indicators, actors, and topics to monitor +2. **Configure Monitoring**: Set up automated collection from relevant sources +3. **Change Detection**: Identify new or changed intelligence +4. **Assessment**: Evaluate significance of changes +5. **Alerting**: Notify stakeholders of significant intelligence updates +6. **Archive**: Store intelligence for historical analysis and trending diff --git a/personas/_shared/skills/analyzing-campaign-attribution-evidence/scripts/agent.py b/personas/_shared/skills/analyzing-campaign-attribution-evidence/scripts/agent.py new file mode 100644 index 0000000..ac5608f --- /dev/null +++ b/personas/_shared/skills/analyzing-campaign-attribution-evidence/scripts/agent.py @@ -0,0 +1,253 @@ +#!/usr/bin/env python3 +"""Campaign attribution analysis agent using Diamond Model and ACH methodology. + +Evaluates attribution evidence including infrastructure overlaps, TTP consistency, +malware code similarity, timing patterns, and language artifacts. +""" + +import json +import re +from collections import defaultdict +from datetime import datetime + + +DIAMOND_DIMENSIONS = { + "adversary": "Threat actor identity, group attribution", + "capability": "Malware, exploits, tools used", + "infrastructure": "C2 servers, domains, IP addresses", + "victim": "Targeted sectors, regions, organizations", +} + +EVIDENCE_WEIGHTS = { + "infrastructure_overlap": 0.25, + "ttp_consistency": 0.30, + "malware_code_similarity": 0.25, + "timing_pattern": 0.10, + "language_artifact": 0.10, +} + +CONFIDENCE_LEVELS = { + (0.8, 1.0): "HIGH - Strong attribution confidence", + (0.5, 0.8): "MEDIUM - Moderate attribution, further analysis recommended", + (0.2, 0.5): "LOW - Weak attribution, insufficient evidence", + (0.0, 0.2): "NEGLIGIBLE - No meaningful attribution possible", +} + + +def diamond_model_analysis(adversary=None, capability=None, infrastructure=None, victim=None): + """Structure evidence using the Diamond Model of Intrusion Analysis.""" + model = { + "adversary": { + "identified": adversary is not None, + "details": adversary or "Unknown", + }, + "capability": { + "tools": capability.get("tools", []) if capability else [], + "exploits": capability.get("exploits", []) if capability else [], + "malware": capability.get("malware", []) if capability else [], + }, + "infrastructure": { + "c2_servers": infrastructure.get("c2", []) if infrastructure else [], + "domains": infrastructure.get("domains", []) if infrastructure else [], + "ip_addresses": infrastructure.get("ips", []) if infrastructure else [], + }, + "victim": { + "sectors": victim.get("sectors", []) if victim else [], + "regions": victim.get("regions", []) if victim else [], + }, + "pivot_opportunities": [], + } + if infrastructure and infrastructure.get("c2"): + model["pivot_opportunities"].append("Pivot from C2 infrastructure to related campaigns") + if capability and capability.get("malware"): + model["pivot_opportunities"].append("Pivot from malware samples to shared infrastructure") + return model + + +def evaluate_infrastructure_overlap(campaign_infra, known_actor_infra): + """Score infrastructure overlap between campaign and known actor.""" + campaign_set = set(campaign_infra) + known_set = set(known_actor_infra) + if not campaign_set or not known_set: + return 0.0, [] + overlap = campaign_set & known_set + score = len(overlap) / max(len(campaign_set), len(known_set)) + return round(score, 4), sorted(overlap) + + +def evaluate_ttp_consistency(campaign_ttps, actor_ttps): + """Score TTP consistency using MITRE ATT&CK technique overlap.""" + campaign_set = set(campaign_ttps) + actor_set = set(actor_ttps) + if not campaign_set or not actor_set: + return 0.0, [] + overlap = campaign_set & actor_set + jaccard = len(overlap) / len(campaign_set | actor_set) + return round(jaccard, 4), sorted(overlap) + + +def evaluate_malware_similarity(sample_features, known_features): + """Score malware code similarity based on feature comparison.""" + if not sample_features or not known_features: + return 0.0 + matches = 0 + total = max(len(sample_features), len(known_features)) + for feature in sample_features: + if feature in known_features: + matches += 1 + return round(matches / total, 4) if total > 0 else 0.0 + + +def evaluate_timing_pattern(campaign_timestamps, actor_timezone_offset=None): + """Analyze operational timing to infer timezone/working hours.""" + if not campaign_timestamps: + return {"score": 0.0, "working_hours": None, "timezone_guess": None} + hours = [] + for ts in campaign_timestamps: + try: + if isinstance(ts, str): + dt = datetime.fromisoformat(ts.replace("Z", "+00:00")) + else: + dt = ts + adjusted = dt.hour + (actor_timezone_offset or 0) + hours.append(adjusted % 24) + except (ValueError, TypeError): + continue + if not hours: + return {"score": 0.0} + work_hours = sum(1 for h in hours if 8 <= h <= 18) + work_ratio = work_hours / len(hours) + avg_hour = sum(hours) / len(hours) + return { + "score": round(work_ratio, 4), + "average_hour_utc": round(avg_hour, 1), + "work_hour_ratio": round(work_ratio, 4), + "sample_size": len(hours), + } + + +def evaluate_language_artifacts(strings_list): + """Detect language artifacts in malware strings or documents.""" + language_indicators = { + "Russian": [r"[а-яА-Я]{3,}", r"codepage.*1251", r"locale.*ru"], + "Chinese": [r"[\u4e00-\u9fff]{2,}", r"codepage.*936", r"GB2312"], + "Korean": [r"[\uac00-\ud7af]{2,}", r"codepage.*949", r"EUC-KR"], + "Farsi": [r"[\u0600-\u06ff]{3,}", r"codepage.*1256"], + "English": [r"\b(the|and|for|with)\b"], + } + detections = defaultdict(int) + for s in strings_list: + for lang, patterns in language_indicators.items(): + for pattern in patterns: + if re.search(pattern, s, re.IGNORECASE): + detections[lang] += 1 + total = sum(detections.values()) or 1 + scored = {lang: round(count / total, 4) for lang, count in detections.items()} + return scored + + +def ach_analysis(hypotheses, evidence_items): + """Analysis of Competing Hypotheses (ACH) for attribution.""" + matrix = {} + for hyp in hypotheses: + hyp_name = hyp["name"] + matrix[hyp_name] = {"consistent": 0, "inconsistent": 0, "neutral": 0, "score": 0} + for evidence in evidence_items: + ev_name = evidence["name"] + consistency = evidence.get("hypotheses", {}).get(hyp_name, "neutral") + if consistency == "consistent": + matrix[hyp_name]["consistent"] += evidence.get("weight", 1) + elif consistency == "inconsistent": + matrix[hyp_name]["inconsistent"] += evidence.get("weight", 1) + else: + matrix[hyp_name]["neutral"] += evidence.get("weight", 1) + c = matrix[hyp_name]["consistent"] + i = matrix[hyp_name]["inconsistent"] + matrix[hyp_name]["score"] = round((c - i) / (c + i + 0.01), 4) + return matrix + + +def compute_attribution_score(scores): + """Compute weighted attribution confidence score.""" + total = 0.0 + for evidence_type, weight in EVIDENCE_WEIGHTS.items(): + score = scores.get(evidence_type, 0.0) + total += score * weight + confidence = "UNKNOWN" + for (low, high), label in CONFIDENCE_LEVELS.items(): + if low <= total < high: + confidence = label + break + return round(total, 4), confidence + + +def generate_attribution_report(campaign_name, candidate_actor, evidence): + """Generate structured attribution assessment report.""" + scores = {} + details = {} + + infra_score, infra_overlap = evaluate_infrastructure_overlap( + evidence.get("campaign_infra", []), evidence.get("actor_infra", [])) + scores["infrastructure_overlap"] = infra_score + details["infrastructure_overlap"] = infra_overlap + + ttp_score, ttp_overlap = evaluate_ttp_consistency( + evidence.get("campaign_ttps", []), evidence.get("actor_ttps", [])) + scores["ttp_consistency"] = ttp_score + details["ttp_consistency"] = ttp_overlap + + malware_score = evaluate_malware_similarity( + evidence.get("sample_features", []), evidence.get("known_features", [])) + scores["malware_code_similarity"] = malware_score + + timing = evaluate_timing_pattern( + evidence.get("timestamps", []), evidence.get("tz_offset")) + scores["timing_pattern"] = timing.get("score", 0.0) + details["timing"] = timing + + lang = evaluate_language_artifacts(evidence.get("strings", [])) + scores["language_artifact"] = max(lang.values()) if lang else 0.0 + details["language_artifacts"] = lang + + total_score, confidence = compute_attribution_score(scores) + + return { + "campaign": campaign_name, + "candidate_actor": candidate_actor, + "attribution_score": total_score, + "confidence_level": confidence, + "evidence_scores": scores, + "evidence_details": details, + } + + +if __name__ == "__main__": + print("=" * 60) + print("Campaign Attribution Evidence Analysis Agent") + print("Diamond Model, ACH, TTP/infrastructure/malware scoring") + print("=" * 60) + + demo_evidence = { + "campaign_infra": ["185.220.101.1", "evil-domain.com", "c2.attacker.net"], + "actor_infra": ["185.220.101.1", "c2.attacker.net", "other-domain.org"], + "campaign_ttps": ["T1566.001", "T1059.001", "T1053.005", "T1071.001", "T1041"], + "actor_ttps": ["T1566.001", "T1059.001", "T1053.005", "T1071.001", "T1021.001", "T1003.001"], + "sample_features": ["xor_0x55", "mutex_Global\\QWE", "ua_Mozilla5", "rc4_key"], + "known_features": ["xor_0x55", "mutex_Global\\QWE", "ua_Mozilla5", "aes_cbc"], + "timestamps": ["2024-03-15T06:30:00Z", "2024-03-15T07:15:00Z", + "2024-03-16T08:00:00Z", "2024-03-16T09:45:00Z"], + "tz_offset": 3, + "strings": ["Привет мир", "connect to server", "upload file"], + } + + report = generate_attribution_report("Operation DarkShadow", "APT29", demo_evidence) + + print(f"\n[*] Campaign: {report['campaign']}") + print(f"[*] Candidate: {report['candidate_actor']}") + print(f"[*] Attribution Score: {report['attribution_score']}") + print(f"[*] Confidence: {report['confidence_level']}") + print("\n--- Evidence Scores ---") + for ev, score in report["evidence_scores"].items(): + weight = EVIDENCE_WEIGHTS.get(ev, 0) + print(f" {ev:30s} score={score:.4f} weight={weight}") + print(f"\n[*] Full report:\n{json.dumps(report, indent=2, default=str)}") diff --git a/personas/_shared/skills/analyzing-campaign-attribution-evidence/scripts/process.py b/personas/_shared/skills/analyzing-campaign-attribution-evidence/scripts/process.py new file mode 100644 index 0000000..3e1a9ae --- /dev/null +++ b/personas/_shared/skills/analyzing-campaign-attribution-evidence/scripts/process.py @@ -0,0 +1,169 @@ +#!/usr/bin/env python3 +""" +Campaign Attribution Evidence Analysis Script + +Implements structured attribution analysis: +- Analysis of Competing Hypotheses (ACH) matrix +- Infrastructure overlap scoring +- TTP similarity comparison using ATT&CK +- Evidence weighting and confidence assessment + +Requirements: + pip install attackcti stix2 requests + +Usage: + python process.py --evidence evidence.json --hypotheses actors.json --output report.json + python process.py --compare-ttps --campaign campaign_techs.json --actor APT29 +""" + +import argparse +import json +import sys +from collections import defaultdict + + +class AttributionEngine: + """Structured attribution analysis using ACH methodology.""" + + def __init__(self): + self.evidence = [] + self.hypotheses = {} + + def load_evidence(self, filepath): + with open(filepath) as f: + self.evidence = json.load(f) + + def add_evidence(self, category, description, value, confidence): + self.evidence.append({ + "id": len(self.evidence), + "category": category, + "description": description, + "value": value, + "confidence": confidence, + }) + + def add_hypothesis(self, actor_name, supporting_info=""): + self.hypotheses[actor_name] = { + "info": supporting_info, + "assessments": {}, + "score": 0, + } + + def evaluate(self, evidence_id, actor_name, assessment): + """Evaluate evidence against hypothesis: C=consistent, I=inconsistent, N=neutral.""" + weight = self.evidence[evidence_id]["confidence"] + self.hypotheses[actor_name]["assessments"][evidence_id] = assessment + + if assessment == "C": + self.hypotheses[actor_name]["score"] += weight + elif assessment == "I": + self.hypotheses[actor_name]["score"] -= weight * 2 + + def generate_ach_matrix(self): + matrix = {"evidence": [], "hypotheses": {}} + for e in self.evidence: + matrix["evidence"].append({ + "id": e["id"], + "category": e["category"], + "description": e["description"], + }) + + for actor, data in self.hypotheses.items(): + matrix["hypotheses"][actor] = { + "assessments": data["assessments"], + "score": data["score"], + "consistent": sum(1 for a in data["assessments"].values() if a == "C"), + "inconsistent": sum(1 for a in data["assessments"].values() if a == "I"), + "neutral": sum(1 for a in data["assessments"].values() if a == "N"), + } + + return matrix + + def rank(self): + ranked = sorted( + self.hypotheses.items(), key=lambda x: x[1]["score"], reverse=True + ) + results = [] + for name, data in ranked: + incon = sum(1 for a in data["assessments"].values() if a == "I") + confidence = "HIGH" if data["score"] >= 80 and incon == 0 else \ + "MODERATE" if data["score"] >= 40 else "LOW" + results.append({ + "actor": name, + "score": data["score"], + "confidence": confidence, + "inconsistent_count": incon, + }) + return results + + +def compare_ttp_similarity(campaign_techs, actor_techs): + campaign_set = set(campaign_techs) + actor_set = set(actor_techs) + common = campaign_set & actor_set + + jaccard = len(common) / len(campaign_set | actor_set) if (campaign_set | actor_set) else 0 + return { + "common": sorted(common), + "jaccard_similarity": round(jaccard, 3), + "campaign_coverage": round(len(common) / len(campaign_set) * 100, 1) if campaign_set else 0, + } + + +def main(): + parser = argparse.ArgumentParser(description="Campaign Attribution Analysis") + parser.add_argument("--evidence", help="Evidence JSON file") + parser.add_argument("--hypotheses", help="Hypotheses JSON file") + parser.add_argument("--compare-ttps", action="store_true") + parser.add_argument("--campaign", help="Campaign techniques JSON") + parser.add_argument("--actor", help="Actor name for ATT&CK lookup") + parser.add_argument("--output", default="attribution_report.json") + + args = parser.parse_args() + engine = AttributionEngine() + + if args.evidence and args.hypotheses: + engine.load_evidence(args.evidence) + with open(args.hypotheses) as f: + hyps = json.load(f) + for h in hyps: + engine.add_hypothesis(h["name"], h.get("info", "")) + for eid, assessment in h.get("evaluations", {}).items(): + engine.evaluate(int(eid), h["name"], assessment) + + matrix = engine.generate_ach_matrix() + rankings = engine.rank() + report = {"ach_matrix": matrix, "rankings": rankings} + print(json.dumps(report, indent=2)) + + with open(args.output, "w") as f: + json.dump(report, f, indent=2) + + elif args.compare_ttps and args.campaign: + with open(args.campaign) as f: + campaign_techs = json.load(f) + + if args.actor: + try: + from attackcti import attack_client + lift = attack_client() + groups = lift.get_groups() + group = next( + (g for g in groups if args.actor.lower() in g.get("name", "").lower()), + None, + ) + if group: + gid = group["external_references"][0]["external_id"] + techs = lift.get_techniques_used_by_group(gid) + actor_techs = [ + t["external_references"][0]["external_id"] + for t in techs if t.get("external_references") + ] + result = compare_ttp_similarity(campaign_techs, actor_techs) + print(json.dumps(result, indent=2)) + except ImportError: + print("[-] attackcti not installed") + + +if __name__ == "__main__": + main() diff --git a/personas/_shared/skills/analyzing-certificate-transparency-for-phishing/LICENSE b/personas/_shared/skills/analyzing-certificate-transparency-for-phishing/LICENSE new file mode 100644 index 0000000..d885118 --- /dev/null +++ b/personas/_shared/skills/analyzing-certificate-transparency-for-phishing/LICENSE @@ -0,0 +1,201 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to the Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by the Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding any notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. Please do not remove or change + the license header comment from a contributed file except when + necessary. + + Copyright 2026 mukul975 + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/personas/_shared/skills/analyzing-certificate-transparency-for-phishing/SKILL.md b/personas/_shared/skills/analyzing-certificate-transparency-for-phishing/SKILL.md new file mode 100644 index 0000000..0ae3157 --- /dev/null +++ b/personas/_shared/skills/analyzing-certificate-transparency-for-phishing/SKILL.md @@ -0,0 +1,336 @@ +--- +name: analyzing-certificate-transparency-for-phishing +description: Monitor Certificate Transparency logs using crt.sh and Certstream to detect phishing domains, lookalike certificates, + and unauthorized certificate issuance targeting your organization. +domain: cybersecurity +subdomain: threat-intelligence +tags: +- certificate-transparency +- ct-logs +- phishing +- crt-sh +- certstream +- ssl +- domain-monitoring +- threat-intelligence +version: '1.0' +author: mahipal +license: Apache-2.0 +atlas_techniques: +- AML.T0052 +nist_csf: +- ID.RA-01 +- ID.RA-05 +- DE.CM-01 +- DE.AE-02 +--- +# Analyzing Certificate Transparency for Phishing + +## Overview + +Certificate Transparency (CT) is an Internet security standard that creates a public, append-only log of all issued SSL/TLS certificates. Monitoring CT logs enables early detection of phishing domains that register certificates mimicking legitimate brands, unauthorized certificate issuance for owned domains, and certificate-based attack infrastructure. This skill covers querying CT logs via crt.sh, real-time monitoring with Certstream, building automated alerting for suspicious certificates, and integrating findings into threat intelligence workflows. + + +## When to Use + +- When investigating security incidents that require analyzing certificate transparency for phishing +- When building detection rules or threat hunting queries for this domain +- When SOC analysts need structured procedures for this analysis type +- When validating security monitoring coverage for related attack techniques + +## Prerequisites + +- Python 3.9+ with `requests`, `certstream`, `tldextract`, `Levenshtein` libraries +- Access to crt.sh (https://crt.sh/) for historical CT log queries +- Certstream (https://certstream.calidog.io/) for real-time monitoring +- List of organization domains and brand keywords to monitor +- Understanding of SSL/TLS certificate structure and issuance process + +## Key Concepts + +### Certificate Transparency Logs + +CT logs are cryptographically assured, publicly auditable, append-only records of TLS certificate issuance. Major CAs (Let's Encrypt, DigiCert, Sectigo, Google Trust Services) submit all issued certificates to multiple CT logs. As of 2025, Chrome and Safari require CT for all publicly trusted certificates. + +### Phishing Detection via CT + +Attackers register lookalike domains and obtain free certificates (often from Let's Encrypt) to make phishing sites appear legitimate with HTTPS. CT monitoring detects these early because the certificate appears in logs before the phishing campaign launches, providing a window for proactive blocking. + +### crt.sh Database + +crt.sh is a free web interface and PostgreSQL database operated by Sectigo that indexes CT logs. It supports wildcard searches (`%.example.com`), direct SQL queries, and JSON API responses. It tracks certificate issuance, expiration, and revocation across all major CT logs. + +## Workflow + +### Step 1: Query crt.sh for Certificate History + +```python +import requests +import json +from datetime import datetime +import tldextract + +class CTLogMonitor: + CRT_SH_URL = "https://crt.sh" + + def __init__(self, monitored_domains, brand_keywords): + self.monitored_domains = monitored_domains + self.brand_keywords = [k.lower() for k in brand_keywords] + + def query_crt_sh(self, domain, include_expired=False): + """Query crt.sh for certificates matching a domain.""" + params = { + "q": f"%.{domain}", + "output": "json", + } + if not include_expired: + params["exclude"] = "expired" + + resp = requests.get(self.CRT_SH_URL, params=params, timeout=30) + if resp.status_code == 200: + certs = resp.json() + print(f"[+] crt.sh: {len(certs)} certificates for *.{domain}") + return certs + return [] + + def find_suspicious_certs(self, domain): + """Find certificates that may be phishing attempts.""" + certs = self.query_crt_sh(domain) + suspicious = [] + + for cert in certs: + common_name = cert.get("common_name", "").lower() + name_value = cert.get("name_value", "").lower() + issuer = cert.get("issuer_name", "") + not_before = cert.get("not_before", "") + not_after = cert.get("not_after", "") + + # Check for exact domain matches (legitimate) + extracted = tldextract.extract(common_name) + cert_domain = f"{extracted.domain}.{extracted.suffix}" + if cert_domain == domain: + continue # Legitimate certificate + + # Flag suspicious patterns + flags = [] + if domain.replace(".", "") in common_name.replace(".", ""): + flags.append("contains target domain string") + if any(kw in common_name for kw in self.brand_keywords): + flags.append("contains brand keyword") + if "let's encrypt" in issuer.lower(): + flags.append("free CA (Let's Encrypt)") + + if flags: + suspicious.append({ + "common_name": cert.get("common_name", ""), + "name_value": cert.get("name_value", ""), + "issuer": issuer, + "not_before": not_before, + "not_after": not_after, + "serial": cert.get("serial_number", ""), + "flags": flags, + "crt_sh_id": cert.get("id", ""), + "crt_sh_url": f"https://crt.sh/?id={cert.get('id', '')}", + }) + + print(f"[+] Found {len(suspicious)} suspicious certificates") + return suspicious + +monitor = CTLogMonitor( + monitored_domains=["mycompany.com", "mycompany.org"], + brand_keywords=["mycompany", "mybrand", "myproduct"], +) +suspicious = monitor.find_suspicious_certs("mycompany.com") +for cert in suspicious[:5]: + print(f" [{cert['common_name']}] Flags: {cert['flags']}") +``` + +### Step 2: Real-Time Monitoring with Certstream + +```python +import certstream +import Levenshtein +import re +from datetime import datetime + +class CertstreamMonitor: + def __init__(self, watched_domains, brand_keywords, similarity_threshold=0.8): + self.watched_domains = [d.lower() for d in watched_domains] + self.brand_keywords = [k.lower() for k in brand_keywords] + self.threshold = similarity_threshold + self.alerts = [] + + def start_monitoring(self, max_alerts=100): + """Start real-time CT log monitoring.""" + print("[*] Starting Certstream monitoring...") + print(f" Watching: {self.watched_domains}") + print(f" Keywords: {self.brand_keywords}") + + def callback(message, context): + if message["message_type"] == "certificate_update": + data = message["data"] + leaf = data.get("leaf_cert", {}) + all_domains = leaf.get("all_domains", []) + + for domain in all_domains: + domain_lower = domain.lower().strip("*.") + if self._is_suspicious(domain_lower): + alert = { + "domain": domain, + "all_domains": all_domains, + "issuer": leaf.get("issuer", {}).get("O", ""), + "fingerprint": leaf.get("fingerprint", ""), + "not_before": leaf.get("not_before", ""), + "detected_at": datetime.now().isoformat(), + "reason": self._get_reason(domain_lower), + } + self.alerts.append(alert) + print(f" [ALERT] {domain} - {alert['reason']}") + + if len(self.alerts) >= max_alerts: + raise KeyboardInterrupt + + try: + certstream.listen_for_events(callback, url="wss://certstream.calidog.io/") + except KeyboardInterrupt: + print(f"\n[+] Monitoring stopped. {len(self.alerts)} alerts collected.") + return self.alerts + + def _is_suspicious(self, domain): + """Check if domain is suspicious relative to watched domains.""" + for watched in self.watched_domains: + # Exact keyword match + watched_base = watched.split(".")[0] + if watched_base in domain and domain != watched: + return True + + # Levenshtein distance (typosquatting detection) + domain_base = tldextract.extract(domain).domain + similarity = Levenshtein.ratio(watched_base, domain_base) + if similarity >= self.threshold and domain_base != watched_base: + return True + + # Brand keyword match + for keyword in self.brand_keywords: + if keyword in domain: + return True + + return False + + def _get_reason(self, domain): + """Determine why domain was flagged.""" + reasons = [] + for watched in self.watched_domains: + watched_base = watched.split(".")[0] + if watched_base in domain: + reasons.append(f"contains '{watched_base}'") + domain_base = tldextract.extract(domain).domain + similarity = Levenshtein.ratio(watched_base, domain_base) + if similarity >= self.threshold and domain_base != watched_base: + reasons.append(f"similar to '{watched}' ({similarity:.0%})") + for kw in self.brand_keywords: + if kw in domain: + reasons.append(f"brand keyword '{kw}'") + return "; ".join(reasons) if reasons else "unknown" + +cs_monitor = CertstreamMonitor( + watched_domains=["mycompany.com"], + brand_keywords=["mycompany", "mybrand"], + similarity_threshold=0.75, +) +alerts = cs_monitor.start_monitoring(max_alerts=50) +``` + +### Step 3: Enumerate Subdomains from CT Logs + +```python +def enumerate_subdomains_ct(domain): + """Discover all subdomains from Certificate Transparency logs.""" + params = {"q": f"%.{domain}", "output": "json"} + resp = requests.get("https://crt.sh", params=params, timeout=30) + + if resp.status_code != 200: + return [] + + certs = resp.json() + subdomains = set() + for cert in certs: + name_value = cert.get("name_value", "") + for name in name_value.split("\n"): + name = name.strip().lower() + if name.endswith(f".{domain}") or name == domain: + name = name.lstrip("*.") + subdomains.add(name) + + sorted_subs = sorted(subdomains) + print(f"[+] CT subdomain enumeration for {domain}: {len(sorted_subs)} subdomains") + return sorted_subs + +subdomains = enumerate_subdomains_ct("example.com") +for sub in subdomains[:20]: + print(f" {sub}") +``` + +### Step 4: Generate CT Intelligence Report + +```python +def generate_ct_report(suspicious_certs, certstream_alerts, domain): + report = f"""# Certificate Transparency Intelligence Report +## Target Domain: {domain} +## Generated: {datetime.now().isoformat()} + +## Summary +- Suspicious certificates found: {len(suspicious_certs)} +- Real-time alerts triggered: {len(certstream_alerts)} + +## Suspicious Certificates (crt.sh) +| Common Name | Issuer | Flags | crt.sh Link | +|------------|--------|-------|-------------| +""" + for cert in suspicious_certs[:20]: + flags = "; ".join(cert.get("flags", [])) + report += (f"| {cert['common_name']} | {cert['issuer'][:30]} " + f"| {flags} | [View]({cert['crt_sh_url']}) |\n") + + report += f""" +## Real-Time Certstream Alerts +| Domain | Issuer | Reason | Detected | +|--------|--------|--------|----------| +""" + for alert in certstream_alerts[:20]: + report += (f"| {alert['domain']} | {alert['issuer']} " + f"| {alert['reason']} | {alert['detected_at'][:19]} |\n") + + report += """ +## Recommendations +1. Add flagged domains to DNS sinkhole / web proxy blocklist +2. Submit takedown requests for confirmed phishing domains +3. Monitor CT logs continuously for new certificate registrations +4. Implement CAA DNS records to restrict certificate issuance for your domains +5. Deploy DMARC to prevent email spoofing from lookalike domains +""" + with open(f"ct_report_{domain.replace('.','_')}.md", "w") as f: + f.write(report) + print(f"[+] CT report saved") + return report + +generate_ct_report(suspicious, alerts if 'alerts' in dir() else [], "mycompany.com") +``` + +## Validation Criteria + +- crt.sh queries return certificate data for target domains +- Suspicious certificates identified based on lookalike patterns +- Certstream real-time monitoring detects new phishing certificates +- Subdomain enumeration produces comprehensive list from CT logs +- Alerts generated with reason classification +- CT intelligence report created with actionable recommendations + +## References + +- [crt.sh Certificate Search](https://crt.sh/) +- [Certstream Real-Time CT Monitor](https://certstream.calidog.io/) +- [River Security: CT Logs for Attack Surface Discovery](https://riversecurity.eu/finding-attack-surface-and-fraudulent-domains-via-certificate-transparency-logs/) +- [Let's Encrypt: Certificate Transparency Logs](https://letsencrypt.org/docs/ct-logs/) +- [SSLMate Cert Spotter](https://sslmate.com/certspotter/) +- [CyberSierra: CT Logs as Early Warning System](https://cybersierra.co/blog/ssl-certificate-transparency-logs/) diff --git a/personas/_shared/skills/analyzing-certificate-transparency-for-phishing/references/api-reference.md b/personas/_shared/skills/analyzing-certificate-transparency-for-phishing/references/api-reference.md new file mode 100644 index 0000000..e0a2544 --- /dev/null +++ b/personas/_shared/skills/analyzing-certificate-transparency-for-phishing/references/api-reference.md @@ -0,0 +1,97 @@ +# API Reference: Certificate Transparency Phishing Detection + +## crt.sh API + +### Search Certificates +```bash +# JSON output +curl "https://crt.sh/?q=%.example.com&output=json" + +# Exclude expired +curl "https://crt.sh/?q=%.example.com&output=json&exclude=expired" + +# Exact match +curl "https://crt.sh/?q=example.com&output=json" +``` + +### Response Fields +| Field | Description | +|-------|-------------| +| `id` | Certificate ID in crt.sh database | +| `common_name` | Certificate CN | +| `name_value` | All SANs (newline-separated) | +| `issuer_name` | Certificate Authority | +| `not_before` | Validity start | +| `not_after` | Validity end | +| `serial_number` | Certificate serial | + +## Certstream - Real-time CT Monitoring + +### Python Client +```python +import certstream + +def callback(message, context): + if message["message_type"] == "certificate_update": + data = message["data"] + domains = data["leaf_cert"]["all_domains"] + for domain in domains: + if "example" in domain: + print(f"[ALERT] {domain}") + +certstream.listen_for_events(callback, url="wss://certstream.calidog.io/") +``` + +### Message Fields +| Field | Path | +|-------|------| +| Domains | `data.leaf_cert.all_domains` | +| Issuer | `data.leaf_cert.issuer.O` | +| Subject | `data.leaf_cert.subject.CN` | +| Fingerprint | `data.leaf_cert.fingerprint` | +| Source | `data.source.name` | + +## CT Log Servers + +| Log | Operator | URL | +|-----|----------|-----| +| Argon | Google | `ct.googleapis.com/logs/argon2024` | +| Xenon | Google | `ct.googleapis.com/logs/xenon2024` | +| Nimbus | Cloudflare | `ct.cloudflare.com/logs/nimbus2024` | +| Oak | Let's Encrypt | `oak.ct.letsencrypt.org/2024h1` | +| Yeti | DigiCert | `yeti2024.ct.digicert.com/log` | + +## Phishing Detection Techniques + +### Homoglyph / IDN Attacks +| Original | Lookalike | Technique | +|----------|-----------|-----------| +| example.com | examp1e.com | Character substitution (l→1) | +| google.com | gооgle.com | Cyrillic о (U+043E) | +| paypal.com | paypa1.com | l→1 substitution | +| microsoft.com | mіcrosoft.com | Cyrillic і (U+0456) | + +### dnstwist Integration +```bash +dnstwist -r -f json example.com # Generate and resolve permutations +dnstwist -w wordlist.txt example.com # Dictionary-based +``` + +## Certificate Details Lookup +```bash +# Get full certificate from crt.sh +curl "https://crt.sh/?d=" + +# OpenSSL inspection +openssl s_client -connect domain.com:443 -servername domain.com /dev/null | \ + openssl x509 -noout -text +``` + +## Suspicious Indicators +| Pattern | Risk Level | +|---------|-----------| +| Free CA + new domain + brand keyword | HIGH | +| Wildcard cert on recently registered domain | HIGH | +| Multiple certs for slight domain variants | MEDIUM | +| IDN/punycode domain mimicking brand | HIGH | +| Cert issued same day as domain registration | MEDIUM | diff --git a/personas/_shared/skills/analyzing-certificate-transparency-for-phishing/scripts/agent.py b/personas/_shared/skills/analyzing-certificate-transparency-for-phishing/scripts/agent.py new file mode 100644 index 0000000..7ff7597 --- /dev/null +++ b/personas/_shared/skills/analyzing-certificate-transparency-for-phishing/scripts/agent.py @@ -0,0 +1,210 @@ +#!/usr/bin/env python3 +"""Certificate Transparency monitoring agent for phishing detection. + +Queries crt.sh for certificates matching target domains, detects lookalike +certificates, and identifies potential phishing infrastructure. +""" + +import json +import sys +from collections import defaultdict + +try: + import requests + HAS_REQUESTS = True +except ImportError: + HAS_REQUESTS = False + + +def query_crtsh(domain, wildcard=True, expired=False): + """Query crt.sh for certificates matching a domain.""" + if not HAS_REQUESTS: + return [] + query = f"%.{domain}" if wildcard else domain + params = {"q": query, "output": "json"} + if not expired: + params["exclude"] = "expired" + try: + resp = requests.get("https://crt.sh/", params=params, timeout=30) + resp.raise_for_status() + return resp.json() + except (requests.RequestException, json.JSONDecodeError) as e: + return [{"error": str(e)}] + + +def find_lookalike_domains(target_domain, ct_results): + """Identify certificates for domains that look similar to the target.""" + base = target_domain.split(".")[0].lower() + lookalikes = [] + for cert in ct_results: + cn = cert.get("common_name", "").lower() + names = cert.get("name_value", "").lower().split("\n") + for name in [cn] + names: + name = name.strip() + if not name or name == target_domain: + continue + similarity = calculate_similarity(base, name.split(".")[0]) + if similarity > 0.6 and name != target_domain: + lookalikes.append({ + "domain": name, + "similarity": round(similarity, 3), + "issuer": cert.get("issuer_name", ""), + "not_before": cert.get("not_before", ""), + "not_after": cert.get("not_after", ""), + "cert_id": cert.get("id"), + }) + seen = set() + unique = [] + for l in sorted(lookalikes, key=lambda x: -x["similarity"]): + if l["domain"] not in seen: + seen.add(l["domain"]) + unique.append(l) + return unique + + +def calculate_similarity(s1, s2): + """Calculate string similarity using Levenshtein-like ratio.""" + if s1 == s2: + return 1.0 + len1, len2 = len(s1), len(s2) + if len1 == 0 or len2 == 0: + return 0.0 + matrix = [[0] * (len2 + 1) for _ in range(len1 + 1)] + for i in range(len1 + 1): + matrix[i][0] = i + for j in range(len2 + 1): + matrix[0][j] = j + for i in range(1, len1 + 1): + for j in range(1, len2 + 1): + cost = 0 if s1[i-1] == s2[j-1] else 1 + matrix[i][j] = min(matrix[i-1][j] + 1, matrix[i][j-1] + 1, + matrix[i-1][j-1] + cost) + distance = matrix[len1][len2] + return 1.0 - distance / max(len1, len2) + + +HOMOGLYPH_MAP = { + "a": ["а", "@", "4"], "e": ["е", "3"], "o": ["о", "0"], + "i": ["і", "1", "l"], "l": ["1", "i", "I"], + "s": ["5", "$"], "t": ["7"], "g": ["9", "q"], +} + + +def detect_homoglyph_domains(target_domain, ct_results): + """Detect domains using homoglyph/IDN attacks against target.""" + findings = [] + base = target_domain.split(".")[0].lower() + for cert in ct_results: + names = cert.get("name_value", "").lower().split("\n") + for name in names: + name = name.strip() + if not name or name == target_domain: + continue + name_base = name.split(".")[0] + if len(name_base) == len(base): + diffs = sum(1 for a, b in zip(base, name_base) if a != b) + if 0 < diffs <= 2: + findings.append({ + "domain": name, + "char_differences": diffs, + "cert_id": cert.get("id"), + "issuer": cert.get("issuer_name", ""), + }) + return findings + + +def analyze_issuer_patterns(ct_results): + """Analyze certificate issuer patterns for anomalies.""" + issuer_counts = defaultdict(int) + free_cas = ["Let's Encrypt", "ZeroSSL", "Buypass"] + for cert in ct_results: + issuer = cert.get("issuer_name", "Unknown") + issuer_counts[issuer] += 1 + free_ca_certs = sum( + count for issuer, count in issuer_counts.items() + if any(ca.lower() in issuer.lower() for ca in free_cas) + ) + return { + "issuers": dict(issuer_counts), + "total_certs": len(ct_results), + "free_ca_count": free_ca_certs, + "free_ca_ratio": round(free_ca_certs / max(len(ct_results), 1), 3), + } + + +def detect_wildcard_abuse(ct_results): + """Detect suspicious wildcard certificate patterns.""" + wildcards = [] + for cert in ct_results: + cn = cert.get("common_name", "") + if cn.startswith("*."): + wildcards.append({ + "domain": cn, + "issuer": cert.get("issuer_name", ""), + "not_before": cert.get("not_before", ""), + }) + return wildcards + + +def generate_report(target_domain, ct_results): + """Generate comprehensive CT monitoring report.""" + lookalikes = find_lookalike_domains(target_domain, ct_results) + homoglyphs = detect_homoglyph_domains(target_domain, ct_results) + issuer_analysis = analyze_issuer_patterns(ct_results) + wildcards = detect_wildcard_abuse(ct_results) + + risk_score = 0 + risk_score += min(len(lookalikes) * 10, 40) + risk_score += min(len(homoglyphs) * 15, 30) + risk_score += 20 if issuer_analysis["free_ca_ratio"] > 0.8 else 0 + risk_score = min(risk_score, 100) + + return { + "target_domain": target_domain, + "total_certificates": len(ct_results), + "lookalike_domains": lookalikes[:20], + "homoglyph_domains": homoglyphs[:20], + "issuer_analysis": issuer_analysis, + "wildcard_certs": wildcards[:10], + "risk_score": risk_score, + "risk_level": "HIGH" if risk_score >= 60 else "MEDIUM" if risk_score >= 30 else "LOW", + } + + +if __name__ == "__main__": + print("=" * 60) + print("Certificate Transparency Phishing Detection Agent") + print("crt.sh queries, lookalike detection, homoglyph analysis") + print("=" * 60) + + domain = sys.argv[1] if len(sys.argv) > 1 else None + + if not domain: + print("\n[DEMO] Usage: python agent.py ") + print(" e.g. python agent.py example.com") + sys.exit(0) + + if not HAS_REQUESTS: + print("[!] Install requests: pip install requests") + sys.exit(1) + + print(f"\n[*] Querying crt.sh for: {domain}") + results = query_crtsh(domain) + print(f"[*] Found {len(results)} certificates") + + report = generate_report(domain, results) + + print(f"\n--- Lookalike Domains ({len(report['lookalike_domains'])}) ---") + for l in report["lookalike_domains"][:10]: + print(f" [{l['similarity']:.3f}] {l['domain']} (issuer: {l['issuer'][:40]})") + + print(f"\n--- Homoglyph Domains ({len(report['homoglyph_domains'])}) ---") + for h in report["homoglyph_domains"][:10]: + print(f" [diff={h['char_differences']}] {h['domain']}") + + print(f"\n--- Issuer Analysis ---") + for issuer, count in sorted(report["issuer_analysis"]["issuers"].items(), + key=lambda x: -x[1])[:5]: + print(f" {count:4d} | {issuer[:60]}") + + print(f"\n[*] Risk Score: {report['risk_score']}/100 ({report['risk_level']})") diff --git a/personas/_shared/skills/analyzing-cloud-storage-access-patterns/LICENSE b/personas/_shared/skills/analyzing-cloud-storage-access-patterns/LICENSE new file mode 100644 index 0000000..d885118 --- /dev/null +++ b/personas/_shared/skills/analyzing-cloud-storage-access-patterns/LICENSE @@ -0,0 +1,201 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to the Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by the Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding any notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. Please do not remove or change + the license header comment from a contributed file except when + necessary. + + Copyright 2026 mukul975 + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/personas/_shared/skills/analyzing-cloud-storage-access-patterns/SKILL.md b/personas/_shared/skills/analyzing-cloud-storage-access-patterns/SKILL.md new file mode 100644 index 0000000..a614987 --- /dev/null +++ b/personas/_shared/skills/analyzing-cloud-storage-access-patterns/SKILL.md @@ -0,0 +1,70 @@ +--- +name: analyzing-cloud-storage-access-patterns +description: Detect abnormal access patterns in AWS S3, GCS, and Azure Blob Storage by analyzing CloudTrail Data Events, GCS + audit logs, and Azure Storage Analytics. Identifies after-hours bulk downloads, access from new IP addresses, unusual API + calls (GetObject spikes), and potential data exfiltration using statistical baselines and time-series anomaly detection. +domain: cybersecurity +subdomain: cloud-security +tags: +- analyzing +- cloud +- storage +- access +version: '1.0' +author: mahipal +license: Apache-2.0 +atlas_techniques: +- AML.T0024 +- AML.T0056 +nist_ai_rmf: +- MEASURE-2.7 +- MAP-5.1 +- MANAGE-2.4 +nist_csf: +- PR.IR-01 +- ID.AM-08 +- GV.SC-06 +- DE.CM-01 +--- + + +# Analyzing Cloud Storage Access Patterns + + +## When to Use + +- When investigating security incidents that require analyzing cloud storage access patterns +- When building detection rules or threat hunting queries for this domain +- When SOC analysts need structured procedures for this analysis type +- When validating security monitoring coverage for related attack techniques + +## Prerequisites + +- Familiarity with cloud security concepts and tools +- Access to a test or lab environment for safe execution +- Python 3.8+ with required dependencies installed +- Appropriate authorization for any testing activities + +## Instructions + +1. Install dependencies: `pip install boto3 requests` +2. Query CloudTrail for S3 Data Events using AWS CLI or boto3. +3. Build access baselines: hourly request volume, per-user object counts, source IP history. +4. Detect anomalies: + - After-hours access (outside 8am-6pm local time) + - Bulk downloads: >100 GetObject calls from single principal in 1 hour + - New source IPs not seen in the prior 30 days + - ListBucket enumeration spikes (reconnaissance indicator) +5. Generate prioritized findings report. + +```bash +python scripts/agent.py --bucket my-sensitive-data --hours-back 24 --output s3_access_report.json +``` + +## Examples + +### CloudTrail S3 Data Event +```json +{"eventName": "GetObject", "requestParameters": {"bucketName": "sensitive-data", "key": "financials/q4.xlsx"}, + "sourceIPAddress": "203.0.113.50", "userIdentity": {"arn": "arn:aws:iam::123456789012:user/analyst"}} +``` diff --git a/personas/_shared/skills/analyzing-cloud-storage-access-patterns/references/api-reference.md b/personas/_shared/skills/analyzing-cloud-storage-access-patterns/references/api-reference.md new file mode 100644 index 0000000..b446bf1 --- /dev/null +++ b/personas/_shared/skills/analyzing-cloud-storage-access-patterns/references/api-reference.md @@ -0,0 +1,49 @@ +# API Reference: Cloud Storage Access Pattern Analysis + +## AWS CLI - CloudTrail Lookup +```bash +aws cloudtrail lookup-events \ + --lookup-attributes AttributeKey=ResourceType,AttributeValue=AWS::S3::Object \ + --start-time 2024-01-15T00:00:00Z \ + --output json +``` + +## CloudTrail S3 Data Event Structure +```json +{ + "EventTime": "2024-01-15T10:30:00Z", + "EventName": "GetObject", + "Username": "analyst", + "CloudTrailEvent": "{\"sourceIPAddress\":\"10.0.0.1\",\"userAgent\":\"aws-cli\",\"requestParameters\":{\"bucketName\":\"data\",\"key\":\"file.csv\"},\"userIdentity\":{\"arn\":\"arn:aws:iam::123:user/analyst\"}}" +} +``` + +## Key S3 Event Names +| Event | Meaning | +|-------|---------| +| GetObject | Object download | +| PutObject | Object upload | +| DeleteObject | Object deletion | +| ListBucket / ListObjectsV2 | Bucket enumeration | +| GetBucketPolicy | Policy read | +| PutBucketPolicy | Policy modification | + +## Detection Thresholds +| Anomaly | Threshold | Severity | +|---------|-----------|----------| +| Bulk download | >100 GetObject/hr per user | Critical | +| After-hours | Access outside 08:00-18:00 UTC | Medium | +| New source IP | IP not in 30-day baseline | High | +| Enumeration | >20 ListBucket per user | High | + +## boto3 CloudTrail Client (alternative) +```python +import boto3 +client = boto3.client("cloudtrail") +response = client.lookup_events( + LookupAttributes=[{"AttributeKey":"ResourceType","AttributeValue":"AWS::S3::Object"}], + StartTime=datetime(2024,1,15), + MaxResults=50 +) +events = response["Events"] +``` diff --git a/personas/_shared/skills/analyzing-cloud-storage-access-patterns/scripts/agent.py b/personas/_shared/skills/analyzing-cloud-storage-access-patterns/scripts/agent.py new file mode 100644 index 0000000..ec818c0 --- /dev/null +++ b/personas/_shared/skills/analyzing-cloud-storage-access-patterns/scripts/agent.py @@ -0,0 +1,200 @@ +#!/usr/bin/env python3 +"""Cloud Storage Access Pattern Analyzer - Detects abnormal S3/GCS/Azure Blob access via CloudTrail.""" + +import json +import logging +import argparse +import subprocess +from collections import defaultdict +from datetime import datetime, timedelta + +logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s") +logger = logging.getLogger(__name__) + + +def query_cloudtrail_s3_events(bucket_name, hours_back=24): + """Query CloudTrail for S3 data events on a specific bucket.""" + start_time = (datetime.utcnow() - timedelta(hours=hours_back)).strftime("%Y-%m-%dT%H:%M:%SZ") + cmd = [ + "aws", "cloudtrail", "lookup-events", + "--lookup-attributes", f"AttributeKey=ResourceType,AttributeValue=AWS::S3::Object", + "--start-time", start_time, + "--output", "json", + ] + result = subprocess.run(cmd, capture_output=True, text=True, timeout=120) + if result.returncode != 0: + logger.error("CloudTrail query failed: %s", result.stderr[:200]) + return [] + events = json.loads(result.stdout).get("Events", []) + s3_events = [] + for event in events: + ct_event = json.loads(event.get("CloudTrailEvent", "{}")) + req_params = ct_event.get("requestParameters", {}) + if req_params.get("bucketName") == bucket_name or not bucket_name: + s3_events.append({ + "timestamp": event.get("EventTime", ""), + "event_name": event.get("EventName", ""), + "username": event.get("Username", ""), + "source_ip": ct_event.get("sourceIPAddress", ""), + "user_agent": ct_event.get("userAgent", ""), + "bucket": req_params.get("bucketName", ""), + "key": req_params.get("key", ""), + "user_arn": ct_event.get("userIdentity", {}).get("arn", ""), + }) + logger.info("Retrieved %d S3 events for bucket '%s'", len(s3_events), bucket_name or "all") + return s3_events + + +def detect_bulk_downloads(events, threshold=100): + """Detect bulk GetObject operations from a single principal.""" + user_downloads = defaultdict(list) + for event in events: + if event["event_name"] == "GetObject": + user_downloads[event["user_arn"]].append(event) + alerts = [] + for user_arn, downloads in user_downloads.items(): + if len(downloads) >= threshold: + keys = [d["key"] for d in downloads] + alerts.append({ + "user_arn": user_arn, + "download_count": len(downloads), + "unique_keys": len(set(keys)), + "source_ips": list({d["source_ip"] for d in downloads}), + "first_access": downloads[0]["timestamp"], + "last_access": downloads[-1]["timestamp"], + "severity": "critical", + "indicator": "Bulk download (potential exfiltration)", + }) + logger.info("Found %d bulk download alerts", len(alerts)) + return alerts + + +def detect_after_hours_access(events, business_start=8, business_end=18): + """Detect access outside business hours.""" + after_hours = [] + for event in events: + try: + ts = event["timestamp"] + if isinstance(ts, str): + dt = datetime.fromisoformat(ts.replace("Z", "+00:00")) + else: + dt = ts + hour = dt.hour + if hour < business_start or hour >= business_end: + event["indicator"] = f"After-hours access at {hour:02d}:00 UTC" + event["severity"] = "medium" + after_hours.append(event) + except (ValueError, AttributeError): + continue + logger.info("Found %d after-hours access events", len(after_hours)) + return after_hours + + +def detect_new_source_ips(events, known_ips=None): + """Detect access from IP addresses not in the known baseline.""" + if known_ips is None: + known_ips = set() + new_ip_events = [] + for event in events: + ip = event["source_ip"] + if ip and ip not in known_ips and not ip.startswith("AWS Internal"): + event["indicator"] = f"New source IP: {ip}" + event["severity"] = "high" + new_ip_events.append(event) + unique_new = len({e["source_ip"] for e in new_ip_events}) + logger.info("Found %d events from %d new source IPs", len(new_ip_events), unique_new) + return new_ip_events + + +def detect_enumeration(events, threshold=20): + """Detect ListBucket/ListObjects enumeration patterns.""" + user_listings = defaultdict(int) + for event in events: + if event["event_name"] in ("ListBucket", "ListObjects", "ListObjectsV2"): + user_listings[event["user_arn"]] += 1 + alerts = [] + for user_arn, count in user_listings.items(): + if count >= threshold: + alerts.append({ + "user_arn": user_arn, + "list_count": count, + "severity": "high", + "indicator": "Bucket enumeration spike (reconnaissance)", + }) + return alerts + + +def build_access_baseline(events): + """Build statistical baseline of normal access patterns.""" + hourly_counts = defaultdict(int) + user_counts = defaultdict(int) + ip_set = set() + for event in events: + try: + ts = event["timestamp"] + if isinstance(ts, str): + dt = datetime.fromisoformat(ts.replace("Z", "+00:00")) + hourly_counts[dt.hour] += 1 + except (ValueError, AttributeError): + pass + user_counts[event["user_arn"]] += 1 + if event["source_ip"]: + ip_set.add(event["source_ip"]) + return { + "hourly_distribution": dict(hourly_counts), + "user_request_counts": dict(user_counts), + "known_ips": list(ip_set), + "total_events": len(events), + } + + +def generate_report(events, bulk_alerts, after_hours, new_ips, enum_alerts, baseline): + """Generate cloud storage access analysis report.""" + report = { + "timestamp": datetime.utcnow().isoformat(), + "total_events_analyzed": len(events), + "bulk_download_alerts": bulk_alerts, + "after_hours_access": len(after_hours), + "new_source_ip_events": len(new_ips), + "enumeration_alerts": enum_alerts, + "baseline_summary": { + "known_ips": len(baseline.get("known_ips", [])), + "total_baseline_events": baseline.get("total_events", 0), + }, + "sample_after_hours": after_hours[:10], + "sample_new_ips": new_ips[:10], + } + total_alerts = len(bulk_alerts) + len(enum_alerts) + (1 if new_ips else 0) + print(f"CLOUD STORAGE REPORT: {len(events)} events, {total_alerts} alerts") + return report + + +def main(): + parser = argparse.ArgumentParser(description="Cloud Storage Access Pattern Analyzer") + parser.add_argument("--bucket", default="", help="S3 bucket name to analyze") + parser.add_argument("--hours-back", type=int, default=24) + parser.add_argument("--bulk-threshold", type=int, default=100) + parser.add_argument("--known-ips-file", help="File with known IP baselines") + parser.add_argument("--output", default="s3_access_report.json") + args = parser.parse_args() + + events = query_cloudtrail_s3_events(args.bucket, args.hours_back) + baseline = build_access_baseline(events) + known_ips = set(baseline.get("known_ips", [])) + if args.known_ips_file: + with open(args.known_ips_file) as f: + known_ips.update(line.strip() for line in f if line.strip()) + + bulk_alerts = detect_bulk_downloads(events, args.bulk_threshold) + after_hours = detect_after_hours_access(events) + new_ips = detect_new_source_ips(events, known_ips) + enum_alerts = detect_enumeration(events) + + report = generate_report(events, bulk_alerts, after_hours, new_ips, enum_alerts, baseline) + with open(args.output, "w") as f: + json.dump(report, f, indent=2, default=str) + logger.info("Report saved to %s", args.output) + + +if __name__ == "__main__": + main() diff --git a/personas/_shared/skills/analyzing-cobalt-strike-beacon-configuration/LICENSE b/personas/_shared/skills/analyzing-cobalt-strike-beacon-configuration/LICENSE new file mode 100644 index 0000000..d885118 --- /dev/null +++ b/personas/_shared/skills/analyzing-cobalt-strike-beacon-configuration/LICENSE @@ -0,0 +1,201 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to the Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by the Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding any notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. Please do not remove or change + the license header comment from a contributed file except when + necessary. + + Copyright 2026 mukul975 + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/personas/_shared/skills/analyzing-cobalt-strike-beacon-configuration/SKILL.md b/personas/_shared/skills/analyzing-cobalt-strike-beacon-configuration/SKILL.md new file mode 100644 index 0000000..ebc4c11 --- /dev/null +++ b/personas/_shared/skills/analyzing-cobalt-strike-beacon-configuration/SKILL.md @@ -0,0 +1,381 @@ +--- +name: analyzing-cobalt-strike-beacon-configuration +description: Extract and analyze Cobalt Strike beacon configuration from PE files and memory dumps to identify C2 infrastructure, + malleable profiles, and operator tradecraft. +domain: cybersecurity +subdomain: malware-analysis +tags: +- cobalt-strike +- beacon +- c2 +- malware-analysis +- config-extraction +- threat-hunting +- red-team-tools +version: '1.0' +author: mahipal +license: Apache-2.0 +nist_csf: +- DE.AE-02 +- RS.AN-03 +- ID.RA-01 +- DE.CM-01 +--- +# Analyzing Cobalt Strike Beacon Configuration + +## Overview + +Cobalt Strike is a commercial adversary simulation tool widely abused by threat actors for post-exploitation operations. Beacon payloads contain embedded configuration data that reveals C2 server addresses, communication protocols, sleep intervals, jitter values, malleable C2 profile settings, watermark identifiers, and encryption keys. Extracting this configuration from PE files, shellcode, or memory dumps is critical for incident responders to map attacker infrastructure and attribute campaigns. The beacon configuration is XOR-encoded using a single byte (0x69 for version 3, 0x2e for version 4) and stored in a Type-Length-Value (TLV) format within the .data section. + + +## When to Use + +- When investigating security incidents that require analyzing cobalt strike beacon configuration +- When building detection rules or threat hunting queries for this domain +- When SOC analysts need structured procedures for this analysis type +- When validating security monitoring coverage for related attack techniques + +## Prerequisites + +- Python 3.9+ with `dissect.cobaltstrike`, `pefile`, `yara-python` +- SentinelOne CobaltStrikeParser (`parse_beacon_config.py`) +- Hex editor (010 Editor, HxD) for manual inspection +- Understanding of PE file format and XOR encoding +- Memory dump acquisition tools (Volatility3, WinDbg) +- Network analysis tools (Wireshark) for C2 traffic correlation + +## Key Concepts + +### Beacon Configuration Structure + +Cobalt Strike beacons store their configuration as a blob of TLV (Type-Length-Value) entries within the .data section of the PE. Stageless beacons XOR the entire beacon code with a 4-byte key. The configuration blob itself uses a single-byte XOR key. Each TLV entry contains a 2-byte type identifier (e.g., 0x0001 for BeaconType, 0x0008 for C2Server), a 2-byte length, and variable-length data. + +### Malleable C2 Profiles + +The beacon configuration encodes the malleable C2 profile that dictates HTTP request/response transformations, including URI paths, headers, metadata encoding (Base64, NetBIOS), and data transforms. Analyzing these settings reveals how the beacon disguises its traffic to blend with legitimate web traffic. + +### Watermark and License Identification + +Each Cobalt Strike license embeds a unique watermark (4-byte integer) into generated beacons. Extracting the watermark can link multiple beacons to the same operator or cracked license. Known watermark databases maintained by threat intelligence providers map watermarks to specific threat actors or leaked license keys. + +## Workflow + +### Step 1: Extract Configuration with CobaltStrikeParser + +```python +#!/usr/bin/env python3 +"""Extract Cobalt Strike beacon config from PE or memory dump.""" +import sys +import json + +# Using SentinelOne's CobaltStrikeParser +# pip install dissect.cobaltstrike +from dissect.cobaltstrike.beacon import BeaconConfig + +def extract_beacon_config(filepath): + """Parse beacon configuration from file.""" + configs = list(BeaconConfig.from_path(filepath)) + + if not configs: + print(f"[-] No beacon configuration found in {filepath}") + return None + + for i, config in enumerate(configs): + print(f"\n[+] Beacon Configuration #{i+1}") + print(f"{'='*60}") + + settings = config.as_dict() + + # Critical fields for incident response + critical_fields = [ + "SETTING_C2_REQUEST", + "SETTING_C2_RECOVER", + "SETTING_PUBKEY", + "SETTING_DOMAINS", + "SETTING_BEACONTYPE", + "SETTING_PORT", + "SETTING_SLEEPTIME", + "SETTING_JITTER", + "SETTING_MAXGET", + "SETTING_SPAWNTO_X86", + "SETTING_SPAWNTO_X64", + "SETTING_PIPENAME", + "SETTING_WATERMARK", + "SETTING_C2_VERB_GET", + "SETTING_C2_VERB_POST", + "SETTING_USERAGENT", + "SETTING_PROTOCOL", + ] + + for field in critical_fields: + value = settings.get(field, "N/A") + print(f" {field}: {value}") + + return settings + + return None + + +def extract_c2_indicators(config): + """Extract actionable C2 indicators from beacon config.""" + indicators = { + "c2_domains": [], + "c2_ips": [], + "c2_urls": [], + "user_agent": "", + "named_pipes": [], + "spawn_processes": [], + "watermark": "", + } + + if not config: + return indicators + + # Extract C2 domains + domains = config.get("SETTING_DOMAINS", "") + if domains: + for domain in str(domains).split(","): + domain = domain.strip().rstrip("/") + if domain: + indicators["c2_domains"].append(domain) + + # Extract user agent + indicators["user_agent"] = str(config.get("SETTING_USERAGENT", "")) + + # Extract named pipes + pipe = config.get("SETTING_PIPENAME", "") + if pipe: + indicators["named_pipes"].append(str(pipe)) + + # Extract spawn-to processes + for arch in ["SETTING_SPAWNTO_X86", "SETTING_SPAWNTO_X64"]: + proc = config.get(arch, "") + if proc: + indicators["spawn_processes"].append(str(proc)) + + # Extract watermark + indicators["watermark"] = str(config.get("SETTING_WATERMARK", "")) + + return indicators + + +if __name__ == "__main__": + if len(sys.argv) < 2: + print(f"Usage: {sys.argv[0]} ") + sys.exit(1) + + config = extract_beacon_config(sys.argv[1]) + if config: + indicators = extract_c2_indicators(config) + print(f"\n[+] Extracted C2 Indicators:") + print(json.dumps(indicators, indent=2)) +``` + +### Step 2: Manual XOR Decryption of Beacon Config + +```python +import struct + +def find_and_decrypt_config(data): + """Manually locate and decrypt beacon configuration.""" + # Cobalt Strike 4.x uses 0x2e as XOR key + xor_keys = [0x2e, 0x69] # v4, v3 + + for xor_key in xor_keys: + # Search for the config magic bytes after XOR + # Config starts with 0x0001 (BeaconType) XOR'd with key + magic = bytes([0x00 ^ xor_key, 0x01 ^ xor_key, + 0x00 ^ xor_key, 0x02 ^ xor_key]) + + offset = data.find(magic) + if offset == -1: + continue + + print(f"[+] Found config at offset 0x{offset:x} (XOR key: 0x{xor_key:02x})") + + # Decrypt the config blob (typically 4096 bytes) + config_size = 4096 + encrypted = data[offset:offset + config_size] + decrypted = bytes([b ^ xor_key for b in encrypted]) + + # Parse TLV entries + entries = parse_tlv(decrypted) + return entries + + return None + + +def parse_tlv(data): + """Parse Type-Length-Value configuration entries.""" + entries = {} + offset = 0 + + # TLV field type mapping + field_names = { + 0x0001: "BeaconType", + 0x0002: "Port", + 0x0003: "SleepTime", + 0x0004: "MaxGetSize", + 0x0005: "Jitter", + 0x0006: "MaxDNS", + 0x0007: "Deprecated_PublicKey", + 0x0008: "C2Server", + 0x0009: "UserAgent", + 0x000a: "PostURI", + 0x000b: "Malleable_C2_Instructions", + 0x000c: "Deprecated_HttpGet_Metadata", + 0x000d: "SpawnTo_x86", + 0x000e: "SpawnTo_x64", + 0x000f: "CryptoScheme", + 0x001a: "Watermark", + 0x001d: "C2_HostHeader", + 0x0024: "PipeName", + 0x0025: "Year", + 0x0026: "Month", + 0x0027: "Day", + 0x0036: "ProxyHostname", + } + + while offset + 6 <= len(data): + entry_type = struct.unpack(">H", data[offset:offset+2])[0] + entry_len_type = struct.unpack(">H", data[offset+2:offset+4])[0] + entry_len = struct.unpack(">H", data[offset+4:offset+6])[0] + + if entry_type == 0: + break + + value_start = offset + 6 + value_end = value_start + entry_len + value_data = data[value_start:value_end] + + field_name = field_names.get(entry_type, f"Unknown_0x{entry_type:04x}") + + if entry_len_type == 1: # Short + value = struct.unpack(">H", value_data[:2])[0] + elif entry_len_type == 2: # Int + value = struct.unpack(">I", value_data[:4])[0] + elif entry_len_type == 3: # String/Blob + value = value_data.rstrip(b'\x00').decode('utf-8', errors='replace') + else: + value = value_data.hex() + + entries[field_name] = value + print(f" {field_name}: {value}") + + offset = value_end + + return entries +``` + +### Step 3: YARA Rule for Beacon Detection + +```python +import yara + +cobalt_strike_rule = """ +rule CobaltStrike_Beacon_Config { + meta: + description = "Detects Cobalt Strike beacon configuration" + author = "Malware Analysis Team" + date = "2025-01-01" + + strings: + // XOR'd config marker for CS 4.x (key 0x2e) + $config_v4 = { 2e 2f 2e 2c } + + // XOR'd config marker for CS 3.x (key 0x69) + $config_v3 = { 69 68 69 6b } + + // Common beacon strings + $str_pipe = "\\\\.\\pipe\\" ascii wide + $str_beacon = "beacon" ascii nocase + $str_sleeptime = "sleeptime" ascii nocase + + // Reflective loader pattern + $reflective = { 4D 5A 41 52 55 48 89 E5 } + + condition: + ($config_v4 or $config_v3) or + (2 of ($str_*) and $reflective) +} +""" + +def scan_for_beacons(filepath): + """Scan file with YARA rules for Cobalt Strike beacons.""" + rules = yara.compile(source=cobalt_strike_rule) + matches = rules.match(filepath) + + for match in matches: + print(f"[+] YARA Match: {match.rule}") + for string_match in match.strings: + offset = string_match.instances[0].offset + print(f" String: {string_match.identifier} at offset 0x{offset:x}") + + return matches +``` + +### Step 4: Network Traffic Correlation + +```python +from dissect.cobaltstrike.c2 import HttpC2Config + +def analyze_c2_profile(beacon_config): + """Analyze malleable C2 profile from beacon configuration.""" + print("\n[+] Malleable C2 Profile Analysis") + print("=" * 60) + + # HTTP GET configuration + get_verb = beacon_config.get("SETTING_C2_VERB_GET", "GET") + get_uri = beacon_config.get("SETTING_C2_REQUEST", "") + print(f"\n HTTP GET Request:") + print(f" Verb: {get_verb}") + print(f" URI: {get_uri}") + + # HTTP POST configuration + post_verb = beacon_config.get("SETTING_C2_VERB_POST", "POST") + post_uri = beacon_config.get("SETTING_C2_POSTREQ", "") + print(f"\n HTTP POST Request:") + print(f" Verb: {post_verb}") + print(f" URI: {post_uri}") + + # User Agent + ua = beacon_config.get("SETTING_USERAGENT", "") + print(f"\n User-Agent: {ua}") + + # Host header + host = beacon_config.get("SETTING_C2_HOSTHEADER", "") + print(f" Host Header: {host}") + + # Sleep and jitter for traffic pattern + sleep_ms = beacon_config.get("SETTING_SLEEPTIME", 60000) + jitter = beacon_config.get("SETTING_JITTER", 0) + print(f"\n Sleep Time: {sleep_ms}ms") + print(f" Jitter: {jitter}%") + + # Generate Suricata/Snort signatures + print(f"\n[+] Suggested Network Signatures:") + if ua: + print(f' alert http any any -> any any (msg:"CS Beacon UA"; ' + f'content:"{ua}"; http_user_agent; sid:1000001; rev:1;)') + if get_uri: + print(f' alert http any any -> any any (msg:"CS Beacon URI"; ' + f'content:"{get_uri}"; http_uri; sid:1000002; rev:1;)') +``` + +## Validation Criteria + +- Beacon configuration successfully extracted from PE file or memory dump +- C2 server domains/IPs correctly identified with port and protocol +- Malleable C2 profile parameters decoded showing HTTP transforms +- Watermark value extracted for attribution correlation +- Sleep time and jitter values match observed network beacon intervals +- YARA rules detect beacon in both packed and unpacked samples +- Network signatures generated from extracted C2 profile + +## References + +- [SentinelOne CobaltStrikeParser](https://github.com/Sentinel-One/CobaltStrikeParser) +- [dissect.cobaltstrike Library](https://github.com/fox-it/dissect.cobaltstrike) +- [SentinelLabs Beacon Configuration Analysis](https://www.sentinelone.com/labs/the-anatomy-of-an-apt-attack-and-cobaltstrike-beacons-encoded-configuration/) +- [Cobalt Strike Staging and Config Extraction](https://blog.securehat.co.uk/cobaltstrike/extracting-config-from-cobaltstrike-stager-shellcode) +- [MITRE ATT&CK - Cobalt Strike S0154](https://attack.mitre.org/software/S0154/) diff --git a/personas/_shared/skills/analyzing-cobalt-strike-beacon-configuration/assets/template.md b/personas/_shared/skills/analyzing-cobalt-strike-beacon-configuration/assets/template.md new file mode 100644 index 0000000..2f53170 --- /dev/null +++ b/personas/_shared/skills/analyzing-cobalt-strike-beacon-configuration/assets/template.md @@ -0,0 +1,95 @@ +# Cobalt Strike Beacon Analysis Report Template + +## Report Metadata +| Field | Value | +|-------|-------| +| Report ID | CS-BEACON-YYYY-NNNN | +| Date | YYYY-MM-DD | +| Sample Hash (SHA-256) | | +| Classification | TLP:AMBER | +| Analyst | | + +## Beacon Configuration Summary + +| Setting | Value | +|---------|-------| +| Beacon Type | HTTP / HTTPS / SMB / DNS | +| C2 Server(s) | | +| Port | | +| Sleep Time | ms | +| Jitter | % | +| User-Agent | | +| Watermark | | +| SpawnTo (x86) | | +| SpawnTo (x64) | | +| Named Pipe | | +| Host Header | | +| Crypto Scheme | | + +## C2 Infrastructure + +| Indicator | Type | Value | Context | +|-----------|------|-------|---------| +| C2 Domain | domain | | Primary callback | +| C2 IP | ip | | Resolved address | +| URI Path (GET) | uri | | Beacon check-in | +| URI Path (POST) | uri | | Data exfiltration | + +## Malleable C2 Profile + +### HTTP GET Configuration +| Parameter | Value | +|-----------|-------| +| URI | | +| Verb | | +| Headers | | +| Metadata Encoding | | + +### HTTP POST Configuration +| Parameter | Value | +|-----------|-------| +| URI | | +| Verb | | +| ID Encoding | | +| Output Encoding | | + +## Watermark Attribution + +| Watermark | Known Association | Confidence | +|-----------|------------------|------------| +| | Cracked / Licensed / Threat Actor | High/Med/Low | + +## Network Detection Signatures + +``` +# Suricata signature for beacon C2 traffic +alert http $HOME_NET any -> $EXTERNAL_NET any ( + msg:"Cobalt Strike Beacon C2 Communication"; + content:"[USER_AGENT]"; http_user_agent; + content:"[URI_PATH]"; http_uri; + sid:1000001; rev:1; +) +``` + +## YARA Detection Rule + +```yara +rule CobaltStrike_Beacon_[CAMPAIGN] { + meta: + description = "Detects Cobalt Strike beacon from [CAMPAIGN]" + hash = "[SHA256]" + strings: + $c2 = "[C2_DOMAIN]" ascii + $pipe = "[NAMED_PIPE]" ascii + $ua = "[USER_AGENT]" ascii + condition: + 2 of them +} +``` + +## Recommendations + +1. **Block**: Add C2 domains/IPs to firewall deny lists +2. **Hunt**: Search for named pipe and spawn-to process in endpoint logs +3. **Detect**: Deploy YARA and network signatures to detection stack +4. **Correlate**: Check watermark against threat intelligence databases diff --git a/personas/_shared/skills/analyzing-cobalt-strike-beacon-configuration/references/api-reference.md b/personas/_shared/skills/analyzing-cobalt-strike-beacon-configuration/references/api-reference.md new file mode 100644 index 0000000..f2e192e --- /dev/null +++ b/personas/_shared/skills/analyzing-cobalt-strike-beacon-configuration/references/api-reference.md @@ -0,0 +1,112 @@ +# API Reference: Cobalt Strike Beacon Configuration Analysis + +## Beacon Config TLV Format + +### Structure +``` +[Field ID: 2 bytes][Type: 2 bytes][Value: variable] +Type 1 = short (2 bytes), Type 2 = int (4 bytes), Type 3 = string/blob (2-byte length + data) +``` + +### XOR Encoding +| Version | XOR Key | +|---------|---------| +| CS 3.x | `0x69` | +| CS 4.x | `0x2E` | + +### Key Configuration Fields +| ID | Name | Description | +|----|------|-------------| +| 1 | BeaconType | 0=HTTP, 1=Hybrid, 2=SMB, 8=HTTPS | +| 2 | Port | C2 communication port | +| 3 | SleepTime | Beacon interval (ms) | +| 5 | Jitter | Random sleep variation (%) | +| 7 | PublicKey | RSA public key for encryption | +| 8 | C2Server | Command and control server(s) | +| 9 | UserAgent | HTTP User-Agent string | +| 10 | PostURI | POST callback URI | +| 37 | Watermark | License watermark (operator ID) | +| 54 | PipeName | Named pipe for SMB beacons | + +## 1768.py (Didier Stevens) - Config Extractor + +### Syntax +```bash +python 1768.py # Extract config +python 1768.py -j # JSON output +python 1768.py -r # Raw config dump +``` + +## CobaltStrikeParser (SentinelOne) + +### Syntax +```bash +python parse_beacon_config.py +python parse_beacon_config.py --json +``` + +### Output Fields +``` +BeaconType: HTTPS +Port: 443 +SleepTime: 60000 +Jitter: 37 +C2Server: update.microsoft-cdn.com,/api/v2 +UserAgent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) +Watermark: 305419896 +SpawnToX86: %windir%\syswow64\dllhost.exe +SpawnToX64: %windir%\sysnative\dllhost.exe +``` + +## JARM Fingerprinting + +### Cobalt Strike Default JARM +```bash +# Default CS JARM hash (pre-4.7) +07d14d16d21d21d07c42d41d00041d24a458a375eef0c576d23a7bab9a9fb1 + +# Scan with JARM +python jarm.py -p 443 +``` + +## Known Watermark Values +| Watermark | Attribution | +|-----------|------------| +| 0 | Trial/cracked version | +| 305419896 | Common cracked version | +| 1359593325 | Known threat actor toolkit | +| 1580103824 | Known APT usage | + +## Detection Signatures + +### Suricata +``` +alert http $HOME_NET any -> $EXTERNAL_NET any ( + msg:"ET MALWARE Cobalt Strike Beacon"; + content:"/submit.php"; http_uri; + content:"Cookie:"; http_header; + pcre:"/Cookie:\s[A-Za-z0-9+/=]{60,}/H"; + sid:2028591; rev:1;) +``` + +### YARA +```yara +rule CobaltStrike_Beacon { + strings: + $config_v3 = { 00 01 00 01 00 02 ?? ?? 00 01 00 02 } + $magic = "MSSE-%d-server" + $pipe = "\\\\.\\pipe\\msagent_" + condition: + uint16(0) == 0x5A4D and any of them +} +``` + +## Malleable C2 Profile Elements +| Element | Description | +|---------|-------------| +| `http-get` | GET request profile (URI, headers, metadata transform) | +| `http-post` | POST request profile (URI, body transform) | +| `set sleeptime` | Default beacon interval | +| `set jitter` | Randomization percentage | +| `set useragent` | HTTP User-Agent | +| `set pipename` | SMB named pipe name | diff --git a/personas/_shared/skills/analyzing-cobalt-strike-beacon-configuration/references/standards.md b/personas/_shared/skills/analyzing-cobalt-strike-beacon-configuration/references/standards.md new file mode 100644 index 0000000..2c9c8ff --- /dev/null +++ b/personas/_shared/skills/analyzing-cobalt-strike-beacon-configuration/references/standards.md @@ -0,0 +1,94 @@ +# Standards and Frameworks Reference + +## Cobalt Strike Beacon Configuration Fields + +### Configuration TLV Types +| Type ID | Field Name | Data Type | Description | +|---------|-----------|-----------|-------------| +| 0x0001 | BeaconType | Short | 0=HTTP, 1=Hybrid HTTP/DNS, 8=HTTPS, 10=TCP Bind | +| 0x0002 | Port | Short | C2 communication port | +| 0x0003 | SleepTime | Int | Beacon callback interval in milliseconds | +| 0x0005 | Jitter | Short | Percentage of sleep time randomization (0-99) | +| 0x0008 | C2Server | String | Comma-separated C2 domains/IPs | +| 0x0009 | UserAgent | String | HTTP User-Agent header value | +| 0x000a | PostURI | String | URI for HTTP POST requests | +| 0x000d | SpawnTo_x86 | String | 32-bit process to spawn for post-ex | +| 0x000e | SpawnTo_x64 | String | 64-bit process to spawn for post-ex | +| 0x001a | Watermark | Int | License watermark identifier | +| 0x0024 | PipeName | String | Named pipe for SMB beacon | +| 0x001d | HostHeader | String | HTTP Host header value | +| 0x0032 | ProxyHostname | String | Proxy server address | + +### XOR Encoding Scheme +- **Cobalt Strike 3.x**: XOR key = 0x69 +- **Cobalt Strike 4.x**: XOR key = 0x2e +- Configuration blob size: 4096 bytes (typical) +- Encoding: Single-byte XOR across entire config blob + +### Stageless Beacon Structure +- PE with beacon code in .data section +- 4-byte XOR key applied to .data section content +- Configuration embedded after beacon code +- Reflective DLL loader prepended to beacon + +## MITRE ATT&CK Mappings + +### Cobalt Strike Techniques (S0154) +| Technique | ID | Description | +|-----------|-----|------------| +| Application Layer Protocol | T1071.001 | HTTP/HTTPS C2 communication | +| Encrypted Channel | T1573.002 | AES-256 encrypted C2 | +| Ingress Tool Transfer | T1105 | Download additional payloads | +| Process Injection | T1055 | Inject into spawned processes | +| Named Pipes | T1570 | SMB beacon lateral movement | +| Service Execution | T1569.002 | PSExec-style lateral movement | +| Reflective Code Loading | T1620 | In-memory beacon loading | + +## Malleable C2 Profile Structure + +### HTTP GET Block +``` +http-get { + set uri "/path"; + client { + header "Accept" "text/html"; + metadata { + base64url; + prepend "session="; + header "Cookie"; + } + } + server { + header "Content-Type" "text/html"; + output { + print; + } + } +} +``` + +### HTTP POST Block +``` +http-post { + set uri "/submit"; + client { + id { + uri-append; + } + output { + base64; + print; + } + } + server { + output { + print; + } + } +} +``` + +## References +- [Cobalt Strike Documentation](https://hstechdocs.helpsystems.com/manuals/cobaltstrike/) +- [Malleable C2 Profile Reference](https://hstechdocs.helpsystems.com/manuals/cobaltstrike/current/userguide/content/topics/malleable-c2_main.htm) +- [MITRE ATT&CK Cobalt Strike](https://attack.mitre.org/software/S0154/) diff --git a/personas/_shared/skills/analyzing-cobalt-strike-beacon-configuration/references/workflows.md b/personas/_shared/skills/analyzing-cobalt-strike-beacon-configuration/references/workflows.md new file mode 100644 index 0000000..c0ccd1b --- /dev/null +++ b/personas/_shared/skills/analyzing-cobalt-strike-beacon-configuration/references/workflows.md @@ -0,0 +1,72 @@ +# Cobalt Strike Beacon Analysis Workflows + +## Workflow 1: PE File Configuration Extraction + +``` +[Suspicious PE] --> [Unpack if packed] --> [Locate .data section] --> [XOR Decrypt] + | + v + [Parse TLV Config] + | + v + [Extract C2 Indicators] +``` + +### Steps: +1. **Triage**: Identify file as potential Cobalt Strike beacon via YARA or AV detection +2. **Unpacking**: If packed, unpack using appropriate tool (UPX, custom unpacker) +3. **Section Analysis**: Locate .data section containing XOR'd beacon code +4. **XOR Key Discovery**: Try known keys (0x2e, 0x69) or brute-force 4-byte key +5. **Config Parsing**: Parse decrypted TLV entries for C2 and operational settings +6. **IOC Extraction**: Extract domains, IPs, URIs, user agents, watermarks + +## Workflow 2: Memory Dump Beacon Extraction + +``` +[Memory Dump] --> [Volatility3 malfind] --> [Dump Injected Regions] --> [Parse Config] + | + v + [C2 Infrastructure Map] +``` + +### Steps: +1. **Acquisition**: Capture memory dump from compromised system +2. **Process Scan**: Use Volatility3 to identify suspicious processes +3. **Injection Detection**: Use malfind to find RWX memory regions +4. **Region Extraction**: Dump injected memory regions to files +5. **Config Search**: Scan dumps for beacon configuration signatures +6. **Infrastructure Mapping**: Correlate extracted C2 with network logs + +## Workflow 3: Watermark Attribution + +``` +[Multiple Beacons] --> [Extract Watermarks] --> [Cluster by Watermark] --> [Attribution] + | + v + [Campaign Correlation] +``` + +### Steps: +1. **Collection**: Gather beacon samples from incident or threat intel feeds +2. **Watermark Extraction**: Extract watermark value from each sample +3. **Database Lookup**: Check watermark against known databases +4. **Clustering**: Group beacons sharing the same watermark +5. **Infrastructure Overlap**: Correlate C2 infrastructure across cluster +6. **Attribution Assessment**: Link to known threat actor or cracked license + +## Workflow 4: C2 Traffic Detection + +``` +[Beacon Config] --> [Extract C2 Profile] --> [Generate Signatures] --> [Deploy to NIDS] + | + v + [Monitor Network Traffic] +``` + +### Steps: +1. **Profile Extraction**: Parse malleable C2 profile from beacon config +2. **Pattern Identification**: Identify unique HTTP headers, URIs, and encoding +3. **Signature Creation**: Write Suricata/Snort rules matching C2 patterns +4. **Deployment**: Deploy signatures to network detection infrastructure +5. **Validation**: Test signatures against captured beacon traffic +6. **Monitoring**: Alert on matching network flows for active beacons diff --git a/personas/_shared/skills/analyzing-cobalt-strike-beacon-configuration/scripts/agent.py b/personas/_shared/skills/analyzing-cobalt-strike-beacon-configuration/scripts/agent.py new file mode 100644 index 0000000..4df373c --- /dev/null +++ b/personas/_shared/skills/analyzing-cobalt-strike-beacon-configuration/scripts/agent.py @@ -0,0 +1,239 @@ +#!/usr/bin/env python3 +"""Cobalt Strike beacon configuration extraction and analysis agent. + +Extracts C2 configuration from beacon payloads including server addresses, +communication settings, malleable C2 profile details, and watermark values. +""" + +import struct +import os +import sys +import hashlib +from collections import OrderedDict + +# Cobalt Strike beacon configuration field IDs (Type-Length-Value format) +BEACON_CONFIG_FIELDS = { + 1: ("BeaconType", "short"), + 2: ("Port", "short"), + 3: ("SleepTime", "int"), + 4: ("MaxGetSize", "int"), + 5: ("Jitter", "short"), + 7: ("PublicKey", "bytes"), + 8: ("C2Server", "str"), + 9: ("UserAgent", "str"), + 10: ("PostURI", "str"), + 11: ("Malleable_C2_Instructions", "bytes"), + 12: ("HttpGet_Metadata", "bytes"), + 13: ("HttpPost_Metadata", "bytes"), + 14: ("SpawnToX86", "str"), + 15: ("SpawnToX64", "str"), + 19: ("CryptoScheme", "short"), + 26: ("GetVerb", "str"), + 27: ("PostVerb", "str"), + 28: ("HttpPostChunk", "int"), + 29: ("Spawnto_x86", "str"), + 30: ("Spawnto_x64", "str"), + 31: ("CryptoScheme2", "str"), + 37: ("Watermark", "int"), + 38: ("StageCleanup", "short"), + 39: ("CFGCaution", "short"), + 43: ("DNS_Idle", "int"), + 44: ("DNS_Sleep", "int"), + 50: ("HostHeader", "str"), + 54: ("PipeName", "str"), +} + +BEACON_TYPES = {0: "HTTP", 1: "Hybrid HTTP/DNS", 2: "SMB", 4: "TCP", 8: "HTTPS", 16: "DNS over HTTPS"} + +XOR_KEY_V3 = 0x69 +XOR_KEY_V4 = 0x2E + + +def compute_hash(filepath): + """Compute SHA-256 hash of file.""" + sha256 = hashlib.sha256() + with open(filepath, "rb") as f: + for chunk in iter(lambda: f.read(65536), b""): + sha256.update(chunk) + return sha256.hexdigest() + + +def find_config_offset(data): + """Find the beacon configuration blob in PE data or shellcode.""" + # Look for XOR-encoded config patterns + for xor_key in [XOR_KEY_V3, XOR_KEY_V4]: + # Config starts with 0x0001 (BeaconType field ID) XOR-encoded + encoded_marker = bytes([0x00 ^ xor_key, 0x01 ^ xor_key, 0x00 ^ xor_key, 0x01 ^ xor_key]) + offset = data.find(encoded_marker) + if offset != -1: + return offset, xor_key + # Try unencoded + for offset in range(len(data) - 100): + if data[offset:offset+4] == b"\x00\x01\x00\x01": + return offset, None + return -1, None + + +def xor_decode(data, key): + """XOR decode data with single byte key.""" + if key is None: + return data + return bytes(b ^ key for b in data) + + +def parse_config_field(data, offset): + """Parse a single TLV config field.""" + if offset + 6 > len(data): + return None, None, None, offset + field_id = struct.unpack_from(">H", data, offset)[0] + field_type = struct.unpack_from(">H", data, offset + 2)[0] + if field_type == 1: # short + value = struct.unpack_from(">H", data, offset + 4)[0] + return field_id, "short", value, offset + 6 + elif field_type == 2: # int + value = struct.unpack_from(">I", data, offset + 4)[0] + return field_id, "int", value, offset + 8 + elif field_type == 3: # str/bytes + length = struct.unpack_from(">H", data, offset + 4)[0] + if offset + 6 + length > len(data): + return None, None, None, offset + value = data[offset + 6:offset + 6 + length] + return field_id, "str", value, offset + 6 + length + return None, None, None, offset + 2 + + +def extract_beacon_config(filepath): + """Extract and parse Cobalt Strike beacon configuration.""" + with open(filepath, "rb") as f: + data = f.read() + + config_offset, xor_key = find_config_offset(data) + if config_offset == -1: + return {"error": "No beacon configuration found", "file": filepath} + + config_data = xor_decode(data[config_offset:config_offset + 4096], xor_key) + config = OrderedDict() + config["_meta"] = { + "config_offset": f"0x{config_offset:08X}", + "xor_key": f"0x{xor_key:02X}" if xor_key else "none", + "version_guess": "4.x" if xor_key == XOR_KEY_V4 else "3.x" if xor_key == XOR_KEY_V3 else "unknown", + } + + offset = 0 + max_fields = 100 + parsed = 0 + while offset < len(config_data) - 4 and parsed < max_fields: + field_id, field_type, value, new_offset = parse_config_field(config_data, offset) + if field_id is None or new_offset == offset: + break + offset = new_offset + parsed += 1 + + field_info = BEACON_CONFIG_FIELDS.get(field_id) + if field_info: + field_name, expected_type = field_info + if isinstance(value, bytes): + try: + str_value = value.rstrip(b"\x00").decode("utf-8", errors="replace") + config[field_name] = str_value + except Exception: + config[field_name] = value.hex()[:100] + elif field_id == 1: + config[field_name] = BEACON_TYPES.get(value, f"Unknown({value})") + else: + config[field_name] = value + + return config + + +def extract_c2_indicators(config): + """Extract C2 indicators from parsed config for threat intelligence.""" + indicators = {"c2_servers": [], "user_agents": [], "uris": [], + "pipes": [], "watermark": None, "dns": []} + c2 = config.get("C2Server", "") + if c2: + for server in c2.split(","): + server = server.strip().rstrip("/") + if server: + indicators["c2_servers"].append(server) + ua = config.get("UserAgent", "") + if ua: + indicators["user_agents"].append(ua) + for key in ["PostURI"]: + uri = config.get(key, "") + if uri: + indicators["uris"].append(uri) + pipe = config.get("PipeName", "") + if pipe: + indicators["pipes"].append(pipe) + wm = config.get("Watermark") + if wm: + indicators["watermark"] = wm + return indicators + + +def assess_operator_opsec(config): + """Assess operator OPSEC based on beacon configuration.""" + findings = [] + sleep = config.get("SleepTime", 0) + jitter = config.get("Jitter", 0) + if sleep < 30000: + findings.append({"level": "INFO", "detail": f"Low sleep time: {sleep}ms - high beacon frequency"}) + if jitter == 0: + findings.append({"level": "WARN", "detail": "No jitter configured - predictable beacon interval"}) + ua = config.get("UserAgent", "") + if "Mozilla" not in ua and ua: + findings.append({"level": "WARN", "detail": f"Non-standard User-Agent: {ua[:60]}"}) + spawn86 = config.get("SpawnToX86", config.get("Spawnto_x86", "")) + if "rundll32" in spawn86.lower(): + findings.append({"level": "INFO", "detail": "Default spawn-to process (rundll32) - easy to detect"}) + cleanup = config.get("StageCleanup", 0) + if cleanup == 0: + findings.append({"level": "INFO", "detail": "Stage cleanup disabled - beacon stub remains in memory"}) + return findings + + +if __name__ == "__main__": + print("=" * 60) + print("Cobalt Strike Beacon Configuration Extractor") + print("C2 extraction, watermark analysis, OPSEC assessment") + print("=" * 60) + + target = sys.argv[1] if len(sys.argv) > 1 else None + + if not target or not os.path.exists(target): + print("\n[DEMO] Usage: python agent.py ") + print(" Extracts: C2 servers, sleep/jitter, watermark, malleable profile") + sys.exit(0) + + print(f"\n[*] Analyzing: {target}") + print(f"[*] SHA-256: {compute_hash(target)}") + print(f"[*] Size: {os.path.getsize(target)} bytes") + + config = extract_beacon_config(target) + + if "error" in config: + print(f"\n[!] {config['error']}") + sys.exit(1) + + print("\n--- Beacon Configuration ---") + for key, value in config.items(): + if key == "_meta": + for mk, mv in value.items(): + print(f" {mk}: {mv}") + else: + print(f" {key}: {value}") + + indicators = extract_c2_indicators(config) + print("\n--- C2 Indicators ---") + for c2 in indicators["c2_servers"]: + print(f" [C2] {c2}") + if indicators["watermark"]: + print(f" [Watermark] {indicators['watermark']}") + for pipe in indicators["pipes"]: + print(f" [Pipe] {pipe}") + + opsec = assess_operator_opsec(config) + print("\n--- Operator OPSEC Assessment ---") + for f in opsec: + print(f" [{f['level']}] {f['detail']}") diff --git a/personas/_shared/skills/analyzing-cobalt-strike-beacon-configuration/scripts/process.py b/personas/_shared/skills/analyzing-cobalt-strike-beacon-configuration/scripts/process.py new file mode 100644 index 0000000..43e3f66 --- /dev/null +++ b/personas/_shared/skills/analyzing-cobalt-strike-beacon-configuration/scripts/process.py @@ -0,0 +1,337 @@ +#!/usr/bin/env python3 +""" +Cobalt Strike Beacon Configuration Analyzer + +Extracts and analyzes beacon configurations from PE files, shellcode, +and memory dumps using dissect.cobaltstrike and manual parsing. + +Requirements: + pip install dissect.cobaltstrike pefile yara-python + +Usage: + python process.py --file beacon.exe --output report.json + python process.py --file memdump.bin --scan-memory + python process.py --directory ./samples --batch +""" + +import argparse +import json +import os +import struct +import sys +from collections import defaultdict +from datetime import datetime +from pathlib import Path + +try: + from dissect.cobaltstrike.beacon import BeaconConfig +except ImportError: + print("ERROR: dissect.cobaltstrike not installed.") + print("Run: pip install dissect.cobaltstrike") + sys.exit(1) + + +# TLV field type mapping +TLV_FIELDS = { + 0x0001: ("BeaconType", "short"), + 0x0002: ("Port", "short"), + 0x0003: ("SleepTime", "int"), + 0x0004: ("MaxGetSize", "int"), + 0x0005: ("Jitter", "short"), + 0x0006: ("MaxDNS", "short"), + 0x0008: ("C2Server", "str"), + 0x0009: ("UserAgent", "str"), + 0x000a: ("PostURI", "str"), + 0x000b: ("Malleable_C2_Instructions", "blob"), + 0x000d: ("SpawnTo_x86", "str"), + 0x000e: ("SpawnTo_x64", "str"), + 0x000f: ("CryptoScheme", "short"), + 0x001a: ("Watermark", "int"), + 0x001d: ("HostHeader", "str"), + 0x0024: ("PipeName", "str"), + 0x0025: ("Year", "short"), + 0x0026: ("Month", "short"), + 0x0027: ("Day", "short"), + 0x002c: ("ProxyHostname", "str"), + 0x002d: ("ProxyUsername", "str"), + 0x002e: ("ProxyPassword", "str"), +} + +BEACON_TYPES = { + 0: "HTTP", + 1: "Hybrid HTTP/DNS", + 2: "SMB", + 4: "TCP", + 8: "HTTPS", + 10: "TCP Bind", + 14: "External C2", +} + + +class BeaconAnalyzer: + """Analyze Cobalt Strike beacon configurations.""" + + def __init__(self): + self.results = [] + + def analyze_file(self, filepath): + """Extract beacon config from a file.""" + filepath = Path(filepath) + if not filepath.exists(): + print(f"[-] File not found: {filepath}") + return None + + print(f"[*] Analyzing: {filepath}") + + # Try dissect.cobaltstrike first + result = self._extract_with_dissect(filepath) + + # Fall back to manual extraction + if not result: + result = self._extract_manual(filepath) + + if result: + result["source_file"] = str(filepath) + result["analysis_time"] = datetime.now().isoformat() + self.results.append(result) + + return result + + def _extract_with_dissect(self, filepath): + """Extract config using dissect.cobaltstrike library.""" + try: + configs = list(BeaconConfig.from_path(filepath)) + if not configs: + return None + + config = configs[0] + settings = config.as_dict() + + result = { + "method": "dissect.cobaltstrike", + "config": {}, + "indicators": {}, + } + + for key, value in settings.items(): + if value is not None: + result["config"][key] = str(value) + + result["indicators"] = self._extract_indicators(settings) + return result + + except Exception as e: + print(f" [!] dissect extraction failed: {e}") + return None + + def _extract_manual(self, filepath): + """Manual XOR-based config extraction.""" + try: + with open(filepath, "rb") as f: + data = f.read() + except Exception as e: + print(f" [!] Read failed: {e}") + return None + + for xor_key in [0x2e, 0x69]: + # Search for XOR'd config start marker + magic = bytes([0x00 ^ xor_key, 0x01 ^ xor_key, + 0x00 ^ xor_key, 0x02 ^ xor_key]) + + offset = data.find(magic) + if offset == -1: + continue + + print(f" [+] Config found at 0x{offset:x} (XOR key: 0x{xor_key:02x})") + + config_blob = data[offset:offset + 4096] + decrypted = bytes([b ^ xor_key for b in config_blob]) + + entries = self._parse_tlv(decrypted) + if entries: + return { + "method": "manual_xor", + "xor_key": f"0x{xor_key:02x}", + "config_offset": f"0x{offset:x}", + "config": entries, + "indicators": self._extract_indicators(entries), + } + + return None + + def _parse_tlv(self, data): + """Parse TLV configuration entries.""" + entries = {} + offset = 0 + + while offset + 6 <= len(data): + try: + entry_type = struct.unpack(">H", data[offset:offset+2])[0] + data_type = struct.unpack(">H", data[offset+2:offset+4])[0] + entry_len = struct.unpack(">H", data[offset+4:offset+6])[0] + except struct.error: + break + + if entry_type == 0 or entry_len > 4096: + break + + value_data = data[offset+6:offset+6+entry_len] + field_info = TLV_FIELDS.get(entry_type) + + if field_info: + field_name, expected_type = field_info + else: + field_name = f"Unknown_0x{entry_type:04x}" + expected_type = "blob" + + if data_type == 1 and len(value_data) >= 2: + value = struct.unpack(">H", value_data[:2])[0] + elif data_type == 2 and len(value_data) >= 4: + value = struct.unpack(">I", value_data[:4])[0] + elif data_type == 3: + value = value_data.rstrip(b'\x00').decode('utf-8', errors='replace') + else: + value = value_data.hex() + + # Resolve beacon type names + if field_name == "BeaconType" and isinstance(value, int): + value = BEACON_TYPES.get(value, f"Unknown ({value})") + + entries[field_name] = value + offset += 6 + entry_len + + return entries + + def _extract_indicators(self, config): + """Extract IOCs from parsed configuration.""" + indicators = { + "c2_servers": [], + "user_agent": "", + "named_pipes": [], + "spawn_processes": [], + "watermark": "", + "beacon_type": "", + "sleep_time_ms": 0, + "jitter_pct": 0, + } + + # Handle both dissect dict keys and manual parse keys + c2_keys = ["SETTING_DOMAINS", "C2Server"] + for key in c2_keys: + domains = config.get(key, "") + if domains: + for d in str(domains).split(","): + d = d.strip().rstrip("/") + if d: + indicators["c2_servers"].append(d) + + ua_keys = ["SETTING_USERAGENT", "UserAgent"] + for key in ua_keys: + ua = config.get(key, "") + if ua: + indicators["user_agent"] = str(ua) + + pipe_keys = ["SETTING_PIPENAME", "PipeName"] + for key in pipe_keys: + pipe = config.get(key, "") + if pipe: + indicators["named_pipes"].append(str(pipe)) + + spawn_keys = [ + ("SETTING_SPAWNTO_X86", "SpawnTo_x86"), + ("SETTING_SPAWNTO_X64", "SpawnTo_x64"), + ] + for dissect_key, manual_key in spawn_keys: + for key in [dissect_key, manual_key]: + proc = config.get(key, "") + if proc: + indicators["spawn_processes"].append(str(proc)) + + wm_keys = ["SETTING_WATERMARK", "Watermark"] + for key in wm_keys: + wm = config.get(key, "") + if wm: + indicators["watermark"] = str(wm) + + return indicators + + def batch_analyze(self, directory): + """Analyze all files in a directory.""" + directory = Path(directory) + extensions = {".exe", ".dll", ".bin", ".dmp", ".raw"} + + for filepath in directory.rglob("*"): + if filepath.suffix.lower() in extensions: + self.analyze_file(filepath) + + return self.results + + def cluster_by_watermark(self): + """Cluster analyzed beacons by watermark.""" + clusters = defaultdict(list) + + for result in self.results: + wm = result.get("indicators", {}).get("watermark", "unknown") + clusters[wm].append(result.get("source_file", "unknown")) + + return dict(clusters) + + def generate_report(self, output_path=None): + """Generate JSON analysis report.""" + report = { + "analysis_date": datetime.now().isoformat(), + "total_beacons": len(self.results), + "watermark_clusters": self.cluster_by_watermark(), + "all_c2_servers": list(set( + server + for r in self.results + for server in r.get("indicators", {}).get("c2_servers", []) + )), + "results": self.results, + } + + if output_path: + with open(output_path, "w") as f: + json.dump(report, f, indent=2, default=str) + print(f"[+] Report saved to {output_path}") + + return report + + +def main(): + parser = argparse.ArgumentParser( + description="Cobalt Strike Beacon Configuration Analyzer" + ) + parser.add_argument("--file", help="Single file to analyze") + parser.add_argument("--directory", help="Directory for batch analysis") + parser.add_argument("--output", default="beacon_report.json", + help="Output report path") + parser.add_argument("--scan-memory", action="store_true", + help="Treat input as raw memory dump") + parser.add_argument("--batch", action="store_true", + help="Batch analyze directory") + + args = parser.parse_args() + analyzer = BeaconAnalyzer() + + if args.file: + result = analyzer.analyze_file(args.file) + if result: + print(json.dumps(result, indent=2, default=str)) + + elif args.directory and args.batch: + results = analyzer.batch_analyze(args.directory) + print(f"\n[+] Analyzed {len(results)} beacons") + + else: + parser.print_help() + sys.exit(1) + + report = analyzer.generate_report(args.output) + print(f"\n[+] Total C2 servers found: {len(report['all_c2_servers'])}") + for server in report["all_c2_servers"]: + print(f" {server}") + + +if __name__ == "__main__": + main() diff --git a/personas/_shared/skills/analyzing-cobaltstrike-malleable-c2-profiles/LICENSE b/personas/_shared/skills/analyzing-cobaltstrike-malleable-c2-profiles/LICENSE new file mode 100644 index 0000000..d885118 --- /dev/null +++ b/personas/_shared/skills/analyzing-cobaltstrike-malleable-c2-profiles/LICENSE @@ -0,0 +1,201 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to the Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by the Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding any notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. Please do not remove or change + the license header comment from a contributed file except when + necessary. + + Copyright 2026 mukul975 + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/personas/_shared/skills/analyzing-cobaltstrike-malleable-c2-profiles/SKILL.md b/personas/_shared/skills/analyzing-cobaltstrike-malleable-c2-profiles/SKILL.md new file mode 100644 index 0000000..31f10c0 --- /dev/null +++ b/personas/_shared/skills/analyzing-cobaltstrike-malleable-c2-profiles/SKILL.md @@ -0,0 +1,61 @@ +--- +name: analyzing-cobaltstrike-malleable-c2-profiles +description: Parse and analyze Cobalt Strike Malleable C2 profiles using dissect.cobaltstrike and pyMalleableC2 to extract + C2 indicators, detect evasion techniques, and generate network detection signatures. +domain: cybersecurity +subdomain: malware-analysis +tags: +- cobalt-strike +- malleable-c2 +- c2-detection +- beacon-analysis +- network-signatures +- threat-hunting +- red-team-tools +version: '1.0' +author: mahipal +license: Apache-2.0 +nist_csf: +- DE.AE-02 +- RS.AN-03 +- ID.RA-01 +- DE.CM-01 +--- +# Analyzing CobaltStrike Malleable C2 Profiles + +## Overview + +Cobalt Strike Malleable C2 profiles are domain-specific language scripts that customize how Beacon communicates with the team server, defining HTTP request/response transformations, sleep intervals, jitter values, user agents, URI paths, and process injection behavior. Threat actors use malleable profiles to disguise C2 traffic as legitimate services (Amazon, Google, Slack). Analyzing these profiles reveals network indicators for detection: URI patterns, HTTP headers, POST/GET transforms, DNS settings, and process injection techniques. The `dissect.cobaltstrike` library can parse both profile files and extract configurations from beacon payloads, while `pyMalleableC2` provides AST-based parsing using Lark grammar for programmatic profile manipulation and validation. + + +## When to Use + +- When investigating security incidents that require analyzing cobaltstrike malleable c2 profiles +- When building detection rules or threat hunting queries for this domain +- When SOC analysts need structured procedures for this analysis type +- When validating security monitoring coverage for related attack techniques + +## Prerequisites + +- Python 3.9+ with `dissect.cobaltstrike` and/or `pyMalleableC2` +- Sample Malleable C2 profiles (available from public repositories) +- Understanding of HTTP protocol and Cobalt Strike beacon communication model +- Network monitoring tools (Suricata/Snort) for signature deployment +- PCAP analysis tools for traffic validation + +## Steps + +1. Install libraries: `pip install dissect.cobaltstrike` or `pip install pyMalleableC2` +2. Parse profile with `C2Profile.from_path("profile.profile")` +3. Extract HTTP GET/POST block configurations (URIs, headers, parameters) +4. Identify user agent strings and spoof targets +5. Extract sleep time, jitter percentage, and DNS beacon settings +6. Analyze process injection settings (spawn-to, allocation technique) +7. Generate Suricata/Snort signatures from extracted network indicators +8. Compare profile against known threat actor profile collections +9. Extract staging URIs and payload delivery mechanisms +10. Produce detection report with IOCs and recommended network signatures + +## Expected Output + +A JSON report containing extracted C2 URIs, HTTP headers, user agents, sleep/jitter settings, process injection config, spawned process paths, DNS settings, and generated Suricata-compatible detection rules. diff --git a/personas/_shared/skills/analyzing-cobaltstrike-malleable-c2-profiles/references/api-reference.md b/personas/_shared/skills/analyzing-cobaltstrike-malleable-c2-profiles/references/api-reference.md new file mode 100644 index 0000000..420dafa --- /dev/null +++ b/personas/_shared/skills/analyzing-cobaltstrike-malleable-c2-profiles/references/api-reference.md @@ -0,0 +1,95 @@ +# CobaltStrike Malleable C2 Profile Analysis API Reference + +## Installation + +```bash +pip install dissect.cobaltstrike +pip install 'dissect.cobaltstrike[full]' # With PCAP support +pip install pyMalleableC2 # Alternative parser +``` + +## dissect.cobaltstrike API + +### Parse Beacon Configuration +```python +from dissect.cobaltstrike.beacon import BeaconConfig + +bconfig = BeaconConfig.from_path("beacon.bin") +print(hex(bconfig.watermark)) # 0x5109bf6d +print(bconfig.protocol) # https +print(bconfig.version) # BeaconVersion(...) +print(bconfig.settings) # Full config dict +``` + +### Parse Malleable C2 Profile +```python +from dissect.cobaltstrike.c2profile import C2Profile + +profile = C2Profile.from_path("amazon.profile") +config = profile.as_dict() +print(config["useragent"]) +print(config["http-get.uri"]) +print(config["sleeptime"]) +``` + +### PCAP Analysis +```bash +# Extract beacons from PCAP +beacon-pcap --extract-beacons traffic.pcap + +# Decrypt traffic with private key +beacon-pcap -p team_server.pem traffic.pcap --beacon beacon.bin +``` + +## pyMalleableC2 API + +```python +from malleableC2 import Profile + +profile = Profile.from_file("amazon.profile") +print(profile.sleeptime) +print(profile.useragent) +print(profile.http_get.uri) +print(profile.http_post.uri) +``` + +## Key Profile Settings + +| Setting | Description | Detection Value | +|---------|-------------|-----------------| +| `sleeptime` | Callback interval (ms) | Low values = aggressive beaconing | +| `jitter` | Sleep randomization % | Timing analysis evasion | +| `useragent` | HTTP User-Agent string | Network signature | +| `http-get.uri` | GET request URI path | URI-based detection | +| `http-post.uri` | POST request URI path | URI-based detection | +| `spawnto_x86` | 32-bit spawn process | Process creation detection | +| `spawnto_x64` | 64-bit spawn process | Process creation detection | +| `pipename` | Named pipe pattern | Named pipe monitoring | +| `dns_idle` | DNS idle IP address | DNS beacon detection | +| `watermark` | License watermark | Operator attribution | + +## Suricata Rule Format + +``` +alert http $HOME_NET any -> $EXTERNAL_NET any ( + msg:"MALWARE CobaltStrike C2 URI"; + flow:established,to_server; + http.uri; content:"/api/v1/status"; + http.header; content:"User-Agent: Mozilla/5.0"; + sid:9000001; rev:1; +) +``` + +## CLI Usage + +```bash +python agent.py --input profile.profile --output report.json +python agent.py --input parsed_config.json --output report.json +``` + +## References + +- dissect.cobaltstrike: https://github.com/fox-it/dissect.cobaltstrike +- pyMalleableC2: https://github.com/byt3bl33d3r/pyMalleableC2 +- Unit42 Analysis: https://unit42.paloaltonetworks.com/cobalt-strike-malleable-c2-profile/ +- Config Extractor: https://github.com/strozfriedberg/cobaltstrike-config-extractor diff --git a/personas/_shared/skills/analyzing-cobaltstrike-malleable-c2-profiles/scripts/agent.py b/personas/_shared/skills/analyzing-cobaltstrike-malleable-c2-profiles/scripts/agent.py new file mode 100644 index 0000000..582d5d3 --- /dev/null +++ b/personas/_shared/skills/analyzing-cobaltstrike-malleable-c2-profiles/scripts/agent.py @@ -0,0 +1,234 @@ +#!/usr/bin/env python3 +"""CobaltStrike Malleable C2 Profile Analyzer - parses profiles to extract C2 indicators, detection signatures, and evasion techniques""" +# For authorized security research and defensive analysis only + +import argparse +import json +import re +from collections import Counter +from datetime import datetime +from pathlib import Path + +try: + from dissect.cobaltstrike.c2profile import C2Profile + HAS_DISSECT = True +except ImportError: + HAS_DISSECT = False + +RUN_KEY_SUSPICIOUS = ["powershell", "cmd.exe", "mshta", "rundll32", "regsvr32", "wscript", "cscript"] + +KNOWN_SPOOF_TARGETS = { + "amazon": "Amazon CDN impersonation", + "google": "Google services impersonation", + "microsoft": "Microsoft services impersonation", + "slack": "Slack API impersonation", + "cloudfront": "CloudFront CDN impersonation", + "jquery": "jQuery CDN impersonation", + "outlook": "Outlook Web impersonation", + "onedrive": "OneDrive impersonation", +} + + +def load_data(path): + return json.loads(Path(path).read_text(encoding="utf-8")) + + +def parse_profile_with_dissect(profile_path): + """Parse a .profile file using dissect.cobaltstrike C2Profile.""" + if not HAS_DISSECT: + return None + profile = C2Profile.from_path(profile_path) + return profile.as_dict() + + +def parse_profile_regex(content): + """Regex-based parser for malleable C2 profile when dissect is unavailable.""" + config = {} + set_pattern = re.compile(r'set\s+(\w+)\s+"([^"]*)"', re.MULTILINE) + for match in set_pattern.finditer(content): + config[match.group(1)] = match.group(2) + block_pattern = re.compile(r'(http-get|http-post|http-stager|https-certificate|dns-beacon|process-inject|post-ex)\s*\{', re.MULTILINE) + for match in block_pattern.finditer(content): + config.setdefault("blocks", []).append(match.group(1)) + uri_pattern = re.compile(r'set\s+uri\s+"([^"]*)"', re.MULTILINE) + for match in uri_pattern.finditer(content): + config.setdefault("uris", []).append(match.group(1)) + header_pattern = re.compile(r'header\s+"([^"]+)"\s+"([^"]*)"', re.MULTILINE) + for match in header_pattern.finditer(content): + config.setdefault("headers", []).append({"name": match.group(1), "value": match.group(2)}) + spawn_pattern = re.compile(r'set\s+spawnto_x(?:86|64)\s+"([^"]*)"', re.MULTILINE) + for match in spawn_pattern.finditer(content): + config.setdefault("spawn_to", []).append(match.group(1)) + return config + + +def analyze_profile(config): + """Analyze parsed profile configuration for detection opportunities.""" + findings = [] + ua = config.get("useragent", config.get("user_agent", "")) + if ua: + findings.append({ + "type": "user_agent_identified", + "severity": "info", + "resource": "http-config", + "detail": f"User-Agent: {ua[:100]}", + "indicator": ua, + }) + for target, desc in KNOWN_SPOOF_TARGETS.items(): + if target.lower() in ua.lower(): + findings.append({ + "type": "service_impersonation", + "severity": "medium", + "resource": "user-agent", + "detail": f"{desc} detected in User-Agent string", + }) + sleeptime = config.get("sleeptime", config.get("sleep_time", "")) + jitter = config.get("jitter", "") + if sleeptime: + try: + sleep_ms = int(sleeptime) + if sleep_ms < 1000: + findings.append({ + "type": "aggressive_beaconing", + "severity": "high", + "resource": "beacon-config", + "detail": f"Very low sleep time: {sleep_ms}ms - aggressive C2 callback rate", + }) + except ValueError: + pass + uris = config.get("uris", []) + for uri in uris: + findings.append({ + "type": "c2_uri", + "severity": "high", + "resource": "http-config", + "detail": f"C2 URI path: {uri}", + "indicator": uri, + }) + headers = config.get("headers", []) + for h in headers: + name = h.get("name", "") if isinstance(h, dict) else str(h) + value = h.get("value", "") if isinstance(h, dict) else "" + if name.lower() in ("host", "cookie", "authorization"): + findings.append({ + "type": "c2_header", + "severity": "medium", + "resource": "http-config", + "detail": f"Custom header: {name}: {value[:60]}", + }) + spawn_to = config.get("spawn_to", config.get("spawnto_x86", [])) + if isinstance(spawn_to, str): + spawn_to = [spawn_to] + for proc in spawn_to: + findings.append({ + "type": "spawn_to_process", + "severity": "high", + "resource": "process-inject", + "detail": f"Beacon spawns to: {proc}", + "indicator": proc, + }) + pipename = config.get("pipename", config.get("pipename_stager", "")) + if pipename: + findings.append({ + "type": "named_pipe", + "severity": "high", + "resource": "process-inject", + "detail": f"Named pipe: {pipename}", + "indicator": pipename, + }) + dns_idle = config.get("dns_idle", "") + if dns_idle: + findings.append({ + "type": "dns_beacon_config", + "severity": "medium", + "resource": "dns-beacon", + "detail": f"DNS idle IP: {dns_idle}", + }) + watermark = config.get("watermark", "") + if watermark: + findings.append({ + "type": "watermark", + "severity": "info", + "resource": "beacon-config", + "detail": f"Beacon watermark: {watermark}", + }) + return findings + + +def generate_suricata_rules(findings, sid_start=9000001): + """Generate Suricata rules from extracted indicators.""" + rules = [] + sid = sid_start + for f in findings: + if f["type"] == "c2_uri" and f.get("indicator"): + uri = f["indicator"].replace('"', '\\"') + rules.append( + f'alert http $HOME_NET any -> $EXTERNAL_NET any ' + f'(msg:"MALWARE CobaltStrike Malleable C2 URI {uri}"; ' + f'flow:established,to_server; ' + f'http.uri; content:"{uri}"; ' + f'sid:{sid}; rev:1;)' + ) + sid += 1 + elif f["type"] == "named_pipe" and f.get("indicator"): + pipe = f["indicator"] + rules.append( + f'# Named pipe detection requires endpoint monitoring: {pipe}' + ) + return rules + + +def analyze(data): + if isinstance(data, str): + config = parse_profile_regex(data) + elif isinstance(data, dict): + config = data + else: + config = data[0] if isinstance(data, list) and data else {} + return analyze_profile(config) + + +def generate_report(input_path): + path = Path(input_path) + if path.suffix in (".profile", ".txt"): + content = path.read_text(encoding="utf-8") + config = parse_profile_regex(content) + findings = analyze_profile(config) + else: + data = load_data(input_path) + if isinstance(data, list): + findings = [] + for profile in data: + findings.extend(analyze_profile(profile)) + else: + findings = analyze_profile(data) + sev = Counter(f["severity"] for f in findings) + iocs = [f.get("indicator", "") for f in findings if f.get("indicator")] + rules = generate_suricata_rules(findings) + return { + "report": "cobaltstrike_malleable_c2_analysis", + "generated_at": datetime.utcnow().isoformat() + "Z", + "total_findings": len(findings), + "severity_summary": dict(sev), + "extracted_iocs": iocs, + "suricata_rules": rules, + "findings": findings, + } + + +def main(): + ap = argparse.ArgumentParser(description="CobaltStrike Malleable C2 Profile Analyzer") + ap.add_argument("--input", required=True, help="Input .profile file or JSON with parsed config") + ap.add_argument("--output", help="Output JSON report path") + args = ap.parse_args() + report = generate_report(args.input) + out = json.dumps(report, indent=2) + if args.output: + Path(args.output).write_text(out, encoding="utf-8") + print(f"Report written to {args.output}") + else: + print(out) + + +if __name__ == "__main__": + main() diff --git a/personas/_shared/skills/analyzing-command-and-control-communication/LICENSE b/personas/_shared/skills/analyzing-command-and-control-communication/LICENSE new file mode 100644 index 0000000..d885118 --- /dev/null +++ b/personas/_shared/skills/analyzing-command-and-control-communication/LICENSE @@ -0,0 +1,201 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to the Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by the Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding any notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. Please do not remove or change + the license header comment from a contributed file except when + necessary. + + Copyright 2026 mukul975 + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/personas/_shared/skills/analyzing-command-and-control-communication/SKILL.md b/personas/_shared/skills/analyzing-command-and-control-communication/SKILL.md new file mode 100644 index 0000000..351cf7b --- /dev/null +++ b/personas/_shared/skills/analyzing-command-and-control-communication/SKILL.md @@ -0,0 +1,397 @@ +--- +name: analyzing-command-and-control-communication +description: 'Analyzes malware command-and-control (C2) communication protocols to understand beacon patterns, command structures, + data encoding, and infrastructure. Covers HTTP, HTTPS, DNS, and custom protocol C2 analysis for detection development and + threat intelligence. Activates for requests involving C2 analysis, beacon detection, C2 protocol reverse engineering, or + command-and-control infrastructure mapping. + + ' +domain: cybersecurity +subdomain: malware-analysis +tags: +- malware +- C2 +- command-and-control +- beacon +- protocol-analysis +version: 1.0.0 +author: mahipal +license: Apache-2.0 +nist_csf: +- DE.AE-02 +- RS.AN-03 +- ID.RA-01 +- DE.CM-01 +--- + +# Analyzing Command-and-Control Communication + +## When to Use + +- Reverse engineering a malware sample has revealed network communication that needs protocol analysis +- Building network-level detection signatures for a specific C2 framework (Cobalt Strike, Metasploit, Sliver) +- Mapping C2 infrastructure including primary servers, fallback domains, and dead drops +- Analyzing encrypted or encoded C2 traffic to understand the command set and data format +- Attributing malware to a threat actor based on C2 infrastructure patterns and tooling + +**Do not use** for general network anomaly detection; this is specifically for understanding known or suspected C2 protocols from malware analysis. + +## Prerequisites + +- PCAP capture of malware network traffic (from sandbox, network tap, or full packet capture) +- Wireshark/tshark for packet-level analysis +- Reverse engineering tools (Ghidra, dnSpy) for understanding C2 code in the malware binary +- Python 3.8+ with `scapy`, `dpkt`, and `requests` for protocol analysis and replay +- Threat intelligence databases for C2 infrastructure correlation (VirusTotal, Shodan, Censys) +- JA3/JA3S fingerprint databases for TLS-based C2 identification + +## Workflow + +### Step 1: Identify the C2 Channel + +Determine the protocol and transport used for C2 communication: + +``` +C2 Communication Channels: +━━━━━━━━━━━━━━━━━━━━━━━━━ +HTTP/HTTPS: Most common; uses standard web traffic to blend in + Indicators: Regular POST/GET requests, specific URI patterns, custom headers + +DNS: Tunneling data through DNS queries and responses + Indicators: High-volume TXT queries, long subdomain names, high entropy + +Custom TCP/UDP: Proprietary binary protocol on non-standard port + Indicators: Non-HTTP traffic on high ports, unknown protocol + +ICMP: Data encoded in ICMP echo/reply payloads + Indicators: ICMP packets with large or non-standard payloads + +WebSocket: Persistent bidirectional connection for real-time C2 + Indicators: WebSocket upgrade followed by binary frames + +Cloud Services: Using legitimate APIs (Telegram, Discord, Slack, GitHub) + Indicators: API calls to cloud services from unexpected processes + +Email: SMTP/IMAP for C2 commands and data exfiltration + Indicators: Automated email operations from non-email processes +``` + +### Step 2: Analyze Beacon Pattern + +Characterize the periodic communication pattern: + +```python +from scapy.all import rdpcap, IP, TCP +from collections import defaultdict +import statistics +import json + +packets = rdpcap("c2_traffic.pcap") + +# Group TCP SYN packets by destination +connections = defaultdict(list) +for pkt in packets: + if IP in pkt and TCP in pkt and (pkt[TCP].flags & 0x02): + key = f"{pkt[IP].dst}:{pkt[TCP].dport}" + connections[key].append(float(pkt.time)) + +# Analyze each destination for beaconing +for dst, times in sorted(connections.items()): + if len(times) < 3: + continue + + intervals = [times[i+1] - times[i] for i in range(len(times)-1)] + avg_interval = statistics.mean(intervals) + stdev = statistics.stdev(intervals) if len(intervals) > 1 else 0 + jitter_pct = (stdev / avg_interval * 100) if avg_interval > 0 else 0 + duration = times[-1] - times[0] + + beacon_data = { + "destination": dst, + "connections": len(times), + "duration_seconds": round(duration, 1), + "avg_interval_seconds": round(avg_interval, 1), + "stdev_seconds": round(stdev, 1), + "jitter_percent": round(jitter_pct, 1), + "is_beacon": 5 < avg_interval < 7200 and jitter_pct < 25, + } + + if beacon_data["is_beacon"]: + print(f"[!] BEACON DETECTED: {dst}") + print(f" Interval: {avg_interval:.0f}s +/- {stdev:.0f}s ({jitter_pct:.0f}% jitter)") + print(f" Sessions: {len(times)} over {duration:.0f}s") +``` + +### Step 3: Decode C2 Protocol Structure + +Reverse engineer the message format from captured traffic: + +```python +# HTTP-based C2 protocol analysis +import dpkt +import base64 + +with open("c2_traffic.pcap", "rb") as f: + pcap = dpkt.pcap.Reader(f) + +for ts, buf in pcap: + eth = dpkt.ethernet.Ethernet(buf) + if not isinstance(eth.data, dpkt.ip.IP): + continue + ip = eth.data + if not isinstance(ip.data, dpkt.tcp.TCP): + continue + tcp = ip.data + + if tcp.dport == 80 or tcp.dport == 443: + if len(tcp.data) > 0: + try: + http = dpkt.http.Request(tcp.data) + print(f"\n--- C2 REQUEST ---") + print(f"Method: {http.method}") + print(f"URI: {http.uri}") + print(f"Headers: {dict(http.headers)}") + if http.body: + print(f"Body ({len(http.body)} bytes):") + # Try Base64 decode + try: + decoded = base64.b64decode(http.body) + print(f" Decoded: {decoded[:200]}") + except: + print(f" Raw: {http.body[:200]}") + except: + pass +``` + +### Step 4: Identify C2 Framework + +Match observed patterns to known C2 frameworks: + +``` +Known C2 Framework Signatures: +━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +Cobalt Strike: + - Default URIs: /pixel, /submit.php, /___utm.gif, /ca, /dpixel + - Malleable C2 profiles customize all traffic characteristics + - JA3: varies by profile, catalog at ja3er.com + - Watermark in beacon config (unique per license) + - Config extraction: use CobaltStrikeParser or 1768.py + +Metasploit/Meterpreter: + - Default staging URI patterns: random 4-char checksum + - Reverse HTTP(S) handler patterns + - Meterpreter TLV (Type-Length-Value) protocol structure + +Sliver: + - mTLS, HTTP, DNS, WireGuard transport options + - Protobuf-encoded messages + - Unique implant ID in communication + +Covenant: + - .NET-based C2 framework + - HTTP with customizable profiles + - Task-based command execution + +PoshC2: + - PowerShell/C# based + - HTTP with encrypted payloads + - Cookie-based session management +``` + +```bash +# Extract Cobalt Strike beacon configuration from PCAP or sample +python3 << 'PYEOF' +# Using CobaltStrikeParser (pip install cobalt-strike-parser) +from cobalt_strike_parser import BeaconConfig + +try: + config = BeaconConfig.from_file("suspect.exe") + print("Cobalt Strike Beacon Configuration:") + for key, value in config.items(): + print(f" {key}: {value}") +except Exception as e: + print(f"Not a Cobalt Strike beacon or parse error: {e}") +PYEOF +``` + +### Step 5: Map C2 Infrastructure + +Document the full C2 infrastructure and failover mechanisms: + +```python +# Infrastructure mapping +import requests +import json + +c2_indicators = { + "primary_c2": "185.220.101.42", + "domains": ["update.malicious.com", "backup.evil.net"], + "ports": [443, 8443], + "failover_dns": ["ns1.malicious-dns.com"], +} + +# Enrich with Shodan +def shodan_lookup(ip, api_key): + resp = requests.get(f"https://api.shodan.io/shodan/host/{ip}?key={api_key}") + if resp.status_code == 200: + data = resp.json() + return { + "ip": ip, + "ports": data.get("ports", []), + "os": data.get("os"), + "org": data.get("org"), + "asn": data.get("asn"), + "country": data.get("country_code"), + "hostnames": data.get("hostnames", []), + "last_update": data.get("last_update"), + } + return None + +# Enrich with passive DNS +def pdns_lookup(domain): + # Using VirusTotal passive DNS + resp = requests.get( + f"https://www.virustotal.com/api/v3/domains/{domain}/resolutions", + headers={"x-apikey": VT_API_KEY} + ) + if resp.status_code == 200: + data = resp.json() + resolutions = [] + for r in data.get("data", []): + resolutions.append({ + "ip": r["attributes"]["ip_address"], + "date": r["attributes"]["date"], + }) + return resolutions + return [] +``` + +### Step 6: Create Network Detection Signatures + +Build detection rules based on analyzed C2 characteristics: + +```bash +# Suricata rules for the analyzed C2 +cat << 'EOF' > c2_detection.rules +# HTTP beacon pattern +alert http $HOME_NET any -> $EXTERNAL_NET any ( + msg:"MALWARE MalwareX C2 HTTP Beacon"; + flow:established,to_server; + http.method; content:"POST"; + http.uri; content:"/gate.php"; startswith; + http.header; content:"User-Agent: Mozilla/5.0 (compatible; MSIE 10.0)"; + threshold:type threshold, track by_src, count 5, seconds 600; + sid:9000010; rev:1; +) + +# JA3 fingerprint match +alert tls $HOME_NET any -> $EXTERNAL_NET any ( + msg:"MALWARE MalwareX TLS JA3 Fingerprint"; + ja3.hash; content:"a0e9f5d64349fb13191bc781f81f42e1"; + sid:9000011; rev:1; +) + +# DNS beacon detection (high-entropy subdomain) +alert dns $HOME_NET any -> any any ( + msg:"MALWARE Suspected DNS C2 Tunneling"; + dns.query; pcre:"/^[a-z0-9]{20,}\./"; + threshold:type threshold, track by_src, count 10, seconds 60; + sid:9000012; rev:1; +) + +# Certificate-based detection +alert tls $HOME_NET any -> $EXTERNAL_NET any ( + msg:"MALWARE MalwareX Self-Signed C2 Certificate"; + tls.cert_subject; content:"CN=update.malicious.com"; + sid:9000013; rev:1; +) +EOF +``` + +## Key Concepts + +| Term | Definition | +|------|------------| +| **Beaconing** | Periodic check-in communication from malware to C2 server at regular intervals, often with jitter to avoid pattern detection | +| **Jitter** | Randomization applied to beacon interval (e.g., 60s +/- 15%) to make the timing pattern less predictable and harder to detect | +| **Malleable C2** | Cobalt Strike feature allowing operators to customize all aspects of C2 traffic (URIs, headers, encoding) to mimic legitimate services | +| **Dead Drop** | Intermediate location (paste site, cloud storage, social media) where C2 commands are posted for the malware to retrieve | +| **Domain Fronting** | Using a trusted CDN domain in the TLS SNI while routing to a different backend, making C2 traffic appear to go to a legitimate service | +| **Fast Flux** | Rapidly changing DNS records for C2 domains to distribute across many IPs and resist takedown efforts | +| **C2 Framework** | Software toolkit providing C2 server, implant generator, and operator interface (Cobalt Strike, Metasploit, Sliver, Covenant) | + +## Tools & Systems + +- **Wireshark**: Packet analyzer for detailed C2 protocol analysis at the packet level +- **RITA (Real Intelligence Threat Analytics)**: Open-source tool analyzing Zeek logs for beacon detection and DNS tunneling +- **CobaltStrikeParser**: Tool extracting Cobalt Strike beacon configuration from samples and memory dumps +- **JA3/JA3S**: TLS fingerprinting method for identifying C2 frameworks by their TLS implementation characteristics +- **Shodan/Censys**: Internet scanning platforms for mapping C2 infrastructure and identifying related servers + +## Common Scenarios + +### Scenario: Reverse Engineering a Custom C2 Protocol + +**Context**: A malware sample communicates with its C2 server using an unknown binary protocol over TCP port 8443. The protocol needs to be decoded to understand the command set and build detection signatures. + +**Approach**: +1. Filter PCAP for TCP port 8443 conversations and extract the TCP streams +2. Analyze the first few exchanges to identify the handshake/authentication mechanism +3. Map the message structure (length prefix, type field, payload encoding) +4. Cross-reference with Ghidra disassembly of the send/receive functions in the malware +5. Identify the command dispatcher and document each command code's function +6. Build a protocol decoder in Python for ongoing traffic analysis +7. Create Suricata rules matching the protocol handshake or static header bytes + +**Pitfalls**: +- Assuming the protocol is static; some C2 frameworks negotiate encryption during the handshake +- Not capturing enough traffic to see all command types (some commands are rare) +- Missing fallback C2 channels (DNS, ICMP) that activate when the primary channel fails +- Confusing encrypted payload data with the protocol framing structure + +## Output Format + +``` +C2 COMMUNICATION ANALYSIS REPORT +=================================== +Sample: malware.exe (SHA-256: e3b0c44...) +C2 Framework: Cobalt Strike 4.9 + +BEACON CONFIGURATION +C2 Server: hxxps://185.220.101[.]42/updates +Beacon Type: HTTPS (reverse) +Sleep: 60 seconds +Jitter: 15% +User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) +URI (GET): /dpixel +URI (POST): /submit.php +Watermark: 1234567890 + +PROTOCOL ANALYSIS +Transport: HTTPS (TLS 1.2) +JA3 Hash: a0e9f5d64349fb13191bc781f81f42e1 +Certificate: CN=Microsoft Update (self-signed) +Encoding: Base64 with XOR key 0x69 +Command Format: [4B length][4B command_id][payload] + +COMMAND SET +0x01 - Sleep Change beacon interval +0x02 - Shell Execute cmd.exe command +0x03 - Download Transfer file from C2 +0x04 - Upload Exfiltrate file to C2 +0x05 - Inject Process injection +0x06 - Keylog Start keylogger +0x07 - Screenshot Capture screen + +INFRASTRUCTURE +Primary: 185.220.101[.]42 (AS12345, Hosting Co, NL) +Failover: 91.215.85[.]17 (AS67890, VPS Provider, RU) +DNS: update.malicious[.]com -> 185.220.101[.]42 +Registrar: NameCheap +Registration: 2025-09-01 + +DETECTION SIGNATURES +SID 9000010: HTTP beacon pattern +SID 9000011: JA3 TLS fingerprint +SID 9000013: C2 certificate match +``` diff --git a/personas/_shared/skills/analyzing-command-and-control-communication/references/api-reference.md b/personas/_shared/skills/analyzing-command-and-control-communication/references/api-reference.md new file mode 100644 index 0000000..5aaa095 --- /dev/null +++ b/personas/_shared/skills/analyzing-command-and-control-communication/references/api-reference.md @@ -0,0 +1,112 @@ +# API Reference: C2 Communication Analysis Tools + +## Scapy - Packet Analysis Library (Python) + +### Reading PCAPs +```python +from scapy.all import rdpcap, IP, TCP, UDP, DNS, DNSQR +packets = rdpcap("capture.pcap") +``` + +### Filtering Packets +```python +# TCP SYN packets (connection initiation) +syn_pkts = [p for p in packets if TCP in p and (p[TCP].flags & 0x02)] + +# DNS queries +dns_pkts = [p for p in packets if DNS in p and p[DNS].qr == 0] + +# Access fields +pkt[IP].src # Source IP +pkt[IP].dst # Destination IP +pkt[TCP].sport # Source port +pkt[TCP].dport # Destination port +pkt[TCP].flags # TCP flags (0x02 = SYN) +float(pkt.time) # Packet timestamp +``` + +## dpkt - Packet Parsing Library (Python) + +### Reading PCAPs +```python +import dpkt +with open("capture.pcap", "rb") as f: + pcap = dpkt.pcap.Reader(f) + for timestamp, buf in pcap: + eth = dpkt.ethernet.Ethernet(buf) + ip = eth.data + tcp = ip.data +``` + +### HTTP Request Parsing +```python +http = dpkt.http.Request(tcp.data) +http.method # GET, POST +http.uri # /path +http.headers # dict of headers +http.body # POST body +``` + +## tshark - CLI Wireshark + +### Beacon Analysis +```bash +tshark -r capture.pcap -T fields -e ip.dst -e tcp.dstport -e frame.time_epoch \ + -Y "tcp.flags.syn==1" > syn_times.csv +``` + +### HTTP Extraction +```bash +tshark -r capture.pcap -Y "http.request" -T fields \ + -e http.request.method -e http.host -e http.request.uri -e http.user_agent +``` + +### DNS Extraction +```bash +tshark -r capture.pcap -Y "dns.qr==0" -T fields \ + -e dns.qry.name -e dns.qry.type -e ip.src +``` + +### JA3 TLS Fingerprinting +```bash +tshark -r capture.pcap -Y "tls.handshake.type==1" -T fields \ + -e ip.src -e tls.handshake.ja3 +``` + +## CobaltStrikeParser - Beacon Config Extraction + +### Usage +```python +from cobalt_strike_parser import BeaconConfig +config = BeaconConfig.from_file("beacon.bin") +for key, value in config.items(): + print(f"{key}: {value}") +``` + +### Key Config Fields +| Field | Description | +|-------|-------------| +| `BeaconType` | HTTP, HTTPS, DNS, SMB | +| `C2Server` | Primary C2 URL | +| `SleepTime` | Beacon interval (ms) | +| `Jitter` | Jitter percentage | +| `UserAgent` | HTTP User-Agent string | +| `Watermark` | License watermark ID | + +## Suricata - Network IDS Rules + +### Rule Syntax +``` +alert -> (msg:""; ; sid:N; rev:N;) +``` + +### Key Keywords +| Keyword | Purpose | +|---------|---------| +| `http.method` | Match HTTP method | +| `http.uri` | Match request URI | +| `http.header` | Match header content | +| `ja3.hash` | Match JA3 TLS fingerprint | +| `dns.query` | Match DNS query name | +| `tls.cert_subject` | Match TLS certificate CN | +| `threshold` | Rate-based detection | diff --git a/personas/_shared/skills/analyzing-command-and-control-communication/scripts/agent.py b/personas/_shared/skills/analyzing-command-and-control-communication/scripts/agent.py new file mode 100644 index 0000000..dcd09f5 --- /dev/null +++ b/personas/_shared/skills/analyzing-command-and-control-communication/scripts/agent.py @@ -0,0 +1,214 @@ +#!/usr/bin/env python3 +"""C2 communication analysis agent for beacon detection and protocol decoding.""" + +import statistics +import base64 +import os +import sys +from collections import defaultdict + +try: + from scapy.all import rdpcap, IP, TCP, DNS, DNSQR + HAS_SCAPY = True +except ImportError: + HAS_SCAPY = False + +try: + import dpkt + HAS_DPKT = True +except ImportError: + HAS_DPKT = False + + +def detect_beacons(pcap_path, min_connections=5, max_jitter_pct=25.0): + """Analyze PCAP for periodic beacon patterns using TCP SYN timing.""" + if not HAS_SCAPY: + print("[ERROR] scapy not installed: pip install scapy") + return [] + packets = rdpcap(pcap_path) + connections = defaultdict(list) + for pkt in packets: + if IP in pkt and TCP in pkt and (pkt[TCP].flags & 0x02): + key = f"{pkt[IP].dst}:{pkt[TCP].dport}" + connections[key].append(float(pkt.time)) + beacons = [] + for dst, times in sorted(connections.items()): + if len(times) < min_connections: + continue + intervals = [times[i + 1] - times[i] for i in range(len(times) - 1)] + avg_interval = statistics.mean(intervals) + stdev = statistics.stdev(intervals) if len(intervals) > 1 else 0 + jitter_pct = (stdev / avg_interval * 100) if avg_interval > 0 else 0 + is_beacon = 5 < avg_interval < 7200 and jitter_pct < max_jitter_pct + record = { + "destination": dst, + "connections": len(times), + "duration_seconds": round(times[-1] - times[0], 1), + "avg_interval_seconds": round(avg_interval, 1), + "stdev_seconds": round(stdev, 1), + "jitter_percent": round(jitter_pct, 1), + "is_beacon": is_beacon, + } + if is_beacon: + beacons.append(record) + return beacons + + +def extract_http_requests(pcap_path): + """Extract HTTP requests from a PCAP file using dpkt.""" + if not HAS_DPKT: + print("[ERROR] dpkt not installed: pip install dpkt") + return [] + requests = [] + with open(pcap_path, "rb") as f: + pcap = dpkt.pcap.Reader(f) + for ts, buf in pcap: + try: + eth = dpkt.ethernet.Ethernet(buf) + if not isinstance(eth.data, dpkt.ip.IP): + continue + ip = eth.data + if not isinstance(ip.data, dpkt.tcp.TCP): + continue + tcp = ip.data + if len(tcp.data) == 0: + continue + try: + http = dpkt.http.Request(tcp.data) + decoded_body = None + if http.body: + try: + decoded_body = base64.b64decode(http.body).decode("utf-8", errors="replace") + except Exception: + decoded_body = http.body[:200] + requests.append({ + "timestamp": ts, + "src_ip": ".".join(str(b) for b in ip.src), + "dst_ip": ".".join(str(b) for b in ip.dst), + "dst_port": tcp.dport, + "method": http.method, + "uri": http.uri, + "host": http.headers.get("host", ""), + "user_agent": http.headers.get("user-agent", ""), + "body_size": len(http.body) if http.body else 0, + "decoded_body_preview": decoded_body, + }) + except (dpkt.dpkt.NeedData, dpkt.dpkt.UnpackError): + pass + except Exception: + continue + return requests + + +def extract_dns_queries(pcap_path): + """Extract DNS queries from a PCAP for C2 domain identification.""" + if not HAS_SCAPY: + return [] + packets = rdpcap(pcap_path) + queries = [] + for pkt in packets: + if DNS in pkt and pkt[DNS].qr == 0 and DNSQR in pkt: + qname = pkt[DNSQR].qname.decode("utf-8", errors="replace").rstrip(".") + queries.append({ + "src_ip": pkt[IP].src if IP in pkt else "?", + "query": qname, + "type": pkt[DNSQR].qtype, + }) + return queries + + +def identify_c2_framework(http_requests): + """Match HTTP request patterns against known C2 framework signatures.""" + cs_uris = ["/pixel", "/submit.php", "/__utm.gif", "/ca", "/dpixel", + "/push", "/visit.js", "/tab_icon"] + framework_hits = [] + for req in http_requests: + uri = req.get("uri", "") + ua = req.get("user_agent", "") + for cs_uri in cs_uris: + if cs_uri in uri: + framework_hits.append({ + "framework": "Cobalt Strike", + "indicator": f"URI pattern: {cs_uri}", + "request": req, + }) + break + if "MeterSSL" in ua or len(uri) == 5 and uri.startswith("/"): + framework_hits.append({ + "framework": "Metasploit/Meterpreter", + "indicator": f"URI/UA pattern: {uri} / {ua[:50]}", + "request": req, + }) + return framework_hits + + +def generate_suricata_rules(beacons, http_requests): + """Generate Suricata IDS rules from observed C2 patterns.""" + rules = [] + sid = 9000100 + for beacon in beacons: + dst_ip, dst_port = beacon["destination"].rsplit(":", 1) + rules.append( + f'alert tcp $HOME_NET any -> {dst_ip} {dst_port} (' + f'msg:"MALWARE Detected C2 Beacon to {dst_ip}:{dst_port}"; ' + f'flow:established,to_server; ' + f'threshold:type threshold, track by_src, count 5, seconds 600; ' + f'sid:{sid}; rev:1;)' + ) + sid += 1 + for req in http_requests[:5]: + if req.get("uri"): + uri = req["uri"] + rules.append( + f'alert http $HOME_NET any -> $EXTERNAL_NET any (' + f'msg:"MALWARE Suspected C2 HTTP Request {uri}"; ' + f'flow:established,to_server; ' + f'http.method; content:"{req["method"]}"; ' + f'http.uri; content:"{uri}"; ' + f'sid:{sid}; rev:1;)' + ) + sid += 1 + return rules + + +if __name__ == "__main__": + print("=" * 60) + print("C2 Communication Analysis Agent") + print("Beacon detection, protocol decoding, signature generation") + print("=" * 60) + + pcap_file = sys.argv[1] if len(sys.argv) > 1 else None + + if pcap_file and os.path.exists(pcap_file): + print(f"\n[*] Analyzing PCAP: {pcap_file}") + + print("\n--- Beacon Detection ---") + beacons = detect_beacons(pcap_file) + for b in beacons: + print(f"[!] BEACON: {b['destination']} " + f"interval={b['avg_interval_seconds']}s " + f"jitter={b['jitter_percent']}% " + f"sessions={b['connections']}") + + print("\n--- HTTP Requests ---") + http_reqs = extract_http_requests(pcap_file) + for r in http_reqs[:10]: + print(f" {r['method']} {r['host']}{r['uri']}") + + print("\n--- DNS Queries ---") + dns_qs = extract_dns_queries(pcap_file) + for q in dns_qs[:10]: + print(f" {q['src_ip']} -> {q['query']}") + + print("\n--- C2 Framework Identification ---") + hits = identify_c2_framework(http_reqs) + for h in hits: + print(f"[!] {h['framework']}: {h['indicator']}") + + print("\n--- Suricata Rules ---") + rules = generate_suricata_rules(beacons, http_reqs) + for r in rules: + print(r) + else: + print("\n[DEMO] Usage: python agent.py ") + print("[*] Provide a PCAP file to analyze for C2 communication patterns.") diff --git a/personas/_shared/skills/analyzing-cyber-kill-chain/LICENSE b/personas/_shared/skills/analyzing-cyber-kill-chain/LICENSE new file mode 100644 index 0000000..d885118 --- /dev/null +++ b/personas/_shared/skills/analyzing-cyber-kill-chain/LICENSE @@ -0,0 +1,201 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to the Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by the Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding any notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. Please do not remove or change + the license header comment from a contributed file except when + necessary. + + Copyright 2026 mukul975 + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/personas/_shared/skills/analyzing-cyber-kill-chain/SKILL.md b/personas/_shared/skills/analyzing-cyber-kill-chain/SKILL.md new file mode 100644 index 0000000..a399df0 --- /dev/null +++ b/personas/_shared/skills/analyzing-cyber-kill-chain/SKILL.md @@ -0,0 +1,140 @@ +--- +name: analyzing-cyber-kill-chain +description: 'Analyzes intrusion activity against the Lockheed Martin Cyber Kill Chain framework to identify which phases + an adversary has completed, where defenses succeeded or failed, and what controls would have interrupted the attack at earlier + phases. Use when conducting post-incident analysis, building prevention-focused security controls, or mapping detection + gaps to kill chain phases. Activates for requests involving kill chain analysis, intrusion kill chain, attack phase mapping, + or Lockheed Martin kill chain framework. + + ' +domain: cybersecurity +subdomain: threat-intelligence +tags: +- kill-chain +- Lockheed-Martin +- MITRE-ATT&CK +- intrusion-analysis +- defense-in-depth +- NIST-CSF +version: 1.0.0 +author: team-cybersecurity +license: Apache-2.0 +nist_csf: +- ID.RA-01 +- ID.RA-05 +- DE.CM-01 +- DE.AE-02 +--- +# Analyzing Cyber Kill Chain + +## When to Use + +Use this skill when: +- Conducting post-incident analysis to determine how far an adversary progressed through an attack sequence +- Designing layered defensive controls with the goal of interrupting attacks at the earliest possible phase +- Producing threat intelligence reports that communicate attack progression to non-technical stakeholders + +**Do not use** this skill as a standalone framework — combine with MITRE ATT&CK for technique-level granularity beyond what the 7-phase kill chain provides. + +## Prerequisites + +- Complete incident timeline with forensic artifacts mapped to specific adversary actions +- MITRE ATT&CK Enterprise matrix for technique-level mapping within each kill chain phase +- Access to threat intelligence on the suspected adversary group's typical kill chain progression +- Post-incident report or IR timeline from responding team + +## Workflow + +### Step 1: Map Observed Actions to Kill Chain Phases + +The Lockheed Martin Cyber Kill Chain consists of seven phases. Map all observed adversary actions: + +**Phase 1 - Reconnaissance**: Adversary gathers target information before attack. +- Indicators: DNS queries from adversary IP, LinkedIn scraping, job posting analysis, Shodan scans of organization infrastructure + +**Phase 2 - Weaponization**: Adversary creates attack tool (malware + exploit). +- Indicators: Malware compilation timestamps, exploit document metadata, builder artifacts in malware samples + +**Phase 3 - Delivery**: Adversary transmits weapon to target. +- Indicators: Phishing emails, malicious attachments, drive-by downloads, USB drops, supply chain compromise + +**Phase 4 - Exploitation**: Adversary exploits vulnerability to execute code. +- Indicators: CVE exploitation events in application/OS logs, memory corruption artifacts, shellcode execution + +**Phase 5 - Installation**: Adversary establishes persistence on target. +- Indicators: New scheduled tasks, registry run keys, service installation, web shells, bootkits + +**Phase 6 - Command & Control (C2)**: Adversary communicates with compromised system. +- Indicators: Beaconing traffic (regular intervals), DNS tunneling, HTTPS to uncommon domains, C2 framework signatures (Cobalt Strike, Sliver) + +**Phase 7 - Actions on Objectives**: Adversary achieves goals. +- Indicators: Data staging/exfiltration, lateral movement, ransomware execution, destructive activity + +### Step 2: Identify Phase Completion and Detection Points + +Create a phase matrix for the incident: +``` +Phase 1: Recon → Completed (undetected) +Phase 2: Weaponize → Completed (undetected — pre-attack) +Phase 3: Delivery → Completed; phishing email bypassed SEG +Phase 4: Exploit → Completed; CVE-2023-23397 exploited +Phase 5: Install → DETECTED: EDR flagged scheduled task creation (attack stalled here) +Phase 6: C2 → Not achieved (installation blocked) +Phase 7: Objectives → Not achieved +``` + +For each phase completed without detection, document the defensive control gap. + +### Step 3: Map to MITRE ATT&CK for Technique Detail + +Each kill chain phase maps to multiple ATT&CK tactics: +- Delivery → Initial Access (TA0001) +- Exploitation → Execution (TA0002) +- Installation → Persistence (TA0003), Privilege Escalation (TA0004) +- C2 → Command and Control (TA0011) +- Actions on Objectives → Exfiltration (TA0010), Impact (TA0040) + +Within each phase, enumerate specific ATT&CK techniques observed and map to existing detections. + +### Step 4: Identify Courses of Action per Phase + +For each phase, document applicable defensive courses of action (COAs): +- **Detect COA**: What detection would alert on adversary activity in this phase? +- **Deny COA**: What control would prevent the adversary from completing this phase? +- **Disrupt COA**: What control would interrupt the adversary mid-phase? +- **Degrade COA**: What control would reduce the adversary's effectiveness in this phase? +- **Deceive COA**: What deception (honeypots, canary tokens) would expose activity in this phase? +- **Destroy COA**: What active defense capability would neutralize adversary infrastructure? + +### Step 5: Produce Kill Chain Analysis Report + +Structure findings as: +1. Attack narrative (timeline of phases) +2. Phase-by-phase analysis with evidence +3. Detection point analysis (what worked, what failed) +4. Defensive recommendation per phase prioritized by cost/effectiveness +5. Control improvement roadmap + +## Key Concepts + +| Term | Definition | +|------|-----------| +| **Kill Chain** | Sequential model of adversary intrusion phases; breaking any link theoretically stops the attack | +| **Courses of Action (COA)** | Defensive responses mapped to each kill chain phase: detect, deny, disrupt, degrade, deceive, destroy | +| **Beaconing** | Regular, periodic C2 check-in pattern from compromised host to adversary server; detectable by frequency analysis | +| **Phase Completion** | Adversary successfully finishes a kill chain phase and progresses to the next; defense-in-depth aims to prevent this | +| **Intelligence Gain/Loss** | Analysis of whether detecting at Phase 5 (vs. Phase 3) reduced intelligence about adversary capabilities or intent | + +## Tools & Systems + +- **MITRE ATT&CK Navigator**: Overlay kill chain phases with ATT&CK technique coverage for integrated analysis +- **Elastic Security EQL**: Event Query Language for querying multi-phase attack sequences in Elastic SIEM +- **Splunk ES**: Timeline visualization and correlation searches for kill chain phase sequencing +- **MISP**: Kill chain tagging via galaxy clusters for structured incident event documentation + +## Common Pitfalls + +- **Linear assumption**: Adversaries don't always progress linearly — they may skip phases (weaponization already complete from previous campaign) or loop back (re-establish C2 after detection). +- **Ignoring Phases 1 and 2**: Reconnaissance and weaponization occur before the defender has visibility. Intelligence about these phases requires external sources (OSINT, threat intelligence). +- **Missing insider threats**: The kill chain was designed for external adversaries. Insider threats may skip directly to Phase 7 without traversing earlier phases. +- **Confusing with ATT&CK tactics**: The 7-phase kill chain and 14 ATT&CK tactics are complementary but not directly equivalent. Maintain distinction to prevent analytic confusion. diff --git a/personas/_shared/skills/analyzing-cyber-kill-chain/references/api-reference.md b/personas/_shared/skills/analyzing-cyber-kill-chain/references/api-reference.md new file mode 100644 index 0000000..9b2e436 --- /dev/null +++ b/personas/_shared/skills/analyzing-cyber-kill-chain/references/api-reference.md @@ -0,0 +1,96 @@ +# API Reference: Cyber Kill Chain Analysis Tools + +## Lockheed Martin Cyber Kill Chain Phases + +| Phase | Name | MITRE ATT&CK Tactic | +|-------|------|---------------------| +| 1 | Reconnaissance | TA0043 Reconnaissance | +| 2 | Weaponization | TA0042 Resource Development | +| 3 | Delivery | TA0001 Initial Access | +| 4 | Exploitation | TA0002 Execution | +| 5 | Installation | TA0003 Persistence, TA0004 Privilege Escalation | +| 6 | Command & Control | TA0011 Command and Control | +| 7 | Actions on Objectives | TA0010 Exfiltration, TA0040 Impact | + +## Courses of Action (COA) Matrix + +| COA | Description | +|-----|-------------| +| Detect | Alert on adversary activity | +| Deny | Prevent phase completion | +| Disrupt | Interrupt adversary mid-phase | +| Degrade | Reduce adversary effectiveness | +| Deceive | Expose activity via deception | +| Destroy | Neutralize adversary infrastructure | + +## MITRE ATT&CK Navigator + +### JSON Layer Format +```json +{ + "name": "Kill Chain Coverage", + "versions": {"navigator": "4.8", "layer": "4.4", "attack": "13"}, + "domain": "enterprise-attack", + "techniques": [ + {"techniqueID": "T1566", "color": "#ff6666", "comment": "Phase 3: Delivery"} + ] +} +``` + +### CLI Usage +```bash +# Export layer via ATT&CK Navigator API +curl -X POST https://mitre-attack.github.io/attack-navigator/api/layers \ + -d @layer.json -o coverage_map.svg +``` + +## Splunk - Kill Chain Phase Queries + +### Phase 3 Detection (Delivery) +```spl +index=email sourcetype=exchange action=delivered +| eval has_macro=if(match(attachment, "\.(docm|xlsm|pptm)$"), 1, 0) +| where has_macro=1 +| stats count by sender, subject, attachment +``` + +### Phase 6 Detection (C2) +```spl +index=proxy OR index=firewall +| stats count AS connections, dc(dest) AS unique_dests by src_ip +| where connections > 100 AND unique_dests < 3 +| sort - connections +``` + +## Elastic Security EQL + +### Multi-Phase Detection +```eql +sequence by host.name with maxspan=1h + [process where event.action == "start" and process.name == "WINWORD.EXE"] + [process where event.action == "start" and process.parent.name == "WINWORD.EXE"] + [network where destination.port == 443 and not destination.ip in ("known_good")] +``` + +## MISP - Kill Chain Tagging + +### Galaxy Cluster Tags +``` +misp-galaxy:kill-chain="reconnaissance" +misp-galaxy:kill-chain="delivery" +misp-galaxy:kill-chain="exploitation" +misp-galaxy:kill-chain="installation" +misp-galaxy:kill-chain="command-and-control" +misp-galaxy:kill-chain="actions-on-objectives" +``` + +### PyMISP Event Tagging +```python +from pymisp import PyMISP, MISPEvent + +misp = PyMISP("https://misp.example.com", "API_KEY") +event = MISPEvent() +event.add_tag("kill-chain:delivery") +event.add_tag("mitre-attack-pattern:T1566 - Phishing") +misp.update_event(event) +``` diff --git a/personas/_shared/skills/analyzing-cyber-kill-chain/scripts/agent.py b/personas/_shared/skills/analyzing-cyber-kill-chain/scripts/agent.py new file mode 100644 index 0000000..d1cef63 --- /dev/null +++ b/personas/_shared/skills/analyzing-cyber-kill-chain/scripts/agent.py @@ -0,0 +1,241 @@ +#!/usr/bin/env python3 +"""Cyber Kill Chain analysis agent for mapping incidents to Lockheed Martin kill chain phases.""" + +import datetime + + +KILL_CHAIN_PHASES = { + 1: { + "name": "Reconnaissance", + "description": "Adversary gathers target information", + "indicators": [ + "DNS queries from adversary IP", + "LinkedIn/social media scraping", + "Shodan/Censys scans of infrastructure", + "Job posting analysis for technology stack", + "WHOIS lookups on organization domains", + ], + "mitre_tactics": ["TA0043 - Reconnaissance"], + "coas": { + "detect": "Monitor for anomalous DNS lookups and port scans from single sources", + "deny": "Limit public-facing information, restrict DNS zone transfers", + "disrupt": "Block scanning IPs at perimeter firewall", + "degrade": "Return honeypot responses to recon probes", + "deceive": "Deploy decoy infrastructure and fake employee profiles", + }, + }, + 2: { + "name": "Weaponization", + "description": "Adversary creates attack tool (malware + exploit)", + "indicators": [ + "Malware compilation timestamps", + "Exploit document metadata", + "Builder tool artifacts in samples", + "Reused infrastructure from previous campaigns", + ], + "mitre_tactics": ["TA0042 - Resource Development"], + "coas": { + "detect": "Threat intelligence on adversary tooling and TTPs", + "deny": "Patch vulnerabilities targeted by known exploit kits", + "disrupt": "N/A (occurs outside defender visibility)", + "degrade": "Application hardening reduces exploit reliability", + "deceive": "Share deceptive vulnerability information", + }, + }, + 3: { + "name": "Delivery", + "description": "Adversary transmits weapon to target", + "indicators": [ + "Phishing emails with malicious attachments", + "Drive-by download URLs", + "USB device insertion events", + "Supply chain compromise artifacts", + "Watering hole website modifications", + ], + "mitre_tactics": ["TA0001 - Initial Access"], + "coas": { + "detect": "Email security gateway alerts, proxy URL filtering alerts", + "deny": "Block malicious attachments, URL filtering, USB device control", + "disrupt": "Quarantine suspicious emails before delivery", + "degrade": "Sandbox detonation of attachments delays delivery", + "deceive": "Canary documents in email attachments", + }, + }, + 4: { + "name": "Exploitation", + "description": "Adversary exploits vulnerability to execute code", + "indicators": [ + "CVE exploitation in application logs", + "Memory corruption crash dumps", + "Shellcode execution artifacts", + "Exploit kit landing page access", + ], + "mitre_tactics": ["TA0002 - Execution"], + "coas": { + "detect": "EDR behavioral detection, exploit guard alerts", + "deny": "Patch management, application whitelisting", + "disrupt": "ASLR, DEP, CFG memory protections", + "degrade": "Sandboxed application execution (Protected View)", + "deceive": "Honeypot applications with fake vulnerabilities", + }, + }, + 5: { + "name": "Installation", + "description": "Adversary establishes persistence on target", + "indicators": [ + "New scheduled tasks or services", + "Registry Run key modifications", + "Web shell deployment", + "Startup folder additions", + "DLL search-order hijacking", + ], + "mitre_tactics": ["TA0003 - Persistence", "TA0004 - Privilege Escalation"], + "coas": { + "detect": "Sysmon EventID 11/12/13, EDR persistence monitoring", + "deny": "Application whitelisting, UAC enforcement", + "disrupt": "Real-time file integrity monitoring alerts", + "degrade": "Restrict write access to system directories", + "deceive": "Canary registry keys and file system canaries", + }, + }, + 6: { + "name": "Command & Control", + "description": "Adversary communicates with compromised system", + "indicators": [ + "Beaconing traffic at regular intervals", + "DNS tunneling (high entropy subdomain queries)", + "HTTPS to newly registered domains", + "Known C2 framework signatures", + ], + "mitre_tactics": ["TA0011 - Command and Control"], + "coas": { + "detect": "Network beacon analysis, JA3 fingerprinting, DNS monitoring", + "deny": "DNS sinkholing, firewall egress filtering", + "disrupt": "TLS inspection to identify C2 in encrypted traffic", + "degrade": "Rate-limit suspicious outbound connections", + "deceive": "C2 interception and response manipulation", + }, + }, + 7: { + "name": "Actions on Objectives", + "description": "Adversary achieves mission goals", + "indicators": [ + "Data staging and exfiltration", + "Lateral movement to additional systems", + "Ransomware encryption activity", + "Destructive operations (wiper malware)", + "Credential dumping (LSASS access)", + ], + "mitre_tactics": ["TA0010 - Exfiltration", "TA0040 - Impact"], + "coas": { + "detect": "DLP alerts, anomalous data transfers, UEBA", + "deny": "Network segmentation, data classification controls", + "disrupt": "Isolate compromised systems, kill C2 connections", + "degrade": "Encrypt sensitive data at rest (attacker gets ciphertext)", + "deceive": "Canary files and honeytoken credentials", + }, + }, +} + + +def map_event_to_phase(event_description): + """Map an incident event description to the most likely kill chain phase.""" + event_lower = event_description.lower() + keyword_phase_map = { + 1: ["recon", "scan", "enumerat", "shodan", "whois", "dns lookup"], + 2: ["weaponiz", "builder", "compile", "payload creat"], + 3: ["phish", "email", "deliver", "download", "usb", "attachment", "watering hole"], + 4: ["exploit", "cve-", "buffer overflow", "shellcode", "rce"], + 5: ["persist", "scheduled task", "registry", "run key", "service install", + "web shell", "backdoor", "startup"], + 6: ["beacon", "c2", "c&c", "command and control", "callback", "dns tunnel"], + 7: ["exfiltrat", "lateral", "ransomware", "encrypt", "data stag", "credential dump", + "mimikatz", "wiper"], + } + scores = {phase: 0 for phase in range(1, 8)} + for phase, keywords in keyword_phase_map.items(): + for kw in keywords: + if kw in event_lower: + scores[phase] += 1 + best_phase = max(scores, key=scores.get) + if scores[best_phase] == 0: + return None + return best_phase + + +def analyze_incident(events): + """Analyze a list of incident events and map to kill chain phases.""" + analysis = {phase: {"events": [], "detected": False, "completed": False} + for phase in range(1, 8)} + for event in events: + phase = map_event_to_phase(event.get("description", "")) + if phase: + analysis[phase]["events"].append(event) + analysis[phase]["completed"] = True + if event.get("detected", False): + analysis[phase]["detected"] = True + return analysis + + +def generate_report(analysis): + """Generate a kill chain analysis report.""" + report_lines = [ + "CYBER KILL CHAIN ANALYSIS REPORT", + "=" * 50, + f"Generated: {datetime.datetime.utcnow().isoformat()}Z", + "", + ] + deepest_phase = 0 + detection_phase = None + for phase_num in range(1, 8): + phase_data = analysis[phase_num] + phase_info = KILL_CHAIN_PHASES[phase_num] + if phase_data["completed"]: + deepest_phase = phase_num + if phase_data["detected"] and detection_phase is None: + detection_phase = phase_num + status = "COMPLETED" if phase_data["completed"] else "NOT REACHED" + if phase_data["detected"]: + status += " (DETECTED)" + report_lines.append(f"Phase {phase_num}: {phase_info['name']} -> {status}") + for evt in phase_data["events"]: + report_lines.append(f" - {evt.get('description', 'N/A')}") + report_lines.extend([ + "", + f"Deepest phase reached: {deepest_phase} ({KILL_CHAIN_PHASES.get(deepest_phase, {}).get('name', 'N/A')})", + f"First detection at phase: {detection_phase or 'None'}", + "", + "RECOMMENDED COURSES OF ACTION:", + ]) + for phase_num in range(1, deepest_phase + 1): + phase_info = KILL_CHAIN_PHASES[phase_num] + report_lines.append(f"\n Phase {phase_num} - {phase_info['name']}:") + for coa_type, coa_desc in phase_info["coas"].items(): + report_lines.append(f" {coa_type.upper()}: {coa_desc}") + return "\n".join(report_lines) + + +if __name__ == "__main__": + print("=" * 60) + print("Cyber Kill Chain Analysis Agent") + print("Lockheed Martin framework mapping with MITRE ATT&CK integration") + print("=" * 60) + + # Demo incident events + demo_events = [ + {"description": "Shodan scans detected from 203.0.113.50 targeting web servers", + "timestamp": "2025-09-10T08:00:00Z", "detected": False}, + {"description": "Phishing email with malicious .docm attachment delivered to 5 users", + "timestamp": "2025-09-11T09:15:00Z", "detected": False}, + {"description": "CVE-2023-23397 exploitation detected in Outlook process crash", + "timestamp": "2025-09-11T09:20:00Z", "detected": False}, + {"description": "Scheduled task created for persistence by malware dropper", + "timestamp": "2025-09-11T09:25:00Z", "detected": True}, + {"description": "C2 beacon detected to 185.220.101.42 on port 443", + "timestamp": "2025-09-11T09:30:00Z", "detected": True}, + ] + + print("\n[*] Analyzing demo incident events...") + analysis = analyze_incident(demo_events) + report = generate_report(analysis) + print(f"\n{report}") diff --git a/personas/_shared/skills/analyzing-disk-image-with-autopsy/LICENSE b/personas/_shared/skills/analyzing-disk-image-with-autopsy/LICENSE new file mode 100644 index 0000000..d885118 --- /dev/null +++ b/personas/_shared/skills/analyzing-disk-image-with-autopsy/LICENSE @@ -0,0 +1,201 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to the Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by the Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding any notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. Please do not remove or change + the license header comment from a contributed file except when + necessary. + + Copyright 2026 mukul975 + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/personas/_shared/skills/analyzing-disk-image-with-autopsy/SKILL.md b/personas/_shared/skills/analyzing-disk-image-with-autopsy/SKILL.md new file mode 100644 index 0000000..56b6641 --- /dev/null +++ b/personas/_shared/skills/analyzing-disk-image-with-autopsy/SKILL.md @@ -0,0 +1,264 @@ +--- +name: analyzing-disk-image-with-autopsy +description: Perform comprehensive forensic analysis of disk images using Autopsy to recover files, examine artifacts, and + build investigation timelines. +domain: cybersecurity +subdomain: digital-forensics +tags: +- forensics +- autopsy +- disk-analysis +- sleuth-kit +- file-recovery +- artifact-analysis +version: '1.0' +author: mahipal +license: Apache-2.0 +nist_csf: +- RS.AN-01 +- RS.AN-03 +- DE.AE-02 +- RS.MA-01 +--- + +# Analyzing Disk Image with Autopsy + +## When to Use +- When you have a forensic disk image and need structured analysis of its contents +- During investigations requiring file recovery, keyword searching, and timeline analysis +- When non-technical stakeholders need visual reports from forensic evidence +- For examining file system metadata, deleted files, and embedded artifacts +- When building a comprehensive case from multiple disk images + +## Prerequisites +- Autopsy 4.x installed (Windows) or Autopsy 4.x with The Sleuth Kit (Linux) +- Forensic disk image in raw (dd), E01 (EnCase), or AFF format +- Minimum 8GB RAM (16GB recommended for large images) +- Java Runtime Environment (JRE) 8+ for Autopsy +- Sufficient disk space for the Autopsy case database (2-3x image size) +- Hash databases (NSRL, known-bad hashes) for file identification + +## Workflow + +### Step 1: Install Autopsy and Configure Environment + +```bash +# On Linux, install Sleuth Kit and Autopsy +sudo apt-get install autopsy sleuthkit + +# Download Autopsy 4.x (GUI version) from official source +wget https://github.com/sleuthkit/autopsy/releases/download/autopsy-4.21.0/autopsy-4.21.0.zip +unzip autopsy-4.21.0.zip -d /opt/autopsy + +# On Windows, run the MSI installer from sleuthkit.org +# Launch Autopsy +/opt/autopsy/bin/autopsy --nosplash + +# For Sleuth Kit command-line analysis alongside Autopsy +sudo apt-get install sleuthkit +``` + +### Step 2: Create a New Case and Add the Disk Image + +``` +1. Launch Autopsy > "New Case" +2. Enter Case Name: "CASE-2024-001-Workstation" +3. Set Base Directory: /cases/case-2024-001/autopsy/ +4. Enter Case Number, Examiner Name +5. Click "Add Data Source" +6. Select "Disk Image or VM File" +7. Browse to: /cases/case-2024-001/images/evidence.dd +8. Select Time Zone of the original system +9. Configure Ingest Modules (see Step 3) +``` + +```bash +# Alternatively, use Sleuth Kit CLI to verify the image first +img_stat /cases/case-2024-001/images/evidence.dd + +# List partitions in the image +mmls /cases/case-2024-001/images/evidence.dd + +# Output example: +# DOS Partition Table +# Offset Sector: 0 +# Units are in 512-byte sectors +# Slot Start End Length Description +# 00: ----- 0000000000 0000002047 0000002048 Primary Table (#0) +# 01: 00:00 0000002048 0001026047 0001024000 NTFS (0x07) +# 02: 00:01 0001026048 0976771071 0975745024 NTFS (0x07) + +# List files in a partition (offset 2048 sectors) +fls -o 2048 /cases/case-2024-001/images/evidence.dd +``` + +### Step 3: Configure and Run Ingest Modules + +``` +Enable the following Autopsy Ingest Modules: +- Recent Activity: Extracts browser history, downloads, cookies, bookmarks +- Hash Lookup: Compares files against NSRL and known-bad hash sets +- File Type Identification: Identifies files by signature, not extension +- Keyword Search: Indexes content for full-text searching +- Email Parser: Extracts emails from PST, MBOX, EML files +- Extension Mismatch Detector: Finds files with wrong extensions +- Exif Parser: Extracts metadata from images (GPS, camera, timestamps) +- Encryption Detection: Identifies encrypted files and containers +- Interesting Files Identifier: Flags files matching custom rule sets +- Embedded File Extractor: Extracts files from ZIP, Office docs, PDFs +- Picture Analyzer: Categorizes images using PhotoDNA or hash matching +- Data Source Integrity: Verifies image hash during ingest +``` + +```bash +# Configure NSRL hash set for known-good filtering +# Download NSRL from https://www.nist.gov/itl/ssd/software-quality-group/national-software-reference-library-nsrl +wget https://s3.amazonaws.com/rds.nsrl.nist.gov/RDS/current/rds_modernm.zip +unzip rds_modernm.zip -d /opt/autopsy/hashsets/ + +# Import into Autopsy: +# Tools > Options > Hash Sets > Import > Select NSRLFile.txt +# Mark as "Known" (to filter out known-good files) +``` + +### Step 4: Analyze File System and Recover Deleted Files + +```bash +# In Autopsy GUI: Navigate tree structure +# - Data Sources > evidence.dd > vol2 (NTFS) +# - Examine directory tree, note deleted files (marked with X) + +# Using Sleuth Kit CLI for targeted recovery +# List deleted files +fls -rd -o 2048 /cases/case-2024-001/images/evidence.dd + +# Recover a specific deleted file by inode +icat -o 2048 /cases/case-2024-001/images/evidence.dd 14523 > /cases/case-2024-001/recovered/deleted_document.docx + +# Extract all files from a directory +tsk_recover -o 2048 -d /Users/suspect/Documents \ + /cases/case-2024-001/images/evidence.dd \ + /cases/case-2024-001/recovered/documents/ + +# Get detailed file metadata +istat -o 2048 /cases/case-2024-001/images/evidence.dd 14523 +# Shows: creation, modification, access, MFT change timestamps, size, data runs +``` + +### Step 5: Perform Keyword Searches and Tag Evidence + +``` +In Autopsy: +1. Keyword Search panel > "Ad Hoc Keyword Search" +2. Search terms: credit card patterns, SSN regex, email addresses +3. Example regex for credit cards: \b(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14})\b +4. Example regex for SSN: \b\d{3}-\d{2}-\d{4}\b +5. Review results > Right-click items > "Add Tag" +6. Create tags: "Evidence-Critical", "Evidence-Supporting", "Requires-Review" +7. Add comments to tagged items documenting relevance +``` + +```bash +# Using Sleuth Kit for CLI keyword search +srch_strings -a -o 2048 /cases/case-2024-001/images/evidence.dd | \ + grep -iE '(password|secret|confidential)' > /cases/case-2024-001/keyword_hits.txt + +# Search for specific file signatures +sigfind -o 2048 /cases/case-2024-001/images/evidence.dd 25504446 +# 25504446 = %PDF header signature +``` + +### Step 6: Build Timeline and Generate Reports + +``` +In Autopsy: +1. Timeline viewer: Tools > Timeline +2. Select date range of interest (incident window) +3. Filter by event type: File Created, Modified, Accessed, Web Activity +4. Zoom into suspicious time periods +5. Export timeline events as CSV for external analysis + +Generate Report: +1. Generate Report > HTML Report +2. Select tagged items and data sources to include +3. Configure report sections: file listings, keyword hits, timeline +4. Export to /cases/case-2024-001/reports/ +``` + +```bash +# Using Sleuth Kit mactime for CLI timeline +fls -r -m "/" -o 2048 /cases/case-2024-001/images/evidence.dd > /cases/case-2024-001/bodyfile.txt + +# Generate timeline from bodyfile +mactime -b /cases/case-2024-001/bodyfile.txt -d > /cases/case-2024-001/timeline.csv + +# Filter timeline to specific date range +mactime -b /cases/case-2024-001/bodyfile.txt \ + -d 2024-01-15..2024-01-20 > /cases/case-2024-001/incident_timeline.csv +``` + +## Key Concepts + +| Concept | Description | +|---------|-------------| +| Ingest Modules | Automated analysis plugins that process data sources upon import | +| MFT (Master File Table) | NTFS metadata structure recording all file entries and attributes | +| File carving | Recovering files from unallocated space using file signatures | +| Hash filtering | Using NSRL or custom hash sets to exclude known-good or flag known-bad files | +| Timeline analysis | Chronological reconstruction of file system and user activity events | +| Deleted file recovery | Restoring files whose directory entries are removed but data remains | +| Keyword indexing | Full-text search index built from all file content including slack space | +| Artifact extraction | Automated parsing of browser, email, registry, and OS-specific artifacts | + +## Tools & Systems + +| Tool | Purpose | +|------|---------| +| Autopsy | Open-source GUI forensic platform for disk image analysis | +| The Sleuth Kit (TSK) | Command-line forensic toolkit underlying Autopsy | +| fls | List files and directories in a disk image including deleted entries | +| icat | Extract file content by inode number from a disk image | +| mactime | Generate timeline from TSK bodyfile format | +| mmls | Display partition layout of a disk image | +| NSRL | NIST hash database for identifying known software files | +| sigfind | Search for file signatures at the sector level | + +## Common Scenarios + +**Scenario 1: Employee Data Theft Investigation** +Import the employee workstation image, run all ingest modules, search for company-confidential file names and keywords, examine USB connection artifacts in Recent Activity, check for cloud storage client artifacts, review deleted files for evidence of data staging, generate HTML report for legal team. + +**Scenario 2: Malware Infection Forensics** +Add the compromised system image, enable Extension Mismatch and Encryption Detection modules, examine the prefetch directory for execution evidence, search for known malware hashes, build timeline around the infection window, extract suspicious executables for further analysis in a sandbox. + +**Scenario 3: Child Exploitation Material (CSAM) Investigation** +Import image with PhotoDNA and Project VIC hash sets enabled, run Picture Analyzer module, hash all image files against known-bad databases, tag and categorize matches by severity, generate law enforcement report with chain of custody documentation. + +**Scenario 4: Intellectual Property Dispute** +Import multiple employee disk images as separate data sources in one case, perform keyword searches for proprietary terms and project names, compare file hashes between sources, build timeline showing file access and transfer patterns, export evidence for legal review. + +## Output Format + +``` +Autopsy Case Analysis Summary: + Case: CASE-2024-001-Workstation + Image: evidence.dd (500GB NTFS) + Partitions: 2 (System Reserved + Primary) + Total Files: 245,832 + Deleted Files: 12,456 (recoverable: 8,234) + + Ingest Results: + Hash Matches (Known Bad): 3 files + Extension Mismatches: 17 files + Keyword Hits: 234 across 45 files + Encrypted Files: 5 containers detected + EXIF Data Extracted: 1,245 images with metadata + + Tagged Evidence: + Critical: 12 items + Supporting: 34 items + Review: 67 items + + Timeline Events: 1,234,567 entries (filtered to incident window: 892) + Report: /cases/case-2024-001/reports/autopsy_report.html +``` diff --git a/personas/_shared/skills/analyzing-disk-image-with-autopsy/references/api-reference.md b/personas/_shared/skills/analyzing-disk-image-with-autopsy/references/api-reference.md new file mode 100644 index 0000000..5ed560c --- /dev/null +++ b/personas/_shared/skills/analyzing-disk-image-with-autopsy/references/api-reference.md @@ -0,0 +1,118 @@ +# API Reference: Autopsy and The Sleuth Kit (TSK) + +## mmls - Partition Layout + +### Syntax +```bash +mmls +mmls -t dos # Force DOS partition table +mmls -t gpt # Force GPT partition table +``` + +### Output Format +``` +DOS Partition Table +Offset Sector: 0 + Slot Start End Length Description + 00: 00:00 0000002048 0001026047 0001024000 NTFS (0x07) +``` + +## fls - File Listing + +### Syntax +```bash +fls -o # List root directory +fls -r -o # Recursive listing +fls -rd -o # Deleted files only, recursive +fls -m "/" -r -o # Bodyfile format for mactime +``` + +### Flags +| Flag | Description | +|------|-------------| +| `-r` | Recursive listing | +| `-d` | Deleted entries only | +| `-D` | Directories only | +| `-m "/"` | Output in bodyfile format with mount point | +| `-o` | Partition sector offset | + +## icat - File Extraction by Inode + +### Syntax +```bash +icat -o > recovered_file +icat -r -o > file # Recover slack space +``` + +## istat - File Metadata + +### Syntax +```bash +istat -o +``` + +### Output Includes +- MFT entry number and sequence +- File creation, modification, access, MFT change timestamps +- File size and data run locations +- Attribute list (NTFS: $STANDARD_INFORMATION, $FILE_NAME, $DATA) + +## mactime - Timeline Generation + +### Syntax +```bash +mactime -b -d > timeline.csv +mactime -b -d 2024-01-15..2024-01-20 > filtered.csv +mactime -b -z UTC -d > timeline_utc.csv +``` + +### Output Columns +``` +Date,Size,Type,Mode,UID,GID,Meta,File Name +``` + +## img_stat - Image Information + +### Syntax +```bash +img_stat +``` + +## sigfind - File Signature Search + +### Syntax +```bash +sigfind -o +sigfind -o 2048 evidence.dd 25504446 # Find %PDF headers +sigfind -o 2048 evidence.dd 504B0304 # Find ZIP/DOCX headers +``` + +### Common Signatures +| Hex | File Type | +|-----|-----------| +| `FFD8FF` | JPEG | +| `89504E47` | PNG | +| `25504446` | PDF | +| `504B0304` | ZIP/DOCX/XLSX | +| `D0CF11E0` | OLE (DOC/XLS) | + +## srch_strings - Keyword Search + +### Syntax +```bash +srch_strings -a -o | grep -i "keyword" +srch_strings -t d # Print offset in decimal +``` + +## Autopsy GUI Ingest Modules + +| Module | Function | +|--------|----------| +| Recent Activity | Browser history, downloads, cookies | +| Hash Lookup | NSRL and known-bad hash matching | +| File Type Identification | Signature-based file type detection | +| Keyword Search | Full-text content indexing | +| Email Parser | PST/MBOX/EML extraction | +| Extension Mismatch | Wrong file extension detection | +| Embedded File Extractor | ZIP, Office, PDF extraction | +| Encryption Detection | Encrypted container identification | diff --git a/personas/_shared/skills/analyzing-disk-image-with-autopsy/scripts/agent.py b/personas/_shared/skills/analyzing-disk-image-with-autopsy/scripts/agent.py new file mode 100644 index 0000000..4ad4819 --- /dev/null +++ b/personas/_shared/skills/analyzing-disk-image-with-autopsy/scripts/agent.py @@ -0,0 +1,218 @@ +#!/usr/bin/env python3 +"""Forensic disk image analysis agent using The Sleuth Kit (TSK) command-line tools.""" + +import shlex +import subprocess +import os +import sys +import json +import csv +import datetime + + +def run_cmd(cmd): + """Execute a command and return output.""" + if isinstance(cmd, str): + cmd = shlex.split(cmd) + result = subprocess.run(cmd, capture_output=True, text=True, timeout=120) + return result.stdout.strip(), result.stderr.strip(), result.returncode + + +def get_image_info(image_path): + """Retrieve disk image metadata using img_stat.""" + stdout, _, rc = run_cmd(f"img_stat {image_path}") + if rc == 0: + info = {} + for line in stdout.splitlines(): + if ":" in line: + key, _, val = line.partition(":") + info[key.strip()] = val.strip() + return info + return None + + +def list_partitions(image_path): + """List partition layout using mmls.""" + stdout, _, rc = run_cmd(f"mmls {image_path}") + partitions = [] + if rc == 0: + for line in stdout.splitlines(): + parts = line.split() + if len(parts) >= 6 and parts[2].isdigit(): + partitions.append({ + "slot": parts[0].rstrip(":"), + "start": int(parts[2]), + "end": int(parts[3]), + "length": int(parts[4]), + "description": " ".join(parts[5:]), + }) + return partitions + + +def list_files(image_path, offset, path="/", recursive=False): + """List files in a partition using fls.""" + flags = "-r" if recursive else "" + cmd = f"fls {flags} -o {offset} {image_path}" + if path != "/": + cmd += f" -D {path}" + stdout, _, rc = run_cmd(cmd) + files = [] + if rc == 0: + for line in stdout.splitlines(): + line = line.strip() + if not line: + continue + parts = line.split("\t", 1) + if len(parts) == 2: + meta = parts[0].strip() + name = parts[1].strip() + deleted = meta.startswith("*") + file_type = "d" if "d/" in meta else "r" + inode = "" + for token in meta.split(): + if "-" in token and token.replace("-", "").isdigit(): + inode = token + break + files.append({ + "name": name, + "inode": inode, + "type": "directory" if file_type == "d" else "file", + "deleted": deleted, + }) + return files + + +def list_deleted_files(image_path, offset): + """List only deleted files using fls -rd.""" + stdout, _, rc = run_cmd(f"fls -rd -o {offset} {image_path}") + deleted = [] + if rc == 0: + for line in stdout.splitlines(): + line = line.strip() + if line: + deleted.append(line) + return deleted + + +def recover_file(image_path, offset, inode, output_path): + """Recover a file by inode using icat.""" + result = subprocess.run( + ["icat", "-o", str(offset), image_path, str(inode)], + capture_output=True, + timeout=120, + ) + if result.returncode == 0: + with open(output_path, "wb") as f: + f.write(result.stdout) + return result.returncode == 0 + + +def get_file_metadata(image_path, offset, inode): + """Get detailed file metadata using istat.""" + stdout, _, rc = run_cmd(f"istat -o {offset} {image_path} {inode}") + return stdout if rc == 0 else None + + +def create_bodyfile(image_path, offset, output_path): + """Generate a TSK bodyfile for timeline creation.""" + result = subprocess.run( + ["fls", "-r", "-m", "/", "-o", str(offset), image_path], + capture_output=True, text=True, + timeout=120, + ) + if result.returncode == 0: + with open(output_path, "w") as f: + f.write(result.stdout) + return result.returncode == 0 + + +def generate_timeline(bodyfile_path, output_csv, start_date=None, end_date=None): + """Generate a timeline from a bodyfile using mactime.""" + cmd = ["mactime", "-b", bodyfile_path, "-d"] + if start_date and end_date: + cmd.append(f"{start_date}..{end_date}") + result = subprocess.run(cmd, capture_output=True, text=True, timeout=120) + if result.returncode == 0: + with open(output_csv, "w") as f: + f.write(result.stdout) + return result.returncode == 0 + + +def search_keywords(image_path, offset, keyword): + """Search for keyword strings in the disk image.""" + result = subprocess.run( + ["srch_strings", "-a", "-o", str(offset), image_path], + capture_output=True, text=True, + timeout=120, + ) + if result.returncode != 0 or not result.stdout: + return [] + keyword_lower = keyword.lower() + return [line for line in result.stdout.splitlines() if keyword_lower in line.lower()] + + +def find_file_signature(image_path, offset, hex_signature): + """Find file signatures at the sector level using sigfind.""" + stdout, _, rc = run_cmd(f"sigfind -o {offset} {image_path} {hex_signature}") + return stdout if rc == 0 else None + + +def analyze_image(image_path, case_dir): + """Run a full automated analysis workflow on a disk image.""" + os.makedirs(case_dir, exist_ok=True) + results = {"image": image_path, "timestamp": datetime.datetime.utcnow().isoformat()} + + print(f"[*] Image info...") + results["image_info"] = get_image_info(image_path) + + print(f"[*] Partition layout...") + partitions = list_partitions(image_path) + results["partitions"] = partitions + + for part in partitions: + if "NTFS" in part.get("description", "") or "Linux" in part.get("description", ""): + offset = part["start"] + print(f"[*] Listing files at offset {offset} ({part['description']})...") + files = list_files(image_path, offset, recursive=True) + results[f"files_offset_{offset}"] = { + "total": len(files), + "deleted": sum(1 for f in files if f["deleted"]), + } + print(f" Total: {len(files)}, Deleted: {results[f'files_offset_{offset}']['deleted']}") + + print(f"[*] Creating bodyfile for timeline...") + bf_path = os.path.join(case_dir, f"bodyfile_{offset}.txt") + create_bodyfile(image_path, offset, bf_path) + + tl_path = os.path.join(case_dir, f"timeline_{offset}.csv") + generate_timeline(bf_path, tl_path) + + report_path = os.path.join(case_dir, "analysis_summary.json") + with open(report_path, "w") as f: + json.dump(results, f, indent=2, default=str) + print(f"[*] Summary saved to {report_path}") + return results + + +if __name__ == "__main__": + print("=" * 60) + print("Disk Image Forensic Analysis Agent") + print("Tools: The Sleuth Kit (fls, icat, mmls, mactime)") + print("=" * 60) + + if len(sys.argv) > 1: + image = sys.argv[1] + import tempfile + case = sys.argv[2] if len(sys.argv) > 2 else os.environ.get("AUTOPSY_CASE_DIR", os.path.join(tempfile.gettempdir(), "autopsy_case")) + if os.path.exists(image): + analyze_image(image, case) + else: + print(f"[ERROR] Image not found: {image}") + else: + print("\n[DEMO] Usage: python agent.py [case_directory]") + print("[*] Supported operations:") + print(" - Partition enumeration (mmls)") + print(" - File listing with deleted file recovery (fls, icat)") + print(" - Timeline generation (mactime)") + print(" - Keyword searching (srch_strings)") + print(" - File signature detection (sigfind)") diff --git a/personas/_shared/skills/analyzing-dns-logs-for-exfiltration/LICENSE b/personas/_shared/skills/analyzing-dns-logs-for-exfiltration/LICENSE new file mode 100644 index 0000000..d885118 --- /dev/null +++ b/personas/_shared/skills/analyzing-dns-logs-for-exfiltration/LICENSE @@ -0,0 +1,201 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to the Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by the Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding any notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. Please do not remove or change + the license header comment from a contributed file except when + necessary. + + Copyright 2026 mukul975 + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/personas/_shared/skills/analyzing-dns-logs-for-exfiltration/SKILL.md b/personas/_shared/skills/analyzing-dns-logs-for-exfiltration/SKILL.md new file mode 100644 index 0000000..83a0891 --- /dev/null +++ b/personas/_shared/skills/analyzing-dns-logs-for-exfiltration/SKILL.md @@ -0,0 +1,304 @@ +--- +name: analyzing-dns-logs-for-exfiltration +description: 'Analyzes DNS query logs to detect data exfiltration via DNS tunneling, DGA domain communication, and covert + C2 channels using entropy analysis, query volume anomalies, and subdomain length detection in SIEM platforms. Use when SOC + teams need to identify DNS-based threats that bypass traditional network security controls. + + ' +domain: cybersecurity +subdomain: soc-operations +tags: +- soc +- dns +- exfiltration +- dns-tunneling +- dga +- c2-detection +- splunk +- threat-detection +version: '1.0' +author: mahipal +license: Apache-2.0 +atlas_techniques: +- AML.T0024 +- AML.T0056 +- AML.T0086 +nist_csf: +- DE.CM-01 +- DE.AE-02 +- RS.MA-01 +- DE.AE-06 +--- +# Analyzing DNS Logs for Exfiltration + +## When to Use + +Use this skill when: +- SOC teams suspect data exfiltration through DNS tunneling to bypass firewall/proxy controls +- Threat intelligence indicates adversaries using DNS-based C2 channels (e.g., Cobalt Strike DNS beacon) +- UEBA detects anomalous DNS query volumes from specific hosts +- Malware analysis reveals DNS-over-HTTPS (DoH) or DNS tunneling capabilities + +**Do not use** for standard DNS troubleshooting or availability monitoring — this skill focuses on security-relevant DNS abuse detection. + +## Prerequisites + +- DNS query logging enabled (Windows DNS Server, Bind, Infoblox, or Cisco Umbrella) +- DNS logs ingested into SIEM (Splunk with `Stream:DNS`, `dns` sourcetype, or Zeek DNS logs) +- Passive DNS data for historical domain resolution analysis +- Baseline of normal DNS behavior (query volume, domain distribution, TXT record frequency) +- Python with `math` and `collections` libraries for entropy calculation + +## Workflow + +### Step 1: Detect DNS Tunneling via Subdomain Length Analysis + +DNS tunneling encodes data in subdomain labels, creating unusually long queries: + +```spl +index=dns sourcetype="stream:dns" query_type IN ("A", "AAAA", "TXT", "CNAME", "MX") +| eval domain_parts = split(query, ".") +| eval subdomain = mvindex(domain_parts, 0, mvcount(domain_parts)-3) +| eval subdomain_str = mvjoin(subdomain, ".") +| eval subdomain_len = len(subdomain_str) +| eval tld = mvindex(domain_parts, -1) +| eval registered_domain = mvindex(domain_parts, -2).".".tld +| where subdomain_len > 50 +| stats count AS queries, dc(query) AS unique_queries, + avg(subdomain_len) AS avg_subdomain_len, + max(subdomain_len) AS max_subdomain_len, + values(src_ip) AS sources + by registered_domain +| where queries > 20 +| sort - avg_subdomain_len +| table registered_domain, queries, unique_queries, avg_subdomain_len, max_subdomain_len, sources +``` + +### Step 2: Detect High-Entropy Domain Queries (DGA Detection) + +Domain Generation Algorithms produce random-looking domains: + +```spl +index=dns sourcetype="stream:dns" +| eval domain_parts = split(query, ".") +| eval sld = mvindex(domain_parts, -2) +| eval sld_len = len(sld) +| eval char_count = sld_len +| eval vowels = len(replace(sld, "[^aeiou]", "")) +| eval consonants = len(replace(sld, "[^bcdfghjklmnpqrstvwxyz]", "")) +| eval digits = len(replace(sld, "[^0-9]", "")) +| eval vowel_ratio = if(char_count > 0, vowels / char_count, 0) +| eval digit_ratio = if(char_count > 0, digits / char_count, 0) +| where sld_len > 12 AND (vowel_ratio < 0.2 OR digit_ratio > 0.3) +| stats count AS queries, dc(query) AS unique_domains, values(src_ip) AS sources + by query +| where unique_domains > 10 +| sort - queries +``` + +**Python-based Shannon Entropy Calculation for DNS queries:** + +```python +import math +from collections import Counter + +def shannon_entropy(text): + """Calculate Shannon entropy of a string""" + if not text: + return 0 + counter = Counter(text.lower()) + length = len(text) + entropy = -sum( + (count / length) * math.log2(count / length) + for count in counter.values() + ) + return round(entropy, 4) + +# Test with examples +normal_domain = "google" # Low entropy +dga_domain = "x8kj2m9p4qw7n" # High entropy +tunnel_subdomain = "aGVsbG8gd29ybGQ.evil.com" # Base64 encoded data + +print(f"Normal: {shannon_entropy(normal_domain)}") # ~2.25 +print(f"DGA: {shannon_entropy(dga_domain)}") # ~3.70 +print(f"Tunnel: {shannon_entropy(tunnel_subdomain)}") # ~3.50 + +# Threshold: entropy > 3.5 for subdomain = likely tunneling/DGA +``` + +**Splunk implementation of entropy scoring:** + +```spl +index=dns sourcetype="stream:dns" +| eval domain_parts = split(query, ".") +| eval check_string = mvindex(domain_parts, 0) +| eval check_len = len(check_string) +| where check_len > 8 +| eval chars = split(check_string, "") +| stats count AS total_chars, dc(chars) AS unique_chars by query, src_ip, check_string, check_len +| eval entropy_estimate = log(unique_chars, 2) * (unique_chars / check_len) +| where entropy_estimate > 3.5 +| stats count AS high_entropy_queries, dc(query) AS unique_queries by src_ip +| where high_entropy_queries > 50 +| sort - high_entropy_queries +``` + +### Step 3: Detect Anomalous DNS Query Volume + +Identify hosts generating abnormal DNS traffic: + +```spl +index=dns sourcetype="stream:dns" earliest=-24h +| bin _time span=1h +| stats count AS queries, dc(query) AS unique_domains by src_ip, _time +| eventstats avg(queries) AS avg_queries, stdev(queries) AS stdev_queries by src_ip +| eval z_score = (queries - avg_queries) / stdev_queries +| where z_score > 3 OR queries > 5000 +| sort - z_score +| table _time, src_ip, queries, unique_domains, avg_queries, z_score +``` + +**Detect TXT record abuse (common tunneling method):** + +```spl +index=dns sourcetype="stream:dns" query_type="TXT" +| stats count AS txt_queries, dc(query) AS unique_txt_domains, + values(query) AS domains by src_ip +| where txt_queries > 100 +| eval suspicion = case( + txt_queries > 1000, "CRITICAL — Likely DNS tunneling", + txt_queries > 500, "HIGH — Possible DNS tunneling", + txt_queries > 100, "MEDIUM — Unusual TXT volume" + ) +| sort - txt_queries +| table src_ip, txt_queries, unique_txt_domains, suspicion +``` + +### Step 4: Detect Known DNS Tunneling Tools + +Search for signatures of common DNS tunneling tools: + +```spl +index=dns sourcetype="stream:dns" +| eval query_lower = lower(query) +| where ( + match(query_lower, "\.dnscat\.") OR + match(query_lower, "\.dns2tcp\.") OR + match(query_lower, "\.iodine\.") OR + match(query_lower, "\.dnscapy\.") OR + match(query_lower, "\.cobalt.*\.beacon") OR + query_type="NULL" OR + (query_type="TXT" AND len(query) > 100) + ) +| stats count by src_ip, query, query_type +| sort - count +``` + +**Detect DNS over HTTPS (DoH) bypassing local DNS:** + +```spl +index=proxy OR index=firewall +dest IN ("1.1.1.1", "1.0.0.1", "8.8.8.8", "8.8.4.4", + "9.9.9.9", "149.112.112.112", "208.67.222.222") +dest_port=443 +| stats sum(bytes_out) AS total_bytes, count AS connections by src_ip, dest +| where connections > 100 OR total_bytes > 10485760 +| eval alert = "Possible DoH bypass — DNS queries sent over HTTPS to public resolver" +| sort - total_bytes +``` + +### Step 5: Correlate DNS Findings with Endpoint Data + +Cross-reference suspicious DNS with process data: + +```spl +index=dns src_ip="192.168.1.105" query="*.evil-tunnel.com" earliest=-24h +| stats count AS dns_queries, earliest(_time) AS first_query, latest(_time) AS last_query + by src_ip, query +| join src_ip [ + search index=sysmon EventCode=3 DestinationPort=53 Computer="WORKSTATION-042" + | stats count AS connections, values(Image) AS processes by SourceIp + | rename SourceIp AS src_ip + ] +| table src_ip, query, dns_queries, first_query, last_query, processes +``` + +### Step 6: Calculate Data Exfiltration Volume Estimate + +Estimate data volume encoded in DNS queries: + +```spl +index=dns src_ip="192.168.1.105" query="*.evil-tunnel.com" earliest=-24h +| eval domain_parts = split(query, ".") +| eval encoded_data = mvindex(domain_parts, 0) +| eval encoded_bytes = len(encoded_data) +| eval decoded_bytes = encoded_bytes * 0.75 -- Base64 decoding factor +| stats sum(decoded_bytes) AS total_bytes_estimated, count AS total_queries, + earliest(_time) AS first_seen, latest(_time) AS last_seen +| eval estimated_kb = round(total_bytes_estimated / 1024, 1) +| eval estimated_mb = round(total_bytes_estimated / 1048576, 2) +| eval duration_hours = round((last_seen - first_seen) / 3600, 1) +| eval rate_kbps = round(estimated_kb / (duration_hours * 3600) * 8, 2) +| table total_queries, estimated_mb, duration_hours, rate_kbps, first_seen, last_seen +``` + +## Key Concepts + +| Term | Definition | +|------|-----------| +| **DNS Tunneling** | Technique encoding data within DNS queries/responses to exfiltrate data or establish C2 channels through DNS | +| **DGA** | Domain Generation Algorithm — malware technique generating pseudo-random domain names for C2 resilience | +| **Shannon Entropy** | Mathematical measure of randomness in a string — high entropy (>3.5) in domain names indicates DGA or tunneling | +| **TXT Record Abuse** | Using DNS TXT records (designed for text data) as a high-bandwidth channel for data tunneling | +| **DNS over HTTPS (DoH)** | DNS queries encrypted over HTTPS (port 443), bypassing traditional DNS monitoring | +| **Passive DNS** | Historical record of DNS resolutions showing which IPs a domain resolved to over time | + +## Tools & Systems + +- **Splunk Stream**: Network traffic capture add-on providing parsed DNS query data for SIEM analysis +- **Zeek (Bro)**: Network security monitor generating detailed DNS transaction logs for analysis +- **Cisco Umbrella (OpenDNS)**: Cloud DNS security platform blocking malicious domains and logging query data +- **Infoblox DNS Firewall**: DNS-layer security providing RPZ-based blocking and detailed query logging +- **Farsight DNSDB**: Passive DNS database for historical domain resolution lookups and infrastructure mapping + +## Common Scenarios + +- **Cobalt Strike DNS Beacon**: Detect periodic TXT queries with encoded payloads to C2 domain +- **Data Exfiltration**: Large volumes of unique subdomain queries encoding stolen data in Base64/hex +- **DGA Malware**: Detect DNS queries to algorithmically generated domains (high entropy, no web content) +- **DNS-over-HTTPS Bypass**: Employee using DoH to bypass corporate DNS filtering and monitoring +- **Slow Drip Exfiltration**: Low-volume DNS tunneling staying below threshold alerts (requires baseline comparison) + +## Output Format + +``` +DNS EXFILTRATION ANALYSIS — WORKSTATION-042 +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +Period: 2024-03-14 to 2024-03-15 +Source: 192.168.1.105 (WORKSTATION-042, Finance Dept) + +Findings: + [CRITICAL] DNS tunneling detected to evil-tunnel[.]com + Query Volume: 12,847 queries in 18 hours + Avg Subdomain Len: 63 characters (normal: <20) + Avg Entropy: 3.82 (threshold: 3.5) + Query Types: TXT (89%), A (11%) + Estimated Data: ~4.7 MB exfiltrated via DNS + Rate: 0.58 kbps (slow drip pattern) + + [HIGH] DGA-like domains resolved + Unique DGA Domains: 247 domains resolved + Pattern: 15-char random alphanumeric.xyz TLD + Entropy Range: 3.6 - 4.1 + +Process Attribution: + Process: svchost_update.exe (masquerading — not legitimate svchost) + PID: 4892 + Parent: explorer.exe + Hash: SHA256: a1b2c3d4... (VT: 34/72 malicious — Cobalt Strike beacon) + +Containment: + [DONE] Host isolated via EDR + [DONE] Domain evil-tunnel[.]com added to DNS sinkhole + [DONE] Incident IR-2024-0448 created +``` diff --git a/personas/_shared/skills/analyzing-dns-logs-for-exfiltration/references/api-reference.md b/personas/_shared/skills/analyzing-dns-logs-for-exfiltration/references/api-reference.md new file mode 100644 index 0000000..f6ea940 --- /dev/null +++ b/personas/_shared/skills/analyzing-dns-logs-for-exfiltration/references/api-reference.md @@ -0,0 +1,112 @@ +# API Reference: DNS Exfiltration Detection Tools + +## Shannon Entropy Calculation + +### Python Implementation +```python +import math +from collections import Counter + +def shannon_entropy(text): + counter = Counter(text.lower()) + length = len(text) + return -sum((c/length) * math.log2(c/length) for c in counter.values()) +``` + +### Threshold Values +| Entropy | Classification | +|---------|---------------| +| < 2.5 | Normal domain (e.g., "google") | +| 2.5 - 3.5 | Borderline (monitor) | +| > 3.5 | Suspicious (likely DGA/tunneling) | +| > 4.0 | High confidence malicious | + +## Splunk DNS Queries + +### Tunneling Detection +```spl +index=dns sourcetype="stream:dns" +| eval subdomain_len=len(mvindex(split(query,"."),0)) +| where subdomain_len > 50 +| stats count by registered_domain, src_ip +``` + +### DGA Detection +```spl +index=dns +| eval sld=mvindex(split(query,"."), -2) +| where len(sld) > 12 +| stats count, dc(query) AS unique by src_ip +``` + +### Volume Anomaly +```spl +index=dns earliest=-24h +| bin _time span=1h +| stats count AS queries by src_ip, _time +| eventstats avg(queries) AS avg_q, stdev(queries) AS stdev_q by src_ip +| eval z_score=(queries - avg_q) / stdev_q +| where z_score > 3 +``` + +### TXT Record Abuse +```spl +index=dns query_type="TXT" +| stats count AS txt_queries by src_ip +| where txt_queries > 100 +``` + +## Zeek DNS Log Format + +### Log Fields (dns.log) +| Column | Field | Description | +|--------|-------|-------------| +| 0 | ts | Timestamp | +| 2 | id.orig_h | Source IP | +| 4 | id.resp_h | DNS server IP | +| 9 | query | Query domain name | +| 13 | qtype_name | Query type (A, TXT, CNAME) | +| 15 | rcode_name | Response code | +| 21 | answers | Response answers | + +### Zeek CLI Analysis +```bash +cat dns.log | zeek-cut query qtype_name id.orig_h | sort | uniq -c | sort -rn +``` + +## DNS Tunneling Tools (Detection Signatures) + +| Tool | DNS Pattern | +|------|-------------| +| iodine | `*.pirate.sea` (TXT/NULL records) | +| dnscat2 | `*.dnscat.` prefix in queries | +| dns2tcp | `*.dns2tcp.` pattern | +| Cobalt Strike DNS | Periodic TXT queries with encoded payloads | + +## Passive DNS Lookup APIs + +### Farsight DNSDB +```bash +curl -H "X-API-Key: $KEY" \ + "https://api.dnsdb.info/dnsdb/v2/lookup/rrset/name/evil.com/A" +``` + +### VirusTotal Domain Resolutions +```bash +curl -H "x-apikey: $KEY" \ + "https://www.virustotal.com/api/v3/domains/evil.com/resolutions" +``` + +## Cisco Umbrella (OpenDNS) Investigate API + +### Domain Categorization +```bash +curl -H "Authorization: Bearer $TOKEN" \ + "https://investigate.api.umbrella.com/domains/categorization/evil.com" +``` + +### Security Information +```bash +curl -H "Authorization: Bearer $TOKEN" \ + "https://investigate.api.umbrella.com/security/name/evil.com" +``` diff --git a/personas/_shared/skills/analyzing-dns-logs-for-exfiltration/scripts/agent.py b/personas/_shared/skills/analyzing-dns-logs-for-exfiltration/scripts/agent.py new file mode 100644 index 0000000..4b9a61d --- /dev/null +++ b/personas/_shared/skills/analyzing-dns-logs-for-exfiltration/scripts/agent.py @@ -0,0 +1,229 @@ +#!/usr/bin/env python3 +"""DNS exfiltration detection agent using entropy analysis and query pattern detection.""" + +import math +from collections import Counter, defaultdict + + +def shannon_entropy(text): + """Calculate Shannon entropy of a string.""" + if not text: + return 0.0 + counter = Counter(text.lower()) + length = len(text) + entropy = -sum( + (count / length) * math.log2(count / length) + for count in counter.values() + ) + return round(entropy, 4) + + +def extract_subdomain(fqdn): + """Extract the subdomain portion from a fully qualified domain name.""" + parts = fqdn.rstrip(".").split(".") + if len(parts) > 2: + return ".".join(parts[:-2]) + return "" + + +def extract_registered_domain(fqdn): + """Extract the registered domain (SLD + TLD) from an FQDN.""" + parts = fqdn.rstrip(".").split(".") + if len(parts) >= 2: + return ".".join(parts[-2:]) + return fqdn + + +def detect_tunneling(dns_records, subdomain_len_threshold=50, min_queries=20): + """Detect DNS tunneling based on subdomain length anomalies.""" + domain_stats = defaultdict(lambda: {"queries": 0, "unique_queries": set(), + "subdomain_lengths": [], "sources": set()}) + for record in dns_records: + query = record.get("query", "") + src = record.get("src_ip", "unknown") + subdomain = extract_subdomain(query) + reg_domain = extract_registered_domain(query) + if len(subdomain) > subdomain_len_threshold: + stats = domain_stats[reg_domain] + stats["queries"] += 1 + stats["unique_queries"].add(query) + stats["subdomain_lengths"].append(len(subdomain)) + stats["sources"].add(src) + alerts = [] + for domain, stats in domain_stats.items(): + if stats["queries"] >= min_queries: + avg_len = sum(stats["subdomain_lengths"]) / len(stats["subdomain_lengths"]) + max_len = max(stats["subdomain_lengths"]) + alerts.append({ + "domain": domain, + "queries": stats["queries"], + "unique_queries": len(stats["unique_queries"]), + "avg_subdomain_length": round(avg_len, 1), + "max_subdomain_length": max_len, + "sources": list(stats["sources"]), + "verdict": "CRITICAL - Likely DNS tunneling", + }) + return sorted(alerts, key=lambda x: x["avg_subdomain_length"], reverse=True) + + +def detect_dga(dns_records, entropy_threshold=3.5, min_sld_length=12): + """Detect Domain Generation Algorithm queries using entropy scoring.""" + suspicious = defaultdict(lambda: {"count": 0, "sources": set(), "entropies": []}) + for record in dns_records: + query = record.get("query", "").rstrip(".") + src = record.get("src_ip", "unknown") + parts = query.split(".") + if len(parts) < 2: + continue + sld = parts[-2] + if len(sld) < min_sld_length: + continue + ent = shannon_entropy(sld) + if ent > entropy_threshold: + suspicious[query]["count"] += 1 + suspicious[query]["sources"].add(src) + suspicious[query]["entropies"].append(ent) + alerts = [] + for domain, data in suspicious.items(): + avg_entropy = sum(data["entropies"]) / len(data["entropies"]) + alerts.append({ + "domain": domain, + "queries": data["count"], + "avg_entropy": round(avg_entropy, 4), + "sources": list(data["sources"]), + "verdict": "HIGH - Possible DGA domain", + }) + return sorted(alerts, key=lambda x: x["avg_entropy"], reverse=True) + + +def detect_volume_anomaly(dns_records, z_score_threshold=3.0): + """Detect hosts with anomalously high DNS query volumes.""" + host_counts = defaultdict(int) + for record in dns_records: + src = record.get("src_ip", "unknown") + host_counts[src] += 1 + if not host_counts: + return [] + values = list(host_counts.values()) + mean_q = sum(values) / len(values) + if len(values) < 2: + return [] + variance = sum((x - mean_q) ** 2 for x in values) / (len(values) - 1) + stdev_q = variance ** 0.5 + if stdev_q == 0: + return [] + anomalies = [] + for host, count in host_counts.items(): + z = (count - mean_q) / stdev_q + if z > z_score_threshold: + anomalies.append({ + "src_ip": host, + "queries": count, + "z_score": round(z, 2), + "mean": round(mean_q, 1), + "verdict": "HIGH - Anomalous query volume", + }) + return sorted(anomalies, key=lambda x: x["z_score"], reverse=True) + + +def detect_txt_abuse(dns_records, threshold=100): + """Detect excessive TXT record queries (common tunneling method).""" + txt_counts = defaultdict(lambda: {"count": 0, "unique_domains": set()}) + for record in dns_records: + qtype = str(record.get("query_type", "")).upper() + if qtype in ("TXT", "16"): + src = record.get("src_ip", "unknown") + txt_counts[src]["count"] += 1 + txt_counts[src]["unique_domains"].add(record.get("query", "")) + alerts = [] + for src, data in txt_counts.items(): + if data["count"] > threshold: + level = "CRITICAL" if data["count"] > 1000 else "HIGH" if data["count"] > 500 else "MEDIUM" + alerts.append({ + "src_ip": src, + "txt_queries": data["count"], + "unique_domains": len(data["unique_domains"]), + "verdict": f"{level} - Possible DNS tunneling via TXT records", + }) + return sorted(alerts, key=lambda x: x["txt_queries"], reverse=True) + + +def estimate_exfil_volume(dns_records, target_domain): + """Estimate data volume encoded in DNS queries to a specific domain.""" + total_encoded_bytes = 0 + query_count = 0 + for record in dns_records: + query = record.get("query", "") + if target_domain in query: + subdomain = extract_subdomain(query) + total_encoded_bytes += len(subdomain) + query_count += 1 + decoded_bytes = int(total_encoded_bytes * 0.75) # Base64 decode factor + return { + "target_domain": target_domain, + "total_queries": query_count, + "encoded_bytes": total_encoded_bytes, + "estimated_decoded_bytes": decoded_bytes, + "estimated_kb": round(decoded_bytes / 1024, 1), + "estimated_mb": round(decoded_bytes / (1024 * 1024), 3), + } + + +def parse_zeek_dns_log(log_path): + """Parse a Zeek dns.log file into structured records.""" + records = [] + with open(log_path, "r") as f: + for line in f: + if line.startswith("#"): + continue + parts = line.strip().split("\t") + if len(parts) >= 10: + records.append({ + "timestamp": parts[0], + "src_ip": parts[2], + "src_port": parts[3], + "dst_ip": parts[4], + "query": parts[9] if len(parts) > 9 else "", + "query_type": parts[13] if len(parts) > 13 else "", + }) + return records + + +if __name__ == "__main__": + print("=" * 60) + print("DNS Exfiltration Detection Agent") + print("Tunneling, DGA, volume anomaly, and TXT abuse detection") + print("=" * 60) + + # Demo with synthetic DNS records + demo_records = [ + {"query": f"{'a' * 60}.evil-tunnel.com", "src_ip": "192.168.1.105", + "query_type": "TXT"} for _ in range(50) + ] + [ + {"query": "x8kj2m9p4qw7nz3.xyz", "src_ip": "192.168.1.110", + "query_type": "A"} for _ in range(5) + ] + [ + {"query": "google.com", "src_ip": "192.168.1.50", "query_type": "A"} + for _ in range(10) + ] + + print("\n--- DNS Tunneling Detection ---") + tunneling = detect_tunneling(demo_records, subdomain_len_threshold=30, min_queries=10) + for t in tunneling: + print(f"[!] {t['domain']}: {t['queries']} queries, " + f"avg subdomain len={t['avg_subdomain_length']}") + + print("\n--- DGA Detection ---") + dga = detect_dga(demo_records, entropy_threshold=3.0, min_sld_length=10) + for d in dga[:5]: + print(f"[!] {d['domain']}: entropy={d['avg_entropy']}") + + print("\n--- TXT Record Abuse ---") + txt = detect_txt_abuse(demo_records, threshold=10) + for t in txt: + print(f"[!] {t['src_ip']}: {t['txt_queries']} TXT queries") + + print("\n--- Entropy Examples ---") + examples = ["google", "x8kj2m9p4qw7n", "aGVsbG8gd29ybGQ"] + for ex in examples: + print(f" '{ex}' -> entropy={shannon_entropy(ex)}") diff --git a/personas/_shared/skills/analyzing-docker-container-forensics/LICENSE b/personas/_shared/skills/analyzing-docker-container-forensics/LICENSE new file mode 100644 index 0000000..d885118 --- /dev/null +++ b/personas/_shared/skills/analyzing-docker-container-forensics/LICENSE @@ -0,0 +1,201 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to the Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by the Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding any notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. Please do not remove or change + the license header comment from a contributed file except when + necessary. + + Copyright 2026 mukul975 + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/personas/_shared/skills/analyzing-docker-container-forensics/SKILL.md b/personas/_shared/skills/analyzing-docker-container-forensics/SKILL.md new file mode 100644 index 0000000..d76d954 --- /dev/null +++ b/personas/_shared/skills/analyzing-docker-container-forensics/SKILL.md @@ -0,0 +1,341 @@ +--- +name: analyzing-docker-container-forensics +description: Investigate compromised Docker containers by analyzing images, layers, volumes, logs, and runtime artifacts to + identify malicious activity and evidence. +domain: cybersecurity +subdomain: digital-forensics +tags: +- forensics +- docker +- container-forensics +- container-security +- image-analysis +- runtime-investigation +version: '1.0' +author: mahipal +license: Apache-2.0 +nist_csf: +- RS.AN-01 +- RS.AN-03 +- DE.AE-02 +- RS.MA-01 +--- + +# Analyzing Docker Container Forensics + +## When to Use +- When investigating a compromised Docker container or container host +- For analyzing malicious Docker images pulled from registries +- During incident response involving containerized application breaches +- When examining container escape attempts or privilege escalation +- For auditing container configurations and identifying misconfigurations + +## Prerequisites +- Docker CLI access on the forensic workstation +- Access to the Docker host file system (forensic image or live) +- Understanding of Docker layered file system (overlay2, aufs) +- dive, docker-explorer, or container-diff for image analysis +- Knowledge of Docker daemon configuration and socket security +- Trivy or Grype for vulnerability scanning of container images + +## Workflow + +### Step 1: Preserve Container State and Evidence + +```bash +# List all containers (including stopped) +docker ps -a --no-trunc > /cases/case-2024-001/docker/container_list.txt + +# Inspect the compromised container +CONTAINER_ID="abc123def456" +docker inspect $CONTAINER_ID > /cases/case-2024-001/docker/container_inspect.json + +# Export container filesystem as tarball (preserves current state) +docker export $CONTAINER_ID > /cases/case-2024-001/docker/container_export.tar + +# Create an image from the container's current state +docker commit $CONTAINER_ID forensic-evidence:case-2024-001 +docker save forensic-evidence:case-2024-001 > /cases/case-2024-001/docker/container_image.tar + +# Capture container logs +docker logs $CONTAINER_ID --timestamps > /cases/case-2024-001/docker/container_logs.txt 2>&1 + +# Capture running processes (if container is still running) +docker top $CONTAINER_ID > /cases/case-2024-001/docker/container_processes.txt + +# Capture network connections +docker exec $CONTAINER_ID netstat -tlnp 2>/dev/null > /cases/case-2024-001/docker/container_network.txt + +# Copy specific files from the container +docker cp $CONTAINER_ID:/var/log/ /cases/case-2024-001/docker/container_var_log/ +docker cp $CONTAINER_ID:/tmp/ /cases/case-2024-001/docker/container_tmp/ +docker cp $CONTAINER_ID:/etc/passwd /cases/case-2024-001/docker/container_passwd + +# Hash all exported evidence +sha256sum /cases/case-2024-001/docker/*.tar > /cases/case-2024-001/docker/evidence_hashes.txt +``` + +### Step 2: Analyze Container Image Layers + +```bash +# Install dive for image layer analysis +wget https://github.com/wagoodman/dive/releases/latest/download/dive_linux_amd64.deb +sudo dpkg -i dive_linux_amd64.deb + +# Analyze image layers interactively +dive forensic-evidence:case-2024-001 + +# Non-interactive layer analysis +dive forensic-evidence:case-2024-001 --ci --json /cases/case-2024-001/docker/dive_analysis.json + +# Extract and examine individual layers +mkdir -p /cases/case-2024-001/docker/layers/ +tar -xf /cases/case-2024-001/docker/container_image.tar -C /cases/case-2024-001/docker/layers/ + +# List the image manifest and layer order +cat /cases/case-2024-001/docker/layers/manifest.json | python3 -m json.tool + +# Examine each layer for changes +for layer in /cases/case-2024-001/docker/layers/*/layer.tar; do + echo "=== Layer: $(dirname $layer | xargs basename) ===" + tar -tf "$layer" | head -20 + echo "..." +done + +# Use container-diff to compare with original base image +# Install container-diff +curl -LO https://storage.googleapis.com/container-diff/latest/container-diff-linux-amd64 +chmod +x container-diff-linux-amd64 + +# Compare committed image with original +./container-diff-linux-amd64 diff daemon://nginx:latest daemon://forensic-evidence:case-2024-001 \ + --type=file --type=apt --type=history --json \ + > /cases/case-2024-001/docker/container_diff.json +``` + +### Step 3: Examine Docker Host Artifacts + +```bash +# Docker data directory (default: /var/lib/docker/) +DOCKER_ROOT="/mnt/evidence/var/lib/docker" + +# Examine overlay2 filesystem layers +ls -la $DOCKER_ROOT/overlay2/ + +# Find the container's merged filesystem +CONTAINER_HASH=$(docker inspect $CONTAINER_ID --format '{{.GraphDriver.Data.MergedDir}}' 2>/dev/null) +# Or manually from forensic image: +# Look in /var/lib/docker/containers//config.v2.json + +# Analyze container configuration files +cat $DOCKER_ROOT/containers/$CONTAINER_ID/config.v2.json | python3 -m json.tool \ + > /cases/case-2024-001/docker/container_config.json + +# Check Docker daemon configuration +cat /mnt/evidence/etc/docker/daemon.json 2>/dev/null > /cases/case-2024-001/docker/daemon_config.json + +# Examine Docker events log +cat $DOCKER_ROOT/containers/$CONTAINER_ID/*.log > /cases/case-2024-001/docker/container_json_logs.txt + +# Check for volume mounts (potential host filesystem access) +python3 << 'PYEOF' +import json + +with open('/cases/case-2024-001/docker/container_inspect.json') as f: + data = json.load(f) + +inspect = data[0] if isinstance(data, list) else data + +print("=== CONTAINER SECURITY ANALYSIS ===\n") + +# Check mounts +print("Volume Mounts:") +for mount in inspect.get('Mounts', []): + rw = "READ-WRITE" if mount.get('RW') else "READ-ONLY" + print(f" {mount.get('Source', 'N/A')} -> {mount.get('Destination', 'N/A')} ({rw})") + if mount.get('Source') in ('/', '/etc', '/var', '/root') and mount.get('RW'): + print(f" WARNING: Sensitive host path mounted read-write!") + +# Check privileged mode +host_config = inspect.get('HostConfig', {}) +if host_config.get('Privileged'): + print("\nWARNING: Container was running in PRIVILEGED mode!") + +# Check capabilities +cap_add = host_config.get('CapAdd', []) +if cap_add: + print(f"\nAdded Capabilities: {cap_add}") + dangerous_caps = ['SYS_ADMIN', 'SYS_PTRACE', 'NET_ADMIN', 'SYS_MODULE'] + for cap in cap_add: + if cap in dangerous_caps: + print(f" WARNING: Dangerous capability: {cap}") + +# Check PID namespace +if host_config.get('PidMode') == 'host': + print("\nWARNING: Container shares host PID namespace!") + +# Check network mode +if host_config.get('NetworkMode') == 'host': + print("\nWARNING: Container shares host network namespace!") + +# Check user +user = inspect.get('Config', {}).get('User', 'root (default)') +print(f"\nRunning as user: {user}") + +# Check environment variables for secrets +env_vars = inspect.get('Config', {}).get('Env', []) +print(f"\nEnvironment Variables: {len(env_vars)}") +for env in env_vars: + key = env.split('=')[0] + if any(s in key.upper() for s in ['PASSWORD', 'SECRET', 'KEY', 'TOKEN', 'CREDENTIAL']): + print(f" SENSITIVE: {key}=***REDACTED***") +PYEOF +``` + +### Step 4: Analyze Container File System Changes + +```bash +# Compare container filesystem to original image +docker diff $CONTAINER_ID > /cases/case-2024-001/docker/filesystem_changes.txt + +# A = Added, C = Changed, D = Deleted +# Analyze changes +python3 << 'PYEOF' +added = [] +changed = [] +deleted = [] + +with open('/cases/case-2024-001/docker/filesystem_changes.txt') as f: + for line in f: + line = line.strip() + if line.startswith('A '): + added.append(line[2:]) + elif line.startswith('C '): + changed.append(line[2:]) + elif line.startswith('D '): + deleted.append(line[2:]) + +print(f"Files Added: {len(added)}") +print(f"Files Changed: {len(changed)}") +print(f"Files Deleted: {len(deleted)}") + +# Flag suspicious additions +suspicious = [f for f in added if any(s in f for s in + ['/tmp/', '/dev/shm/', '/root/', '.sh', '.py', '.elf', 'reverse', 'shell', 'backdoor'])] +if suspicious: + print(f"\nSuspicious Added Files:") + for f in suspicious: + print(f" {f}") + +# Flag suspicious changes +sus_changed = [f for f in changed if any(s in f for s in + ['/etc/passwd', '/etc/shadow', '/etc/crontab', '/etc/ssh', '.bashrc'])] +if sus_changed: + print(f"\nSuspicious Changed Files:") + for f in sus_changed: + print(f" {f}") +PYEOF + +# Extract and examine the container export +mkdir -p /cases/case-2024-001/docker/container_fs/ +tar -xf /cases/case-2024-001/docker/container_export.tar -C /cases/case-2024-001/docker/container_fs/ + +# Scan for webshells and malicious files +find /cases/case-2024-001/docker/container_fs/tmp/ -type f -exec file {} \; +find /cases/case-2024-001/docker/container_fs/ -name "*.php" -newer /cases/case-2024-001/docker/container_fs/etc/hostname +``` + +### Step 5: Scan for Vulnerabilities and Generate Report + +```bash +# Scan the image for known vulnerabilities +trivy image forensic-evidence:case-2024-001 \ + --format json \ + --output /cases/case-2024-001/docker/vulnerability_scan.json + +# Scan the exported filesystem +trivy fs /cases/case-2024-001/docker/container_fs/ \ + --format table \ + --output /cases/case-2024-001/docker/fs_vulnerabilities.txt + +# Check for secrets in the image +trivy image forensic-evidence:case-2024-001 \ + --scanners secret \ + --format json \ + --output /cases/case-2024-001/docker/secrets_scan.json +``` + +## Key Concepts + +| Concept | Description | +|---------|-------------| +| Image layers | Read-only filesystem layers stacked to form the container image | +| overlay2 | Default Docker storage driver using union filesystem for layers | +| Container diff | Comparison of runtime filesystem changes against the original image | +| Privileged mode | Container with full host capabilities (bypasses most isolation) | +| Docker socket | Unix socket (/var/run/docker.sock) controlling the Docker daemon | +| Container escape | Technique for breaking out of container isolation to the host | +| Volume mounts | Host filesystem paths made accessible inside the container | +| Image history | Record of Dockerfile instructions used to build each layer | + +## Tools & Systems + +| Tool | Purpose | +|------|---------| +| docker inspect | Detailed container configuration and state information | +| docker diff | Show filesystem changes made in a running/stopped container | +| dive | Interactive Docker image layer analysis tool | +| container-diff | Google tool for comparing container image contents | +| Trivy | Vulnerability scanner for container images and filesystems | +| docker-explorer | Forensic tool for offline Docker artifact analysis | +| Sysdig | Container runtime security monitoring and forensics | +| Falco | Runtime threat detection for containers and Kubernetes | + +## Common Scenarios + +**Scenario 1: Web Application Container Compromise** +Export the container filesystem, identify webshells in web root, analyze access logs for exploitation attempts, check for added files and modified configurations, examine network connections for C2 communication, review container capabilities for escalation paths. + +**Scenario 2: Supply Chain Attack via Malicious Image** +Analyze image layers with dive to identify which layer added malicious content, compare with the official base image using container-diff, check image history for suspicious RUN commands, scan for embedded backdoors and cryptocurrency miners, trace the image pull from registry logs. + +**Scenario 3: Container Escape Investigation** +Check if container ran privileged or with dangerous capabilities, examine host filesystem mount points for unauthorized access, review Docker socket mount enabling Docker-in-Docker abuse, analyze host system logs for container escape indicators, check for kernel exploit artifacts. + +**Scenario 4: Cryptojacking in Container Environment** +Identify high-CPU containers, export and analyze the container image for mining binaries, check for unauthorized images in the registry, review container creation events for rogue deployments, examine network connections for mining pool communications. + +## Output Format + +``` +Docker Container Forensics Summary: + Container: abc123def456 (nginx-app) + Image: company/web-app:v2.1 + Status: Running (started 2024-01-10 09:00 UTC) + Host: docker-host-01.corp.local + + Security Configuration: + Privileged: No + Capabilities Added: NET_ADMIN (WARNING) + Volume Mounts: /var/log -> /host-logs (RW) + Network Mode: bridge + User: root (WARNING) + + Filesystem Changes: + Added: 23 files (5 suspicious) + Changed: 12 files (2 suspicious) + Deleted: 0 files + + Suspicious Findings: + /tmp/reverse.sh - Reverse shell script (Added) + /var/www/html/.hidden/shell.php - PHP webshell (Added) + /etc/crontab - Modified (persistence cron entry added) + /root/.ssh/authorized_keys - Modified (unauthorized key added) + + Vulnerability Scan: + Critical: 3 (CVE-2024-xxxx in base image) + High: 12 + Medium: 34 + + Evidence: /cases/case-2024-001/docker/ +``` diff --git a/personas/_shared/skills/analyzing-docker-container-forensics/references/api-reference.md b/personas/_shared/skills/analyzing-docker-container-forensics/references/api-reference.md new file mode 100644 index 0000000..aa7c35d --- /dev/null +++ b/personas/_shared/skills/analyzing-docker-container-forensics/references/api-reference.md @@ -0,0 +1,116 @@ +# API Reference: Docker Container Forensics Tools + +## docker inspect - Container Details + +### Syntax +```bash +docker inspect +docker inspect --format '{{.HostConfig.Privileged}}' +docker inspect --format '{{json .Mounts}}' | jq +docker inspect --format '{{.GraphDriver.Data.MergedDir}}' +``` + +### Key JSON Paths +| Path | Description | +|------|-------------| +| `.HostConfig.Privileged` | Privileged mode status | +| `.HostConfig.CapAdd` | Added capabilities | +| `.HostConfig.PidMode` | PID namespace mode | +| `.HostConfig.NetworkMode` | Network namespace mode | +| `.Mounts` | Volume mount configuration | +| `.Config.User` | Container user | +| `.Config.Env` | Environment variables | +| `.Config.Image` | Source image name | +| `.State.StartedAt` | Container start time | + +## docker diff - Filesystem Changes + +### Syntax +```bash +docker diff +``` + +### Output Codes +| Code | Meaning | +|------|---------| +| `A` | File or directory was added | +| `C` | File or directory was changed | +| `D` | File or directory was deleted | + +## docker export - Container Filesystem Export + +### Syntax +```bash +docker export > container_fs.tar +docker export | gzip > container_fs.tar.gz +``` + +## docker commit / docker save - Image Preservation + +### Syntax +```bash +docker commit forensic-evidence:case001 +docker save forensic-evidence:case001 > evidence_image.tar +``` + +## docker logs - Container Log Retrieval + +### Syntax +```bash +docker logs --timestamps +docker logs --since 2024-01-15 +docker logs --tail 1000 +docker logs -f # Follow (live) +``` + +## dive - Image Layer Analysis + +### Syntax +```bash +dive # Interactive mode +dive --ci # CI mode (non-interactive) +dive --ci --json out.json # JSON output +``` + +### Output Includes +- Layer-by-layer filesystem changes +- Image efficiency score +- Wasted space analysis + +## container-diff - Image Comparison + +### Syntax +```bash +container-diff diff daemon://nginx:latest daemon://suspect:latest \ + --type=file --type=apt --type=history --json +``` + +### Diff Types +| Type | Description | +|------|-------------| +| `file` | File system differences | +| `apt` | APT package differences | +| `pip` | Python package differences | +| `history` | Docker build history differences | + +## Trivy - Vulnerability Scanning + +### Syntax +```bash +trivy image +trivy image --format json +trivy image --scanners vuln,secret +trivy fs /path/to/exported/container/ +``` + +### Severity Levels +`CRITICAL` | `HIGH` | `MEDIUM` | `LOW` | `UNKNOWN` + +## docker-explorer - Offline Forensics + +### Syntax +```bash +de.py -r /var/lib/docker list +de.py -r /var/lib/docker mount /mnt/forensic +de.py -r /var/lib/docker history +``` diff --git a/personas/_shared/skills/analyzing-docker-container-forensics/scripts/agent.py b/personas/_shared/skills/analyzing-docker-container-forensics/scripts/agent.py new file mode 100644 index 0000000..ed525bb --- /dev/null +++ b/personas/_shared/skills/analyzing-docker-container-forensics/scripts/agent.py @@ -0,0 +1,238 @@ +#!/usr/bin/env python3 +"""Docker container forensics agent for investigating compromised containers.""" + +import shlex +import subprocess +import json +import os +import sys +import hashlib +import datetime + + +def run_cmd(cmd): + """Execute a command and return output.""" + if isinstance(cmd, str): + cmd = shlex.split(cmd) + result = subprocess.run(cmd, capture_output=True, text=True, timeout=120) + return result.stdout.strip(), result.stderr.strip(), result.returncode + + +def list_containers(all_containers=True): + """List Docker containers with detailed information.""" + flags = "-a" if all_containers else "" + cmd = f"docker ps {flags} --no-trunc --format '{{{{json .}}}}'" + stdout, _, rc = run_cmd(cmd) + containers = [] + if rc == 0 and stdout: + for line in stdout.splitlines(): + try: + containers.append(json.loads(line)) + except json.JSONDecodeError: + continue + return containers + + +def inspect_container(container_id): + """Get detailed container inspection data.""" + stdout, _, rc = run_cmd(f"docker inspect {container_id}") + if rc == 0 and stdout: + return json.loads(stdout) + return None + + +def analyze_security_config(inspect_data): + """Analyze container security configuration for misconfigurations.""" + if isinstance(inspect_data, list): + inspect_data = inspect_data[0] + findings = [] + host_config = inspect_data.get("HostConfig", {}) + config = inspect_data.get("Config", {}) + + if host_config.get("Privileged"): + findings.append({"severity": "CRITICAL", "finding": "Container running in PRIVILEGED mode"}) + + cap_add = host_config.get("CapAdd") or [] + dangerous_caps = ["SYS_ADMIN", "SYS_PTRACE", "NET_ADMIN", "SYS_MODULE", + "DAC_OVERRIDE", "NET_RAW"] + for cap in cap_add: + if cap in dangerous_caps: + findings.append({"severity": "HIGH", "finding": f"Dangerous capability added: {cap}"}) + + if host_config.get("PidMode") == "host": + findings.append({"severity": "HIGH", "finding": "Shares host PID namespace"}) + + if host_config.get("NetworkMode") == "host": + findings.append({"severity": "HIGH", "finding": "Shares host network namespace"}) + + mounts = inspect_data.get("Mounts", []) + sensitive_paths = ["/", "/etc", "/var", "/root", "/home", "/var/run/docker.sock"] + for mount in mounts: + src = mount.get("Source", "") + rw = mount.get("RW", False) + if src in sensitive_paths and rw: + findings.append({ + "severity": "CRITICAL", + "finding": f"Sensitive host path mounted RW: {src} -> {mount.get('Destination')}" + }) + if "docker.sock" in src: + findings.append({ + "severity": "CRITICAL", + "finding": "Docker socket mounted (container can control Docker daemon)" + }) + + user = config.get("User", "") + if not user or user == "root": + findings.append({"severity": "MEDIUM", "finding": "Running as root user"}) + + env_vars = config.get("Env", []) + secret_keywords = ["PASSWORD", "SECRET", "KEY", "TOKEN", "CREDENTIAL", "API_KEY"] + for env in env_vars: + key = env.split("=")[0] + if any(s in key.upper() for s in secret_keywords): + findings.append({"severity": "HIGH", "finding": f"Sensitive env var exposed: {key}"}) + + return findings + + +def get_filesystem_changes(container_id): + """Get filesystem changes between container and its image.""" + stdout, _, rc = run_cmd(f"docker diff {container_id}") + changes = {"added": [], "changed": [], "deleted": []} + if rc == 0 and stdout: + for line in stdout.splitlines(): + line = line.strip() + if line.startswith("A "): + changes["added"].append(line[2:]) + elif line.startswith("C "): + changes["changed"].append(line[2:]) + elif line.startswith("D "): + changes["deleted"].append(line[2:]) + return changes + + +def detect_suspicious_files(changes): + """Analyze filesystem changes for indicators of compromise.""" + suspicious_patterns = [ + "/tmp/", "/dev/shm/", "/root/", ".sh", ".py", ".elf", + "reverse", "shell", "backdoor", "miner", "xmr", "nc ", + ".php", "webshell", "c2", "beacon", + ] + suspicious_changes = ["/etc/passwd", "/etc/shadow", "/etc/crontab", + "/etc/ssh", ".bashrc", "/etc/sudoers", "authorized_keys"] + + findings = [] + for f in changes["added"]: + for pattern in suspicious_patterns: + if pattern in f.lower(): + findings.append({"type": "ADDED", "path": f, "reason": f"Matches pattern: {pattern}"}) + break + for f in changes["changed"]: + for pattern in suspicious_changes: + if pattern in f.lower(): + findings.append({"type": "CHANGED", "path": f, "reason": f"Critical file modified"}) + break + return findings + + +def export_container(container_id, output_path): + """Export container filesystem as a tarball for offline analysis.""" + with open(output_path, "wb") as out_f: + result = subprocess.run( + ["docker", "export", container_id], + stdout=out_f, stderr=subprocess.PIPE, + timeout=120, + ) + if result.returncode == 0 and os.path.exists(output_path): + sha256 = hashlib.sha256() + with open(output_path, "rb") as f: + for chunk in iter(lambda: f.read(65536), b""): + sha256.update(chunk) + return True, sha256.hexdigest() + return False, None + + +def get_container_logs(container_id, tail=500): + """Retrieve container logs with timestamps.""" + stdout, stderr, rc = run_cmd(f"docker logs --timestamps --tail {tail} {container_id}") + return stdout + "\n" + stderr if rc == 0 else None + + +def scan_image_vulnerabilities(image_name): + """Run Trivy vulnerability scan on a container image.""" + cmd = f"trivy image --format json {image_name}" + stdout, _, rc = run_cmd(cmd) + if rc == 0 and stdout: + try: + return json.loads(stdout) + except json.JSONDecodeError: + return None + return None + + +def generate_report(container_id, inspect_data, security_findings, + fs_changes, suspicious_files): + """Generate a forensic analysis report.""" + container_name = "unknown" + image = "unknown" + if inspect_data: + data = inspect_data[0] if isinstance(inspect_data, list) else inspect_data + container_name = data.get("Name", "").lstrip("/") + image = data.get("Config", {}).get("Image", "unknown") + + report = { + "report_type": "Docker Container Forensics", + "timestamp": datetime.datetime.utcnow().isoformat() + "Z", + "container_id": container_id, + "container_name": container_name, + "image": image, + "security_findings": security_findings, + "filesystem_changes": { + "added": len(fs_changes["added"]), + "changed": len(fs_changes["changed"]), + "deleted": len(fs_changes["deleted"]), + }, + "suspicious_files": suspicious_files, + } + return report + + +if __name__ == "__main__": + print("=" * 60) + print("Docker Container Forensics Agent") + print("Security analysis, filesystem diffing, evidence collection") + print("=" * 60) + + container_id = sys.argv[1] if len(sys.argv) > 1 else None + + if container_id: + print(f"\n[*] Analyzing container: {container_id}") + + inspect_data = inspect_container(container_id) + if not inspect_data: + print("[ERROR] Failed to inspect container. Is Docker running?") + sys.exit(1) + + print("\n--- Security Configuration Analysis ---") + findings = analyze_security_config(inspect_data) + for f in findings: + print(f"[{f['severity']}] {f['finding']}") + + print("\n--- Filesystem Changes ---") + changes = get_filesystem_changes(container_id) + print(f" Added: {len(changes['added'])}, Changed: {len(changes['changed'])}, " + f"Deleted: {len(changes['deleted'])}") + + print("\n--- Suspicious Files ---") + suspicious = detect_suspicious_files(changes) + for s in suspicious: + print(f"[!] {s['type']}: {s['path']} ({s['reason']})") + + report = generate_report(container_id, inspect_data, findings, changes, suspicious) + print(f"\n[*] Report:\n{json.dumps(report, indent=2)}") + else: + print("\n[*] Listing all containers...") + containers = list_containers() + for c in containers: + print(f" {c.get('ID', '?')[:12]} {c.get('Names', '?')} {c.get('Status', '?')}") + print(f"\n[DEMO] Usage: python agent.py ") diff --git a/personas/_shared/skills/analyzing-email-headers-for-phishing-investigation/LICENSE b/personas/_shared/skills/analyzing-email-headers-for-phishing-investigation/LICENSE new file mode 100644 index 0000000..d885118 --- /dev/null +++ b/personas/_shared/skills/analyzing-email-headers-for-phishing-investigation/LICENSE @@ -0,0 +1,201 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to the Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by the Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding any notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. Please do not remove or change + the license header comment from a contributed file except when + necessary. + + Copyright 2026 mukul975 + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/personas/_shared/skills/analyzing-email-headers-for-phishing-investigation/SKILL.md b/personas/_shared/skills/analyzing-email-headers-for-phishing-investigation/SKILL.md new file mode 100644 index 0000000..32b98c2 --- /dev/null +++ b/personas/_shared/skills/analyzing-email-headers-for-phishing-investigation/SKILL.md @@ -0,0 +1,327 @@ +--- +name: analyzing-email-headers-for-phishing-investigation +description: Parse and analyze email headers to trace the origin of phishing emails, verify sender authenticity, and identify + spoofing through SPF, DKIM, and DMARC validation. +domain: cybersecurity +subdomain: digital-forensics +tags: +- forensics +- email-analysis +- phishing +- spf +- dkim +- dmarc +- header-analysis +version: '1.0' +author: mahipal +license: Apache-2.0 +atlas_techniques: +- AML.T0052 +nist_csf: +- RS.AN-01 +- RS.AN-03 +- DE.AE-02 +- RS.MA-01 +--- + +# Analyzing Email Headers for Phishing Investigation + +## When to Use +- When investigating a suspected phishing email to determine its true origin +- For verifying sender authenticity and detecting email spoofing +- During incident response when a user has clicked a phishing link +- When tracing the delivery path and relay servers of a suspicious email +- For validating SPF, DKIM, and DMARC alignment to identify forgery + +## Prerequisites +- Raw email headers from the suspicious message (EML or MSG format) +- Understanding of SMTP protocol and email header fields +- Access to DNS lookup tools (dig, nslookup) for SPF/DKIM/DMARC verification +- Email header analysis tools (MHA, emailheaders.net concepts) +- Python with email parsing libraries for automated analysis +- Access to threat intelligence platforms for IP/domain reputation + +## Workflow + +### Step 1: Extract Raw Email Headers + +```bash +# Export from Outlook: Open email > File > Properties > Internet Headers +# Export from Gmail: Open email > Three dots > Show original +# Export from Thunderbird: View > Message Source + +# If working with EML file from forensic image +cp /mnt/evidence/Users/suspect/AppData/Local/Microsoft/Outlook/phishing_email.eml \ + /cases/case-2024-001/email/ + +# If working with PST file, extract individual messages +pip install pypff +python3 << 'PYEOF' +import pypff + +pst = pypff.file() +pst.open("/cases/case-2024-001/email/outlook.pst") +root = pst.get_root_folder() + +def extract_messages(folder, path=""): + for i in range(folder.get_number_of_sub_messages()): + msg = folder.get_sub_message(i) + headers = msg.get_transport_headers() + subject = msg.get_subject() + if headers: + filename = f"/cases/case-2024-001/email/msg_{i}_{subject[:30]}.txt" + with open(filename, 'w') as f: + f.write(headers) + for i in range(folder.get_number_of_sub_folders()): + extract_messages(folder.get_sub_folder(i)) + +extract_messages(root) +PYEOF +``` + +### Step 2: Parse the Email Header Chain + +```bash +# Parse headers using Python email library +python3 << 'PYEOF' +import email +from email import policy + +with open('/cases/case-2024-001/email/phishing_email.eml', 'r') as f: + msg = email.message_from_file(f, policy=policy.default) + +print("=== KEY HEADER FIELDS ===") +print(f"From: {msg['From']}") +print(f"To: {msg['To']}") +print(f"Subject: {msg['Subject']}") +print(f"Date: {msg['Date']}") +print(f"Message-ID: {msg['Message-ID']}") +print(f"Reply-To: {msg['Reply-To']}") +print(f"Return-Path: {msg['Return-Path']}") +print(f"X-Mailer: {msg['X-Mailer']}") +print(f"X-Originating-IP: {msg['X-Originating-IP']}") + +print("\n=== RECEIVED HEADERS (bottom-up = chronological) ===") +received_headers = msg.get_all('Received') +if received_headers: + for i, header in enumerate(reversed(received_headers)): + print(f"\nHop {i+1}: {header.strip()}") + +print("\n=== AUTHENTICATION RESULTS ===") +auth_results = msg.get_all('Authentication-Results') +if auth_results: + for result in auth_results: + print(result) + +print(f"\nARC-Authentication-Results: {msg.get('ARC-Authentication-Results', 'Not present')}") +print(f"Received-SPF: {msg.get('Received-SPF', 'Not present')}") +print(f"DKIM-Signature: {msg.get('DKIM-Signature', 'Not present')}") +PYEOF +``` + +### Step 3: Validate SPF, DKIM, and DMARC Records + +```bash +# Extract the envelope sender domain +SENDER_DOMAIN="example-corp.com" + +# Check SPF record +dig TXT $SENDER_DOMAIN +short | grep "v=spf1" +# Example: "v=spf1 include:_spf.google.com include:sendgrid.net ~all" + +# Check DKIM record (selector from DKIM-Signature header, e.g., "s=selector1") +DKIM_SELECTOR="selector1" +dig TXT ${DKIM_SELECTOR}._domainkey.${SENDER_DOMAIN} +short + +# Check DMARC record +dig TXT _dmarc.${SENDER_DOMAIN} +short +# Example: "v=DMARC1; p=reject; rua=mailto:dmarc@example-corp.com; pct=100" + +# Verify the sending IP against SPF +# Extract IP from first Received header +SENDING_IP="203.0.113.45" + +# Manual SPF check using python +python3 << 'PYEOF' +import spf # pip install pyspf + +result, explanation = spf.check2( + i='203.0.113.45', + s='sender@example-corp.com', + h='mail.example-corp.com' +) +print(f"SPF Result: {result}") +print(f"Explanation: {explanation}") +# Results: pass, fail, softfail, neutral, none, temperror, permerror +PYEOF + +# Check if sending IP is in known malicious IP lists +# Query AbuseIPDB or VirusTotal +curl -s "https://api.abuseipdb.com/api/v2/check?ipAddress=${SENDING_IP}" \ + -H "Key: YOUR_API_KEY" -H "Accept: application/json" | python3 -m json.tool +``` + +### Step 4: Analyze Sender Domain and Infrastructure + +```bash +# WHOIS lookup on sender domain +whois $SENDER_DOMAIN | grep -iE '(registrar|creation|expiration|registrant|nameserver)' + +# Check domain age (recently registered domains are suspicious) +# DNS record investigation +dig A $SENDER_DOMAIN +short +dig MX $SENDER_DOMAIN +short +dig NS $SENDER_DOMAIN +short + +# Reverse DNS on sending IP +dig -x $SENDING_IP +short + +# Check for lookalike/typosquatting domains +# Compare with legitimate domain using visual similarity +python3 << 'PYEOF' +import Levenshtein # pip install python-Levenshtein + +legitimate = "microsoft.com" +suspicious = "micr0soft.com" + +distance = Levenshtein.distance(legitimate, suspicious) +ratio = Levenshtein.ratio(legitimate, suspicious) +print(f"Edit distance: {distance}") +print(f"Similarity ratio: {ratio:.2%}") +if ratio > 0.8: + print("WARNING: Likely typosquatting/lookalike domain!") +PYEOF + +# Check domain reputation on VirusTotal +curl -s "https://www.virustotal.com/api/v3/domains/${SENDER_DOMAIN}" \ + -H "x-apikey: YOUR_VT_API_KEY" | python3 -m json.tool + +# Check if the Reply-To differs from From (common phishing indicator) +python3 -c " +import email +with open('/cases/case-2024-001/email/phishing_email.eml') as f: + msg = email.message_from_file(f) +from_addr = email.utils.parseaddr(msg['From'])[1] +reply_to = email.utils.parseaddr(msg.get('Reply-To', msg['From']))[1] +if from_addr != reply_to: + print(f'WARNING: From ({from_addr}) != Reply-To ({reply_to})') +else: + print('From and Reply-To match') +" +``` + +### Step 5: Examine Email Body and Attachments + +```bash +# Extract URLs from email body +python3 << 'PYEOF' +import email +import re +from email import policy + +with open('/cases/case-2024-001/email/phishing_email.eml', 'r') as f: + msg = email.message_from_file(f, policy=policy.default) + +body = msg.get_body(preferencelist=('html', 'plain')) +if body: + content = body.get_content() + urls = re.findall(r'https?://[^\s<>"\']+', content) + print("=== URLs FOUND IN EMAIL BODY ===") + for url in set(urls): + print(f" {url}") + + # Check for URL obfuscation (display text != href) + href_pattern = re.findall(r']*href=["\']([^"\']+)["\'][^>]*>(.*?)', content, re.DOTALL) + print("\n=== HYPERLINK ANALYSIS ===") + for href, text in href_pattern: + display_url = re.findall(r'https?://[^\s<]+', text) + if display_url and display_url[0] != href: + print(f" MISMATCH: Display='{display_url[0]}' -> Actual='{href}'") + +# Extract and hash attachments +print("\n=== ATTACHMENTS ===") +for part in msg.walk(): + if part.get_content_disposition() == 'attachment': + filename = part.get_filename() + content = part.get_payload(decode=True) + import hashlib + sha256 = hashlib.sha256(content).hexdigest() + print(f" File: {filename}, Size: {len(content)}, SHA-256: {sha256}") + with open(f'/cases/case-2024-001/email/attachments/{filename}', 'wb') as af: + af.write(content) +PYEOF + +# Submit attachment hashes to VirusTotal +# Submit URLs to URLhaus or PhishTank for reputation check +``` + +## Key Concepts + +| Concept | Description | +|---------|-------------| +| SPF (Sender Policy Framework) | DNS record specifying authorized mail servers for a domain | +| DKIM (DomainKeys Identified Mail) | Cryptographic signature verifying email content integrity | +| DMARC | Policy framework combining SPF and DKIM for sender authentication | +| Received headers | Server-added headers showing each hop in the delivery chain (read bottom to top) | +| Return-Path | Envelope sender address used for bounce messages; may differ from From | +| Message-ID | Unique identifier assigned by the originating mail server | +| X-Originating-IP | Original sender IP address (added by some mail services) | +| Header forgery | Attackers can forge From, Reply-To, and other headers but not Received chains | + +## Tools & Systems + +| Tool | Purpose | +|------|---------| +| MXToolbox | Online email header analyzer and DNS lookup | +| dig/nslookup | DNS record queries for SPF, DKIM, DMARC verification | +| pyspf | Python SPF record validation library | +| dkimpy | Python DKIM signature verification library | +| PhishTool | Specialized phishing email analysis platform | +| VirusTotal | URL and file reputation checking service | +| AbuseIPDB | IP address reputation database | +| whois | Domain registration information lookup | + +## Common Scenarios + +**Scenario 1: CEO Fraud / Business Email Compromise** +The email claims to be from the CEO but Reply-To points to a Gmail address, SPF fails because the sending IP is not authorized for the spoofed domain, DKIM is missing, and the From domain is a lookalike (ceo-company.com vs company.com). + +**Scenario 2: Credential Harvesting Phishing** +Email contains a link that displays "login.microsoft.com" but href points to a lookalike domain, the attachment is an HTML file containing a fake login page with credential exfiltration JavaScript, the sending domain was registered 3 days ago. + +**Scenario 3: Malware Delivery via Attachment** +Email with an Office document attachment containing macros, the sender domain passes SPF but the account was compromised, DKIM signature is valid (sent from legitimate infrastructure), attachment SHA-256 matches known malware on VirusTotal. + +**Scenario 4: Spear Phishing with Legitimate Service** +Attacker uses a legitimate email marketing service to send phishing, SPF and DKIM pass because the service is authorized, the phishing is in the content not the infrastructure, requires URL and content analysis rather than header authentication checks. + +## Output Format + +``` +Email Header Analysis Report: + Subject: "Urgent: Invoice Payment Required" + From: accounting@examp1e-corp.com (SPOOFED) + Reply-To: payments.urgent@gmail.com (MISMATCH) + Return-Path: + Date: 2024-01-15 09:23:45 UTC + + Delivery Path (4 hops): + Hop 1: mail-server.xyz [203.0.113.45] -> relay1.isp.com + Hop 2: relay1.isp.com -> mx.target-company.com + Hop 3: mx.target-company.com -> internal-filter.target.com + Hop 4: internal-filter.target.com -> mailbox + + Authentication: + SPF: FAIL (203.0.113.45 not authorized for examp1e-corp.com) + DKIM: NONE (no signature present) + DMARC: FAIL (p=none, no enforcement) + + Indicators of Phishing: + - Lookalike domain (examp1e-corp.com vs example-corp.com, 96% similar) + - From/Reply-To mismatch + - Domain registered 2 days before email sent + - URL in body points to credential harvesting page + - Attachment: invoice.xlsm (SHA-256: a3f2...) - Known malware on VT + + Risk Level: HIGH +``` diff --git a/personas/_shared/skills/analyzing-email-headers-for-phishing-investigation/references/api-reference.md b/personas/_shared/skills/analyzing-email-headers-for-phishing-investigation/references/api-reference.md new file mode 100644 index 0000000..6a690b5 --- /dev/null +++ b/personas/_shared/skills/analyzing-email-headers-for-phishing-investigation/references/api-reference.md @@ -0,0 +1,121 @@ +# API Reference: Email Header Analysis Tools + +## Python email Module + +### Parsing EML Files +```python +import email +from email import policy + +with open("phishing.eml", "r") as f: + msg = email.message_from_file(f, policy=policy.default) + +msg["From"] # From header +msg["To"] # To header +msg["Subject"] # Subject line +msg["Message-ID"] # Unique message identifier +msg["Reply-To"] # Reply-To address +msg["Return-Path"] # Envelope sender +msg.get_all("Received") # All Received headers (list) +msg.get_all("Authentication-Results") # Auth results +``` + +### Body and Attachment Extraction +```python +body = msg.get_body(preferencelist=("html", "plain")) +content = body.get_content() + +for part in msg.walk(): + if part.get_content_disposition() == "attachment": + filename = part.get_filename() + data = part.get_payload(decode=True) +``` + +## dig - DNS Record Lookup + +### SPF Record +```bash +dig TXT example.com +short +# Output: "v=spf1 include:_spf.google.com ~all" +``` + +### DKIM Record +```bash +dig TXT selector1._domainkey.example.com +short +``` + +### DMARC Record +```bash +dig TXT _dmarc.example.com +short +# Output: "v=DMARC1; p=reject; rua=mailto:dmarc@example.com" +``` + +## pyspf - SPF Validation (Python) + +### Syntax +```python +import spf +result, explanation = spf.check2( + i="203.0.113.45", # Sending IP + s="sender@example.com", # Envelope sender + h="mail.example.com" # HELO hostname +) +# Results: pass, fail, softfail, neutral, none, temperror, permerror +``` + +## dkimpy - DKIM Verification (Python) + +### Syntax +```python +import dkim +with open("email.eml", "rb") as f: + message = f.read() +result = dkim.verify(message) +# Returns True/False +``` + +## AbuseIPDB - IP Reputation + +### API Endpoint +```bash +curl -G "https://api.abuseipdb.com/api/v2/check" \ + -H "Key: YOUR_API_KEY" \ + -H "Accept: application/json" \ + -d "ipAddress=203.0.113.45" -d "maxAgeInDays=90" +``` + +### Response Fields +| Field | Description | +|-------|-------------| +| `abuseConfidenceScore` | 0-100 confidence of abuse | +| `totalReports` | Number of abuse reports | +| `countryCode` | Source country | +| `isp` | Internet service provider | + +## VirusTotal - Domain/URL Reputation + +### Domain Lookup +```bash +curl -H "x-apikey: YOUR_KEY" \ + "https://www.virustotal.com/api/v3/domains/suspicious.com" +``` + +### URL Scan +```bash +curl -X POST "https://www.virustotal.com/api/v3/urls" \ + -H "x-apikey: YOUR_KEY" \ + -d "url=http://suspicious-url.com/login" +``` + +## whois - Domain Registration + +### Syntax +```bash +whois suspicious-domain.com +``` + +### Key Fields +- `Registrar` - Domain registrar +- `Creation Date` - When domain was registered +- `Registrant` - Domain owner info +- `Name Server` - Authoritative DNS servers diff --git a/personas/_shared/skills/analyzing-email-headers-for-phishing-investigation/scripts/agent.py b/personas/_shared/skills/analyzing-email-headers-for-phishing-investigation/scripts/agent.py new file mode 100644 index 0000000..9754f32 --- /dev/null +++ b/personas/_shared/skills/analyzing-email-headers-for-phishing-investigation/scripts/agent.py @@ -0,0 +1,229 @@ +#!/usr/bin/env python3 +"""Email header analysis agent for phishing investigation and sender verification.""" + +import email +import email.utils +import re +import hashlib +import os +import sys +import subprocess +from email import policy + + +def parse_email_file(eml_path): + """Parse an EML file and extract key header fields.""" + with open(eml_path, "r", errors="replace") as f: + msg = email.message_from_file(f, policy=policy.default) + headers = { + "from": str(msg["From"] or ""), + "to": str(msg["To"] or ""), + "subject": str(msg["Subject"] or ""), + "date": str(msg["Date"] or ""), + "message_id": str(msg["Message-ID"] or ""), + "reply_to": str(msg["Reply-To"] or ""), + "return_path": str(msg["Return-Path"] or ""), + "x_mailer": str(msg["X-Mailer"] or ""), + "x_originating_ip": str(msg["X-Originating-IP"] or ""), + } + return msg, headers + + +def extract_received_chain(msg): + """Extract and parse the Received header chain (bottom-up = chronological).""" + received_headers = msg.get_all("Received") or [] + hops = [] + ip_pattern = re.compile(r"\[?(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\]?") + for i, header in enumerate(reversed(received_headers)): + ips = ip_pattern.findall(header) + hops.append({ + "hop": i + 1, + "header": header.strip()[:200], + "ips": ips, + }) + return hops + + +def extract_authentication_results(msg): + """Extract SPF, DKIM, and DMARC results from Authentication-Results headers.""" + auth_results = msg.get_all("Authentication-Results") or [] + received_spf = str(msg.get("Received-SPF", "")) + dkim_sig = str(msg.get("DKIM-Signature", "")) + results = { + "spf": "unknown", + "dkim": "unknown", + "dmarc": "unknown", + "raw_authentication_results": [], + "received_spf": received_spf, + "has_dkim_signature": bool(dkim_sig), + } + for ar in auth_results: + results["raw_authentication_results"].append(ar.strip()) + ar_lower = ar.lower() + if "spf=" in ar_lower: + spf_match = re.search(r"spf=(\w+)", ar_lower) + if spf_match: + results["spf"] = spf_match.group(1) + if "dkim=" in ar_lower: + dkim_match = re.search(r"dkim=(\w+)", ar_lower) + if dkim_match: + results["dkim"] = dkim_match.group(1) + if "dmarc=" in ar_lower: + dmarc_match = re.search(r"dmarc=(\w+)", ar_lower) + if dmarc_match: + results["dmarc"] = dmarc_match.group(1) + return results + + +def check_from_replyto_mismatch(headers): + """Detect mismatch between From and Reply-To addresses.""" + from_addr = email.utils.parseaddr(headers["from"])[1].lower() + reply_to = headers["reply_to"] + if reply_to: + reply_addr = email.utils.parseaddr(reply_to)[1].lower() + if reply_addr and from_addr != reply_addr: + return True, from_addr, reply_addr + return False, from_addr, None + + +def extract_urls(msg): + """Extract all URLs from the email body.""" + body = msg.get_body(preferencelist=("html", "plain")) + urls = [] + if body: + content = body.get_content() + urls = list(set(re.findall(r"https?://[^\s<>\"']+", content))) + return urls + + +def detect_url_mismatch(msg): + """Detect hyperlinks where display text differs from actual href.""" + body = msg.get_body(preferencelist=("html",)) + mismatches = [] + if body: + content = body.get_content() + href_pattern = re.findall( + r']*href=["\']([^"\']+)["\'][^>]*>(.*?)', content, re.DOTALL + ) + for href, text in href_pattern: + display_urls = re.findall(r"https?://[^\s<]+", text) + if display_urls: + for display_url in display_urls: + if display_url.rstrip("/") != href.rstrip("/"): + mismatches.append({ + "display_url": display_url, + "actual_url": href, + }) + return mismatches + + +def extract_attachments(msg, output_dir=None): + """Extract and hash all email attachments.""" + attachments = [] + for part in msg.walk(): + if part.get_content_disposition() == "attachment": + filename = part.get_filename() or "unnamed_attachment" + content = part.get_payload(decode=True) + if content: + sha256 = hashlib.sha256(content).hexdigest() + md5 = hashlib.md5(content).hexdigest() + att_info = { + "filename": filename, + "size": len(content), + "sha256": sha256, + "md5": md5, + "content_type": part.get_content_type(), + } + if output_dir: + os.makedirs(output_dir, exist_ok=True) + filepath = os.path.join(output_dir, filename) + with open(filepath, "wb") as f: + f.write(content) + att_info["saved_to"] = filepath + attachments.append(att_info) + return attachments + + +def dns_lookup(domain, record_type="TXT"): + """Perform DNS lookup for SPF/DKIM/DMARC records.""" + stdout, _, rc = subprocess.run( + ["dig", record_type, domain, "+short"], + capture_output=True, text=True, timeout=10 + ).stdout, "", 0 + return stdout.strip() if stdout else "" + + +def check_domain_spf(domain): + """Look up the SPF record for a domain.""" + return dns_lookup(domain, "TXT") + + +def check_domain_dmarc(domain): + """Look up the DMARC record for a domain.""" + return dns_lookup(f"_dmarc.{domain}", "TXT") + + +def generate_phishing_indicators(headers, auth, hops, url_mismatches, attachments): + """Compile a list of phishing indicators from the analysis.""" + indicators = [] + mismatch, from_addr, reply_addr = check_from_replyto_mismatch(headers) + if mismatch: + indicators.append(f"From/Reply-To mismatch: {from_addr} vs {reply_addr}") + if auth["spf"] in ("fail", "softfail"): + indicators.append(f"SPF {auth['spf']}") + if auth["dkim"] == "fail" or not auth["has_dkim_signature"]: + indicators.append("DKIM failed or missing") + if auth["dmarc"] in ("fail", "none"): + indicators.append(f"DMARC {auth['dmarc']}") + if url_mismatches: + indicators.append(f"{len(url_mismatches)} URL display/href mismatches detected") + for att in attachments: + if any(att["filename"].endswith(ext) for ext in [".exe", ".scr", ".vbs", ".js", + ".docm", ".xlsm", ".bat", ".ps1", ".hta"]): + indicators.append(f"Suspicious attachment: {att['filename']}") + return indicators + + +if __name__ == "__main__": + print("=" * 60) + print("Email Header Phishing Analysis Agent") + print("SPF/DKIM/DMARC validation, URL analysis, attachment extraction") + print("=" * 60) + + eml_file = sys.argv[1] if len(sys.argv) > 1 else None + + if eml_file and os.path.exists(eml_file): + print(f"\n[*] Analyzing: {eml_file}") + msg, headers = parse_email_file(eml_file) + print(f" From: {headers['from']}") + print(f" To: {headers['to']}") + print(f" Subject: {headers['subject']}") + print(f" Date: {headers['date']}") + + hops = extract_received_chain(msg) + print(f"\n[*] Delivery path: {len(hops)} hops") + for hop in hops: + print(f" Hop {hop['hop']}: IPs={hop['ips']}") + + auth = extract_authentication_results(msg) + print(f"\n[*] Authentication: SPF={auth['spf']} DKIM={auth['dkim']} DMARC={auth['dmarc']}") + + urls = extract_urls(msg) + print(f"\n[*] URLs found: {len(urls)}") + url_mismatches = detect_url_mismatch(msg) + for m in url_mismatches: + print(f" [!] MISMATCH: Display='{m['display_url']}' Actual='{m['actual_url']}'") + + attachments = extract_attachments(msg) + print(f"\n[*] Attachments: {len(attachments)}") + for att in attachments: + print(f" {att['filename']} ({att['size']} bytes) SHA256={att['sha256'][:16]}...") + + indicators = generate_phishing_indicators(headers, auth, hops, url_mismatches, attachments) + if indicators: + print(f"\n[!] PHISHING INDICATORS:") + for ind in indicators: + print(f" - {ind}") + else: + print(f"\n[DEMO] Usage: python agent.py ") + print("[*] Provide an EML file for phishing analysis.") diff --git a/personas/_shared/skills/analyzing-ethereum-smart-contract-vulnerabilities/LICENSE b/personas/_shared/skills/analyzing-ethereum-smart-contract-vulnerabilities/LICENSE new file mode 100644 index 0000000..d885118 --- /dev/null +++ b/personas/_shared/skills/analyzing-ethereum-smart-contract-vulnerabilities/LICENSE @@ -0,0 +1,201 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to the Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by the Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding any notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. Please do not remove or change + the license header comment from a contributed file except when + necessary. + + Copyright 2026 mukul975 + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/personas/_shared/skills/analyzing-ethereum-smart-contract-vulnerabilities/SKILL.md b/personas/_shared/skills/analyzing-ethereum-smart-contract-vulnerabilities/SKILL.md new file mode 100644 index 0000000..a94a14a --- /dev/null +++ b/personas/_shared/skills/analyzing-ethereum-smart-contract-vulnerabilities/SKILL.md @@ -0,0 +1,67 @@ +--- +name: analyzing-ethereum-smart-contract-vulnerabilities +description: Perform static and symbolic analysis of Solidity smart contracts using Slither and Mythril to detect reentrancy, + integer overflow, access control, and other vulnerability classes before deployment to Ethereum mainnet. +domain: cybersecurity +subdomain: blockchain-security +tags: +- ethereum +- solidity +- smart-contract +- slither +- mythril +- blockchain +- defi +- audit +version: '1.0' +author: mahipal +license: Apache-2.0 +nist_csf: +- PR.DS-01 +- PR.DS-02 +- ID.RA-01 +--- + +# Analyzing Ethereum Smart Contract Vulnerabilities + +## Overview + +Smart contract vulnerabilities have led to billions of dollars in losses across DeFi protocols. Unlike traditional software, deployed smart contracts are immutable and handle real financial assets, making pre-deployment security analysis critical. Slither performs fast static analysis using an intermediate representation to detect over 90 vulnerability patterns in seconds, while Mythril uses symbolic execution and SMT solving to discover complex execution path vulnerabilities like reentrancy and integer overflows. This skill covers running both tools against Solidity contracts, interpreting results, triaging findings by severity, and generating audit reports. + + +## When to Use + +- When investigating security incidents that require analyzing ethereum smart contract vulnerabilities +- When building detection rules or threat hunting queries for this domain +- When SOC analysts need structured procedures for this analysis type +- When validating security monitoring coverage for related attack techniques + +## Prerequisites + +- Python 3.10+ with pip +- Slither (pip install slither-analyzer) and solc compiler +- Mythril (pip install mythril) with solc-select for compiler version management +- Solidity source code or compiled contract bytecode +- Foundry or Hardhat development framework (optional, for project-level analysis) + +## Steps + +### Step 1: Run Slither Static Analysis + +Execute Slither against the contract codebase to identify vulnerability patterns, optimization opportunities, and code quality issues using its 90+ built-in detectors. + +### Step 2: Run Mythril Symbolic Execution + +Run Mythril deep analysis to explore execution paths and discover reentrancy, unchecked external calls, and arithmetic vulnerabilities that require path-sensitive analysis. + +### Step 3: Triage and Correlate Findings + +Combine results from both tools, deduplicate findings, assess severity based on exploitability and financial impact, and filter false positives. + +### Step 4: Generate Audit Report + +Produce a structured audit report with vulnerability descriptions, affected code locations, exploit scenarios, and remediation recommendations. + +## Expected Output + +JSON report listing vulnerabilities with SWC (Smart Contract Weakness Classification) identifiers, severity ratings, affected functions, and suggested fixes. diff --git a/personas/_shared/skills/analyzing-ethereum-smart-contract-vulnerabilities/references/api-reference.md b/personas/_shared/skills/analyzing-ethereum-smart-contract-vulnerabilities/references/api-reference.md new file mode 100644 index 0000000..aa8f97a --- /dev/null +++ b/personas/_shared/skills/analyzing-ethereum-smart-contract-vulnerabilities/references/api-reference.md @@ -0,0 +1,103 @@ +# API Reference: Analyzing Ethereum Smart Contract Vulnerabilities + +## Slither CLI + +```bash +# Basic analysis +slither contracts/ + +# JSON output +slither contracts/ --json slither-report.json + +# Run specific detector only +slither contracts/ --detect reentrancy-eth,unprotected-upgrade + +# List all detectors +slither --list-detectors + +# Print contract summary +slither contracts/ --print human-summary + +# Generate inheritance graph +slither contracts/ --print inheritance-graph +``` + +## Mythril CLI + +```bash +# Analyze single contract +myth analyze contracts/Token.sol + +# JSON output +myth analyze contracts/Token.sol -o json + +# Set execution timeout +myth analyze contracts/Token.sol --execution-timeout 300 + +# Analyze deployed bytecode +myth analyze --address 0x1234... --rpc infura + +# Increase analysis depth +myth analyze contracts/Token.sol --max-depth 50 --transaction-count 3 +``` + +## Slither Detector Severity Levels + +| Impact | Confidence | Example Detectors | +|--------|------------|-------------------| +| High | High | reentrancy-eth, suicidal, arbitrary-send-eth | +| High | Medium | controlled-delegatecall, reentrancy-no-eth | +| Medium | High | locked-ether, incorrect-equality | +| Medium | Medium | uninitialized-state, shadowing-state | +| Low | High | naming-convention, solc-version | +| Informational | High | pragma, dead-code | + +## SWC Registry (Key Entries) + +| SWC ID | Title | Tool Coverage | +|--------|-------|---------------| +| SWC-101 | Integer Overflow/Underflow | Mythril | +| SWC-104 | Unchecked Call Return | Slither + Mythril | +| SWC-106 | Unprotected SELFDESTRUCT | Slither + Mythril | +| SWC-107 | Reentrancy | Slither + Mythril | +| SWC-110 | Assert Violation | Mythril | +| SWC-112 | Delegatecall to Untrusted Callee | Slither | +| SWC-115 | tx.origin Authentication | Slither | +| SWC-116 | Block Timestamp Dependence | Mythril | +| SWC-120 | Weak Randomness | Slither | + +## Installation + +```bash +# Slither (requires solc) +pip install slither-analyzer +solc-select install 0.8.20 +solc-select use 0.8.20 + +# Mythril +pip install mythril +``` + +## Slither JSON Output Structure + +```json +{ + "success": true, + "results": { + "detectors": [{ + "check": "reentrancy-eth", + "impact": "High", + "confidence": "Medium", + "description": "Reentrancy in Contract.withdraw()", + "elements": [{"source_mapping": {"filename_short": "Contract.sol", "lines": [42, 43]}}] + }] + } +} +``` + +### References + +- Slither: https://github.com/crytic/slither +- Mythril: https://github.com/Consensys/mythril +- SWC Registry: https://swcregistry.io/ +- Solidity Security: https://docs.soliditylang.org/en/latest/security-considerations.html diff --git a/personas/_shared/skills/analyzing-ethereum-smart-contract-vulnerabilities/scripts/agent.py b/personas/_shared/skills/analyzing-ethereum-smart-contract-vulnerabilities/scripts/agent.py new file mode 100644 index 0000000..dbd2508 --- /dev/null +++ b/personas/_shared/skills/analyzing-ethereum-smart-contract-vulnerabilities/scripts/agent.py @@ -0,0 +1,168 @@ +#!/usr/bin/env python3 +"""Smart Contract Security Agent - runs Slither and Mythril analysis on Solidity contracts.""" + +import json +import argparse +import logging +import subprocess +from collections import defaultdict +from datetime import datetime + +logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s") +logger = logging.getLogger(__name__) + +SWC_REGISTRY = { + "SWC-101": "Integer Overflow and Underflow", + "SWC-104": "Unchecked Call Return Value", + "SWC-106": "Unprotected SELFDESTRUCT", + "SWC-107": "Reentrancy", + "SWC-110": "Assert Violation", + "SWC-112": "Delegatecall to Untrusted Callee", + "SWC-113": "DoS with Failed Call", + "SWC-115": "Authorization through tx.origin", + "SWC-116": "Block values as a proxy for time", + "SWC-120": "Weak Sources of Randomness", +} + + +def run_slither(contract_path): + """Run Slither static analysis on Solidity contract.""" + cmd = ["slither", contract_path, "--json", "-"] + result = subprocess.run(cmd, capture_output=True, text=True, timeout=120) + try: + return json.loads(result.stdout) if result.stdout else {} + except json.JSONDecodeError: + logger.error("Slither JSON parse failed") + return {} + + +def run_mythril(contract_path, timeout=300): + """Run Mythril symbolic execution analysis.""" + cmd = ["myth", "analyze", contract_path, "--execution-timeout", str(timeout), "-o", "json"] + result = subprocess.run(cmd, capture_output=True, text=True, timeout=timeout + 60) + try: + return json.loads(result.stdout) if result.stdout else {} + except json.JSONDecodeError: + logger.error("Mythril JSON parse failed") + return {} + + +def analyze_slither_results(slither_output): + """Parse and categorize Slither detector findings.""" + findings = [] + by_severity = defaultdict(int) + by_detector = defaultdict(int) + for detector in slither_output.get("results", {}).get("detectors", []): + severity = detector.get("impact", "informational").lower() + by_severity[severity] += 1 + det_name = detector.get("check", "unknown") + by_detector[det_name] += 1 + elements = detector.get("elements", []) + location = "" + if elements: + elem = elements[0] + location = f"{elem.get('source_mapping', {}).get('filename_short', '')}:" \ + f"L{elem.get('source_mapping', {}).get('lines', [0])[0] if elem.get('source_mapping', {}).get('lines') else 0}" + findings.append({ + "detector": det_name, + "severity": severity, + "description": detector.get("description", "")[:200], + "location": location, + "confidence": detector.get("confidence", ""), + }) + return { + "total": len(findings), + "by_severity": dict(by_severity), + "by_detector": dict(sorted(by_detector.items(), key=lambda x: x[1], reverse=True)[:15]), + "findings": sorted(findings, key=lambda x: {"high": 0, "medium": 1, "low": 2, "informational": 3}.get(x["severity"], 4)), + } + + +def analyze_mythril_results(mythril_output): + """Parse Mythril symbolic execution findings.""" + findings = [] + by_swc = defaultdict(int) + for issue in mythril_output.get("issues", []): + swc_id = issue.get("swc-id", "") + swc_key = f"SWC-{swc_id}" if swc_id else "unknown" + by_swc[swc_key] += 1 + severity = issue.get("severity", "Medium").lower() + findings.append({ + "swc_id": swc_key, + "swc_title": SWC_REGISTRY.get(swc_key, issue.get("title", "")), + "severity": severity, + "description": issue.get("description", "")[:200], + "contract": issue.get("contract", ""), + "function": issue.get("function", ""), + "line_number": issue.get("lineno", 0), + }) + return { + "total": len(findings), + "by_swc": dict(by_swc), + "findings": findings, + } + + +def deduplicate_findings(slither_findings, mythril_findings): + """Merge and deduplicate findings from both tools.""" + combined = [] + seen = set() + for f in slither_findings.get("findings", []): + key = (f.get("location", ""), f.get("detector", "")) + if key not in seen: + seen.add(key) + combined.append({**f, "source": "slither"}) + for f in mythril_findings.get("findings", []): + key = (f.get("contract", "") + str(f.get("line_number", 0)), f.get("swc_id", "")) + if key not in seen: + seen.add(key) + combined.append({**f, "source": "mythril"}) + return combined + + +def generate_report(contract_path, slither_analysis, mythril_analysis, combined): + critical_high = sum(1 for f in combined if f.get("severity") in ("high", "critical")) + return { + "timestamp": datetime.utcnow().isoformat(), + "contract": contract_path, + "slither_analysis": { + "total_findings": slither_analysis["total"], + "by_severity": slither_analysis["by_severity"], + "top_detectors": slither_analysis["by_detector"], + }, + "mythril_analysis": { + "total_findings": mythril_analysis["total"], + "by_swc": mythril_analysis["by_swc"], + }, + "combined_findings": len(combined), + "critical_high_findings": critical_high, + "audit_result": "FAIL" if critical_high > 0 else "PASS", + "findings": combined[:30], + } + + +def main(): + parser = argparse.ArgumentParser(description="Solidity Smart Contract Security Analysis Agent") + parser.add_argument("--contract", required=True, help="Path to Solidity contract or project directory") + parser.add_argument("--mythril-timeout", type=int, default=300, help="Mythril execution timeout (seconds)") + parser.add_argument("--skip-mythril", action="store_true", help="Skip Mythril (slow symbolic execution)") + parser.add_argument("--output", default="smart_contract_audit_report.json") + args = parser.parse_args() + + slither_output = run_slither(args.contract) + slither_analysis = analyze_slither_results(slither_output) + mythril_analysis = {"total": 0, "by_swc": {}, "findings": []} + if not args.skip_mythril: + mythril_output = run_mythril(args.contract, args.mythril_timeout) + mythril_analysis = analyze_mythril_results(mythril_output) + combined = deduplicate_findings(slither_analysis, mythril_analysis) + report = generate_report(args.contract, slither_analysis, mythril_analysis, combined) + with open(args.output, "w") as f: + json.dump(report, f, indent=2, default=str) + logger.info("Smart contract audit: %d findings (%d critical/high), result: %s", + report["combined_findings"], report["critical_high_findings"], report["audit_result"]) + print(json.dumps(report, indent=2, default=str)) + + +if __name__ == "__main__": + main() diff --git a/personas/_shared/skills/analyzing-golang-malware-with-ghidra/LICENSE b/personas/_shared/skills/analyzing-golang-malware-with-ghidra/LICENSE new file mode 100644 index 0000000..d885118 --- /dev/null +++ b/personas/_shared/skills/analyzing-golang-malware-with-ghidra/LICENSE @@ -0,0 +1,201 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to the Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by the Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding any notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. Please do not remove or change + the license header comment from a contributed file except when + necessary. + + Copyright 2026 mukul975 + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/personas/_shared/skills/analyzing-golang-malware-with-ghidra/SKILL.md b/personas/_shared/skills/analyzing-golang-malware-with-ghidra/SKILL.md new file mode 100644 index 0000000..99fb989 --- /dev/null +++ b/personas/_shared/skills/analyzing-golang-malware-with-ghidra/SKILL.md @@ -0,0 +1,311 @@ +--- +name: analyzing-golang-malware-with-ghidra +description: Reverse engineer Go-compiled malware using Ghidra with specialized scripts for function recovery, string extraction, + and type reconstruction in stripped Go binaries. +domain: cybersecurity +subdomain: malware-analysis +tags: +- golang +- ghidra +- reverse-engineering +- malware-analysis +- binary-analysis +- go-malware +- disassembly +version: '1.0' +author: mahipal +license: Apache-2.0 +nist_csf: +- DE.AE-02 +- RS.AN-03 +- ID.RA-01 +- DE.CM-01 +--- +# Analyzing Golang Malware with Ghidra + +## Overview + +Go (Golang) has become a popular language for malware authors due to its cross-compilation capabilities, static linking that produces self-contained binaries, and the complexity it introduces for reverse engineering. Go binaries contain the entire runtime, standard library, and all dependencies statically linked, resulting in large binaries (often 5-15MB) with thousands of functions. Ghidra struggles with Go-specific string formats (non-null-terminated), stripped function names, and goroutine concurrency patterns. Specialized tools like GoResolver (Volexity, 2025) use control-flow graph similarity to automatically deobfuscate and recover function names in stripped or obfuscated Go binaries. + + +## When to Use + +- When investigating security incidents that require analyzing golang malware with ghidra +- When building detection rules or threat hunting queries for this domain +- When SOC analysts need structured procedures for this analysis type +- When validating security monitoring coverage for related attack techniques + +## Prerequisites + +- Ghidra 11.0+ with JDK 17+ +- GoResolver plugin (for function name recovery) +- Go Reverse Engineering Tool Kit (go-re.tk) +- Python 3.9+ for helper scripts +- Understanding of Go runtime internals (goroutines, channels, interfaces) +- Familiarity with Go binary structure (pclntab, moduledata, itab) + +## Key Concepts + +### Go Binary Structure + +Go binaries embed rich metadata in the `pclntab` (PC Line Table) structure, which maps program counters to function names, source files, and line numbers. Even stripped binaries retain this metadata. The `moduledata` structure contains pointers to type information, itabs (interface tables), and the pclntab itself. Go strings are stored as a pointer-length pair rather than null-terminated C strings. + +### Function Recovery in Stripped Binaries + +Despite stripping symbol tables, Go binaries retain function names within the pclntab. However, obfuscation tools like garble rename functions to random strings. GoResolver addresses this by computing control-flow graph signatures of obfuscated functions and matching them against a database of known Go standard library and third-party package functions. + +### Crate/Dependency Extraction + +Go's dependency management embeds module paths and version strings in the binary. Extracting these reveals the malware's third-party dependencies (HTTP libraries, encryption packages, C2 frameworks), which provides insight into capabilities without full reverse engineering. + +## Workflow + +### Step 1: Initial Binary Analysis + +```python +#!/usr/bin/env python3 +"""Analyze Go binary metadata for malware analysis.""" +import struct +import sys +import re + + +def find_go_build_info(data): + """Extract Go build information from binary.""" + # Go buildinfo magic: \xff Go buildinf: + magic = b'\xff Go buildinf:' + offset = data.find(magic) + if offset == -1: + return None + + print(f"[+] Go build info at offset 0x{offset:x}") + + # Extract Go version string nearby + go_version = re.search(rb'go\d+\.\d+(?:\.\d+)?', data[offset:offset+256]) + if go_version: + print(f" Go Version: {go_version.group().decode()}") + + return offset + + +def find_pclntab(data): + """Locate the pclntab (PC Line Table) structure.""" + # pclntab magic bytes vary by Go version + magics = { + b'\xfb\xff\xff\xff\x00\x00': "Go 1.2-1.15", + b'\xfa\xff\xff\xff\x00\x00': "Go 1.16-1.17", + b'\xf1\xff\xff\xff\x00\x00': "Go 1.18-1.19", + b'\xf0\xff\xff\xff\x00\x00': "Go 1.20+", + } + + for magic, version in magics.items(): + offset = data.find(magic) + if offset != -1: + print(f"[+] pclntab found at 0x{offset:x} ({version})") + return offset, version + + return None, None + + +def extract_function_names(data, pclntab_offset): + """Extract function names from pclntab.""" + if pclntab_offset is None: + return [] + + functions = [] + # Function name strings follow specific patterns + func_pattern = re.compile( + rb'(?:main|runtime|fmt|net|os|crypto|encoding|io|sync|' + rb'syscall|reflect|strings|bytes|path|time|math|sort|' + rb'github\.com|golang\.org)[/\.][\w/.]+', + ) + + for match in func_pattern.finditer(data): + name = match.group().decode('utf-8', errors='replace') + if len(name) > 4 and len(name) < 200: + functions.append(name) + + return sorted(set(functions)) + + +def extract_go_strings(data): + """Extract Go-style strings (pointer+length pairs).""" + # Go strings are not null-terminated; extract readable sequences + strings = [] + ascii_pattern = re.compile(rb'[\x20-\x7e]{10,}') + + for match in ascii_pattern.finditer(data): + s = match.group().decode('ascii') + # Filter for interesting malware strings + interesting = [ + 'http', 'https', 'tcp', 'udp', 'dns', + 'cmd', 'shell', 'exec', 'upload', 'download', + 'encrypt', 'decrypt', 'key', 'token', 'password', + 'c2', 'beacon', 'agent', 'implant', 'bot', + 'mutex', 'persist', 'registry', 'scheduled', + ] + if any(kw in s.lower() for kw in interesting): + strings.append(s) + + return strings + + +def extract_dependencies(data): + """Extract Go module dependencies from binary.""" + deps = [] + # Module paths follow pattern: github.com/user/repo + dep_pattern = re.compile( + rb'((?:github\.com|gitlab\.com|golang\.org|gopkg\.in|' + rb'go\.etcd\.io|google\.golang\.org)/[^\x00\s]{5,80})' + ) + + for match in dep_pattern.finditer(data): + dep = match.group().decode('utf-8', errors='replace') + deps.append(dep) + + unique_deps = sorted(set(deps)) + return unique_deps + + +def analyze_go_binary(filepath): + """Full analysis of Go malware binary.""" + with open(filepath, 'rb') as f: + data = f.read() + + print(f"[+] Analyzing Go binary: {filepath}") + print(f" File size: {len(data):,} bytes") + print("=" * 60) + + # Build info + find_go_build_info(data) + + # pclntab + pclntab_offset, go_version = find_pclntab(data) + + # Functions + functions = extract_function_names(data, pclntab_offset) + print(f"\n[+] Recovered {len(functions)} function names") + + # Categorize functions + categories = { + "network": [], "crypto": [], "os_exec": [], + "file_io": [], "main": [], "third_party": [], + } + for f in functions: + if 'net/' in f or 'http' in f.lower(): + categories["network"].append(f) + elif 'crypto' in f: + categories["crypto"].append(f) + elif 'os/exec' in f or 'syscall' in f: + categories["os_exec"].append(f) + elif 'os.' in f or 'io/' in f: + categories["file_io"].append(f) + elif f.startswith('main.'): + categories["main"].append(f) + elif 'github.com' in f or 'golang.org' in f: + categories["third_party"].append(f) + + for cat, funcs in categories.items(): + if funcs: + print(f"\n [{cat}] ({len(funcs)} functions):") + for fn in funcs[:10]: + print(f" {fn}") + + # Dependencies + deps = extract_dependencies(data) + print(f"\n[+] Dependencies ({len(deps)}):") + for dep in deps[:20]: + print(f" {dep}") + + # Suspicious strings + sus_strings = extract_go_strings(data) + print(f"\n[+] Suspicious strings ({len(sus_strings)}):") + for s in sus_strings[:20]: + print(f" {s}") + + +if __name__ == "__main__": + if len(sys.argv) < 2: + print(f"Usage: {sys.argv[0]} ") + sys.exit(1) + analyze_go_binary(sys.argv[1]) +``` + +### Step 2: Ghidra Analysis Script + +```python +# Ghidra script (run within Ghidra's script manager) +# Save as AnalyzeGoBinary.py in Ghidra scripts directory + +# @category MalwareAnalysis +# @description Analyze Go binary structure and recover metadata + +def analyze_go_binary_ghidra(): + """Ghidra script for Go binary analysis.""" + from ghidra.program.model.mem import MemoryAccessException + + program = getCurrentProgram() + memory = program.getMemory() + listing = program.getListing() + + print("[+] Go Binary Analysis Script") + print(f" Program: {program.getName()}") + + # Find pclntab + pclntab_magics = [ + bytes([0xf0, 0xff, 0xff, 0xff]), # Go 1.20+ + bytes([0xf1, 0xff, 0xff, 0xff]), # Go 1.18-1.19 + bytes([0xfa, 0xff, 0xff, 0xff]), # Go 1.16-1.17 + bytes([0xfb, 0xff, 0xff, 0xff]), # Go 1.2-1.15 + ] + + for magic in pclntab_magics: + addr = memory.findBytes( + program.getMinAddress(), magic, None, True, None + ) + if addr: + print(f"[+] pclntab found at {addr}") + # Create label + program.getSymbolTable().createLabel( + addr, "go_pclntab", None, + ghidra.program.model.symbol.SourceType.ANALYSIS + ) + break + + # Fix Go string definitions + # Go strings are ptr+len, not null terminated + print("[+] Fixing Go string references...") + + # Search for function names containing package paths + symbol_table = program.getSymbolTable() + func_count = 0 + for symbol in symbol_table.getAllSymbols(True): + name = symbol.getName() + if ('.' in name and + any(pkg in name for pkg in + ['main.', 'runtime.', 'net.', 'crypto.', 'os.'])): + func_count += 1 + + print(f"[+] Found {func_count} Go function symbols") + + +# Execute +analyze_go_binary_ghidra() +``` + +## Validation Criteria + +- Go version and build information extracted from binary +- pclntab located and parsed for function name recovery +- Third-party dependencies identified revealing malware capabilities +- Main package functions enumerated for targeted analysis +- Network, crypto, and OS exec functions categorized +- Ghidra analysis correctly labels Go runtime structures + +## References + +- [CUJO AI - Reverse Engineering Go Binaries with Ghidra](https://cujo.com/blog/reverse-engineering-go-binaries-with-ghidra/) +- [Volexity GoResolver](https://www.volexity.com/blog/2025/04/01/goresolver-using-control-flow-graph-similarity-to-deobfuscate-golang-binaries-automatically/) +- [Go Reverse Engineering Tool Kit](https://go-re.tk/about/) +- [SentinelOne AlphaGolang](https://www.sentinelone.com/labs/alphagolang-a-step-by-step-go-malware-reversing-methodology-for-ida-pro/) +- [Go Binary Reversing Notes](https://gist.github.com/0xdevalias/4e430914124c3fd2c51cb7ac2801acba) diff --git a/personas/_shared/skills/analyzing-golang-malware-with-ghidra/assets/template.md b/personas/_shared/skills/analyzing-golang-malware-with-ghidra/assets/template.md new file mode 100644 index 0000000..8b2f0ce --- /dev/null +++ b/personas/_shared/skills/analyzing-golang-malware-with-ghidra/assets/template.md @@ -0,0 +1,35 @@ +# Go Malware Analysis Report + +## Sample Information +| Field | Value | +|-------|-------| +| SHA-256 | | +| File Size | | +| Go Version | | +| Architecture | amd64 / arm64 / 386 | +| Stripped | Yes / No | +| Obfuscated | Yes (garble) / No | + +## Recovered Functions +| Category | Count | Key Functions | +|----------|-------|---------------| +| main | | | +| networking | | | +| crypto | | | +| os/exec | | | +| third-party | | | + +## Dependencies +| Module | Purpose | +|--------|---------| +| | | + +## C2 Infrastructure +| Indicator | Type | Value | +|-----------|------|-------| +| | URL / IP / Domain | | + +## Recommendations +1. Block identified C2 infrastructure +2. Create YARA rule for unique Go function signatures +3. Monitor for similar Go binary compilation artifacts diff --git a/personas/_shared/skills/analyzing-golang-malware-with-ghidra/references/api-reference.md b/personas/_shared/skills/analyzing-golang-malware-with-ghidra/references/api-reference.md new file mode 100644 index 0000000..5777b76 --- /dev/null +++ b/personas/_shared/skills/analyzing-golang-malware-with-ghidra/references/api-reference.md @@ -0,0 +1,90 @@ +# API Reference: Go Malware Analysis with Ghidra + +## Ghidra Go Analysis Setup + +### GoResolver Script (Volexity) +```bash +# Install GoResolver for stripped Go binary function recovery +git clone https://github.com/volexity/GoResolver +# Run against Ghidra project +analyzeHeadless /ghidra_projects MyProject -process go_malware.exe \ + -postScript GoResolver.java +``` + +### Ghidra Built-in Go Support (10.3+) +``` +File > Import > Select Go binary +Analysis > Auto Analyze (includes GolangAnalyzer) +Window > Function Tags > Filter "go." +``` + +## Go Binary Characteristics + +### Build Info Magic +``` +Offset in .go.buildinfo section: "\xff Go buildinf:" +``` + +### gopclntab Magic Bytes +| Go Version | Magic | +|------------|-------| +| 1.2-1.15 | `FB FF FF FF 00 00` | +| 1.16-1.17 | `FA FF FF FF 00 00` | +| 1.18-1.19 | `F0 FF FF FF 00 00` | +| 1.20+ | `F1 FF FF FF 00 00` | + +### String Format +Go strings are length-prefixed (not null-terminated): +``` +struct GoString { + char *ptr; // pointer to string data + int64 length; // string length +}; +``` + +## Go-Specific Ghidra Scripts + +### GoReSym (Mandiant) +```bash +GoReSym -t -d -p /path/to/binary +# -t: Recover type information +# -d: Dump function metadata +# -p: Print package listing +``` + +### redress (Go Reverse Engineering) +```bash +redress -src binary.exe # Reconstruct source tree +redress -pkg binary.exe # List packages +redress -type binary.exe # Type information +redress -string binary.exe # Go string extraction +redress -interface binary.exe # Interface types +``` + +## Go Obfuscation Tools + +| Tool | Technique | Detection | +|------|-----------|-----------| +| garble | Function name hashing, literal obfuscation | Hash-like symbols, missing debug info | +| gobfuscate | Package/function renaming | Random 8-char names | +| go-strip | Symbol table removal | Missing gopclntab entries | + +## Common Go Malware Families + +| Family | Type | Notable Packages | +|--------|------|-----------------| +| Sliver | C2 implant | protobuf, grpc, mtls | +| Merlin | C2 agent | http2, jose, websocket | +| Sunlogin/Cobalt | RAT | screenshot, clipboard, keylog | +| BianLian | Ransomware | crypto/aes, filepath.Walk | +| Royal | Ransomware | goroutine-based parallel encryption | + +## Key Ghidra Analysis Steps +``` +1. Search > For Strings > "go1." (version identification) +2. Search > For Bytes > FB FF FF FF (gopclntab) +3. Symbol Table > Filter "main." (entry points) +4. Navigation > Go To "runtime.main" (program start) +5. Decompiler > Check goroutine spawns (runtime.newproc) +6. Data Types > Apply GoString struct to string references +``` diff --git a/personas/_shared/skills/analyzing-golang-malware-with-ghidra/references/standards.md b/personas/_shared/skills/analyzing-golang-malware-with-ghidra/references/standards.md new file mode 100644 index 0000000..2614646 --- /dev/null +++ b/personas/_shared/skills/analyzing-golang-malware-with-ghidra/references/standards.md @@ -0,0 +1,29 @@ +# Go Binary Analysis Standards + +## Go Binary Structure +| Component | Description | Location | +|-----------|-------------|----------| +| pclntab | PC-to-function mapping table | .gopclntab or .text | +| moduledata | Runtime metadata structure | .noptrdata | +| itab | Interface method tables | .rodata | +| buildinfo | Go version and module info | .go.buildinfo | +| typelinks | Type descriptor table | .rodata | + +## pclntab Magic Bytes by Go Version +| Magic | Go Version | +|-------|-----------| +| 0xFBFFFFFF | 1.2 - 1.15 | +| 0xFAFFFFFF | 1.16 - 1.17 | +| 0xF1FFFFFF | 1.18 - 1.19 | +| 0xF0FFFFFF | 1.20+ | + +## Common Go Malware Families +- Sliver C2 implant +- Geacon (Go Cobalt Strike beacon) +- GoBruteforcer +- Kaiji botnet +- Chaos botnet (Go-based) + +## References +- [Go Runtime Source](https://github.com/golang/go/tree/master/src/runtime) +- [Go Internal ABI](https://go.dev/s/regcallabi) diff --git a/personas/_shared/skills/analyzing-golang-malware-with-ghidra/references/workflows.md b/personas/_shared/skills/analyzing-golang-malware-with-ghidra/references/workflows.md new file mode 100644 index 0000000..da39f40 --- /dev/null +++ b/personas/_shared/skills/analyzing-golang-malware-with-ghidra/references/workflows.md @@ -0,0 +1,37 @@ +# Go Malware Analysis Workflows + +## Workflow 1: Stripped Binary Recovery +``` +[Stripped Go Binary] --> [Find pclntab] --> [Recover Function Names] + | + v + [Apply GoResolver] --> [Deobfuscate Names] + | + v + [Categorize Functions] +``` + +## Workflow 2: Full Ghidra Analysis +``` +[Go Binary] --> [Import to Ghidra] --> [Run Go Analysis Scripts] + | + v + [Fix String References] + | + v + [Identify main Package] + | + v + [Analyze C2/Network Logic] +``` + +## Workflow 3: Dependency-Based Capability Assessment +``` +[Go Binary] --> [Extract Module Info] --> [List Dependencies] + | + v + [Map to Capabilities] + | + v + [Prioritize Analysis] +``` diff --git a/personas/_shared/skills/analyzing-golang-malware-with-ghidra/scripts/agent.py b/personas/_shared/skills/analyzing-golang-malware-with-ghidra/scripts/agent.py new file mode 100644 index 0000000..5eda1cb --- /dev/null +++ b/personas/_shared/skills/analyzing-golang-malware-with-ghidra/scripts/agent.py @@ -0,0 +1,267 @@ +#!/usr/bin/env python3 +"""Go malware analysis agent for Ghidra-assisted reverse engineering. + +Analyzes Go binaries to extract function names, strings, build metadata, +package information, and detects common Go malware characteristics. +""" + +import os +import sys +import json +import hashlib +import re +import math +from collections import Counter + + +def compute_hash(filepath): + """Compute SHA-256 hash of file.""" + sha256 = hashlib.sha256() + with open(filepath, "rb") as f: + for chunk in iter(lambda: f.read(65536), b""): + sha256.update(chunk) + return sha256.hexdigest() + + +def shannon_entropy(data): + """Calculate Shannon entropy.""" + if not data: + return 0.0 + freq = Counter(data) + length = len(data) + return -sum((c / length) * math.log2(c / length) for c in freq.values()) + + +def detect_go_binary(filepath): + """Detect if a binary is compiled with Go and extract version info.""" + with open(filepath, "rb") as f: + data = f.read() + + indicators = { + "is_go_binary": False, + "go_version": None, + "go_buildinfo": False, + "gopclntab_found": False, + } + + # Go build info magic + buildinfo_magic = b"\xff Go buildinf:" + offset = data.find(buildinfo_magic) + if offset != -1: + indicators["is_go_binary"] = True + indicators["go_buildinfo"] = True + + # Go version string + version_pattern = rb"go(\d+\.\d+(?:\.\d+)?)" + matches = re.findall(version_pattern, data) + if matches: + indicators["is_go_binary"] = True + versions = sorted(set(m.decode() for m in matches)) + indicators["go_version"] = versions[-1] if versions else None + + # gopclntab (Go PC line table) magic bytes + gopclntab_magics = [ + b"\xfb\xff\xff\xff\x00\x00", # Go 1.2-1.15 + b"\xfa\xff\xff\xff\x00\x00", # Go 1.16-1.17 + b"\xf0\xff\xff\xff\x00\x00", # Go 1.18+ + b"\xf1\xff\xff\xff\x00\x00", # Go 1.20+ + ] + for magic in gopclntab_magics: + if magic in data: + indicators["gopclntab_found"] = True + indicators["is_go_binary"] = True + break + + # Runtime strings + go_strings = [b"runtime.main", b"runtime.goexit", b"runtime.gopanic", + b"runtime.newproc", b"GOROOT", b"GOPATH"] + found_runtime = sum(1 for s in go_strings if s in data) + if found_runtime >= 2: + indicators["is_go_binary"] = True + indicators["runtime_strings_found"] = found_runtime + + return indicators + + +def extract_go_strings(filepath, min_length=6): + """Extract Go-style strings (length-prefixed, not null-terminated).""" + with open(filepath, "rb") as f: + data = f.read() + + # Standard ASCII string extraction + ascii_pattern = re.compile(rb"[\x20-\x7e]{%d,}" % min_length) + strings = [m.group().decode("ascii", errors="replace") for m in ascii_pattern.finditer(data)] + return strings + + +def extract_go_packages(strings_list): + """Identify Go packages from extracted strings.""" + packages = set() + pkg_pattern = re.compile(r"^([a-zA-Z0-9_]+(?:/[a-zA-Z0-9_.-]+)+)\.") + for s in strings_list: + match = pkg_pattern.match(s) + if match: + packages.add(match.group(1)) + # Also look for known Go import paths + for s in strings_list: + if s.startswith("github.com/") or s.startswith("golang.org/"): + parts = s.split("/") + if len(parts) >= 3: + packages.add("/".join(parts[:3])) + return sorted(packages) + + +SUSPICIOUS_GO_PACKAGES = { + "github.com/kbinani/screenshot": "Screen capture capability", + "github.com/atotto/clipboard": "Clipboard access", + "github.com/go-vgo/robotgo": "Desktop automation / keylogging", + "github.com/miekg/dns": "Custom DNS resolution (C2/tunneling)", + "golang.org/x/crypto/ssh": "SSH client (lateral movement)", + "github.com/shirou/gopsutil": "System enumeration", + "github.com/mitchellh/go-ps": "Process listing", + "github.com/gobuffalo/packr": "Binary resource embedding", + "github.com/Ne0nd0g/merlin": "Merlin C2 agent", + "github.com/BishopFox/sliver": "Sliver C2 framework", + "github.com/traefik/yaegi": "Go interpreter (dynamic execution)", +} + + +def detect_suspicious_packages(packages): + """Flag suspicious Go packages commonly used in malware.""" + findings = [] + for pkg in packages: + for sus_pkg, description in SUSPICIOUS_GO_PACKAGES.items(): + if sus_pkg in pkg: + findings.append({"package": pkg, "concern": description}) + return findings + + +def analyze_sections(filepath): + """Analyze PE/ELF sections for Go binary characteristics.""" + with open(filepath, "rb") as f: + magic = f.read(4) + f.seek(0) + data = f.read() + + sections = [] + if magic[:2] == b"MZ": # PE + try: + import pefile + pe = pefile.PE(data=data) + for section in pe.sections: + name = section.Name.rstrip(b"\x00").decode("ascii", errors="replace") + entropy = section.get_entropy() + sections.append({ + "name": name, "virtual_size": section.Misc_VirtualSize, + "raw_size": section.SizeOfRawData, "entropy": round(entropy, 3), + }) + pe.close() + except ImportError: + sections.append({"note": "pefile not installed"}) + elif magic[:4] == b"\x7fELF": + try: + from elftools.elf.elffile import ELFFile + from io import BytesIO + elf = ELFFile(BytesIO(data)) + for section in elf.iter_sections(): + sec_data = section.data() if section.header.sh_size > 0 else b"" + entropy = shannon_entropy(sec_data) if sec_data else 0 + sections.append({ + "name": section.name, "size": section.header.sh_size, + "entropy": round(entropy, 3), "type": section.header.sh_type, + }) + except ImportError: + sections.append({"note": "pyelftools not installed"}) + return sections + + +def detect_obfuscation(go_info, strings_list): + """Detect Go binary obfuscation (garble, gobfuscate).""" + indicators = {"obfuscated": False, "techniques": []} + + # Garble replaces function names with hashes + hash_names = sum(1 for s in strings_list if re.match(r"^[a-f0-9]{16,}$", s)) + if hash_names > 20: + indicators["obfuscated"] = True + indicators["techniques"].append("Possible garble obfuscation (hash-like function names)") + + # Missing gopclntab suggests stripping + if not go_info.get("gopclntab_found"): + indicators["techniques"].append("gopclntab not found - may be stripped or modified") + + # Low runtime string count + if go_info.get("runtime_strings_found", 0) < 2: + indicators["obfuscated"] = True + indicators["techniques"].append("Low Go runtime string count - possible obfuscation") + + return indicators + + +def generate_report(filepath): + """Generate comprehensive Go malware analysis report.""" + report = { + "file": filepath, + "sha256": compute_hash(filepath), + "size": os.path.getsize(filepath), + } + + go_info = detect_go_binary(filepath) + report["go_detection"] = go_info + + if not go_info["is_go_binary"]: + report["conclusion"] = "Not identified as a Go binary" + return report + + strings_list = extract_go_strings(filepath) + report["total_strings"] = len(strings_list) + + packages = extract_go_packages(strings_list) + report["packages"] = packages[:50] + + suspicious = detect_suspicious_packages(packages) + report["suspicious_packages"] = suspicious + + sections = analyze_sections(filepath) + report["sections"] = sections + + obfuscation = detect_obfuscation(go_info, strings_list) + report["obfuscation"] = obfuscation + + return report + + +if __name__ == "__main__": + print("=" * 60) + print("Go Malware Analysis Agent (Ghidra-assisted)") + print("Go binary detection, package extraction, obfuscation detection") + print("=" * 60) + + target = sys.argv[1] if len(sys.argv) > 1 else None + + if not target or not os.path.exists(target): + print("\n[DEMO] Usage: python agent.py ") + sys.exit(0) + + report = generate_report(target) + go = report.get("go_detection", {}) + print(f"\n[*] File: {target}") + print(f"[*] SHA-256: {report['sha256']}") + print(f"[*] Go binary: {go.get('is_go_binary', False)}") + print(f"[*] Go version: {go.get('go_version', 'unknown')}") + print(f"[*] Strings: {report.get('total_strings', 0)}") + + print("\n--- Packages ---") + for pkg in report.get("packages", [])[:15]: + print(f" {pkg}") + + print("\n--- Suspicious Packages ---") + for s in report.get("suspicious_packages", []): + print(f" [!] {s['package']}: {s['concern']}") + + print("\n--- Obfuscation ---") + obf = report.get("obfuscation", {}) + print(f" Obfuscated: {obf.get('obfuscated', False)}") + for t in obf.get("techniques", []): + print(f" {t}") + + print(f"\n{json.dumps(report, indent=2, default=str)}") diff --git a/personas/_shared/skills/analyzing-golang-malware-with-ghidra/scripts/process.py b/personas/_shared/skills/analyzing-golang-malware-with-ghidra/scripts/process.py new file mode 100644 index 0000000..1951c7d --- /dev/null +++ b/personas/_shared/skills/analyzing-golang-malware-with-ghidra/scripts/process.py @@ -0,0 +1,162 @@ +#!/usr/bin/env python3 +""" +Go Malware Binary Analyzer + +Extracts metadata, function names, dependencies, and suspicious +indicators from Go-compiled malware binaries. + +Usage: + python process.py --file malware.exe --output report.json +""" + +import argparse +import json +import re +import struct +import sys +from pathlib import Path + + +PCLNTAB_MAGICS = { + b'\xf0\xff\xff\xff': "Go 1.20+", + b'\xf1\xff\xff\xff': "Go 1.18-1.19", + b'\xfa\xff\xff\xff': "Go 1.16-1.17", + b'\xfb\xff\xff\xff': "Go 1.2-1.15", +} + + +def find_pclntab(data): + for magic, version in PCLNTAB_MAGICS.items(): + offset = data.find(magic) + if offset != -1: + return offset, version + return None, None + + +def extract_go_version(data): + match = re.search(rb'go(\d+\.\d+(?:\.\d+)?)', data) + return match.group(1).decode() if match else "unknown" + + +def extract_functions(data): + func_pattern = re.compile( + rb'((?:main|runtime|fmt|net|os|crypto|encoding|io|sync|' + rb'syscall|reflect|strings|bytes|path|time|math|sort|' + rb'github\.com|golang\.org|gopkg\.in)[/\.][\w/.]+)' + ) + functions = set() + for match in func_pattern.finditer(data): + name = match.group(1).decode('utf-8', errors='replace') + if 4 < len(name) < 200: + functions.add(name) + return sorted(functions) + + +def extract_dependencies(data): + dep_pattern = re.compile( + rb'((?:github\.com|gitlab\.com|golang\.org|gopkg\.in|' + rb'go\.etcd\.io|google\.golang\.org)/[\w./-]{5,80})' + ) + deps = set() + for match in dep_pattern.finditer(data): + dep = match.group(1).decode('utf-8', errors='replace') + # Clean up trailing artifacts + dep = dep.rstrip('/.') + deps.add(dep) + return sorted(deps) + + +def extract_suspicious_strings(data): + interesting_patterns = [ + rb'https?://[\w./:?&=-]+', + rb'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}(?::\d+)?', + rb'(?:cmd|powershell|bash|sh)(?:\.exe)?', + rb'(?:HKLM|HKCU)\\[^\x00]+', + rb'/etc/(?:passwd|shadow|crontab)', + ] + + results = {} + for pattern in interesting_patterns: + matches = re.findall(pattern, data) + if matches: + decoded = [m.decode('utf-8', errors='replace') for m in matches] + results[pattern.decode('utf-8', errors='replace')] = list(set(decoded)) + + return results + + +def categorize_functions(functions): + categories = { + "main_logic": [], + "networking": [], + "cryptography": [], + "os_execution": [], + "file_operations": [], + "third_party": [], + "runtime": [], + } + + for func in functions: + fl = func.lower() + if func.startswith('main.'): + categories["main_logic"].append(func) + elif any(x in fl for x in ['net/', 'http', 'tcp', 'udp', 'dns']): + categories["networking"].append(func) + elif 'crypto' in fl: + categories["cryptography"].append(func) + elif any(x in fl for x in ['os/exec', 'syscall']): + categories["os_execution"].append(func) + elif any(x in fl for x in ['os.', 'io/', 'ioutil']): + categories["file_operations"].append(func) + elif any(x in fl for x in ['github.com', 'golang.org', 'gopkg.in']): + categories["third_party"].append(func) + elif func.startswith('runtime.'): + categories["runtime"].append(func) + + return {k: v for k, v in categories.items() if v} + + +def analyze(filepath): + with open(filepath, 'rb') as f: + data = f.read() + + report = { + "file": str(filepath), + "size": len(data), + "go_version": extract_go_version(data), + } + + pclntab_offset, pclntab_version = find_pclntab(data) + report["pclntab"] = { + "offset": f"0x{pclntab_offset:x}" if pclntab_offset else None, + "version": pclntab_version, + } + + functions = extract_functions(data) + report["total_functions"] = len(functions) + report["function_categories"] = categorize_functions(functions) + + report["dependencies"] = extract_dependencies(data) + report["suspicious_strings"] = extract_suspicious_strings(data) + + return report + + +def main(): + parser = argparse.ArgumentParser(description="Go Malware Analyzer") + parser.add_argument("--file", required=True, help="Go binary to analyze") + parser.add_argument("--output", help="Output JSON report") + + args = parser.parse_args() + report = analyze(args.file) + + print(json.dumps(report, indent=2)) + + if args.output: + with open(args.output, 'w') as f: + json.dump(report, f, indent=2) + print(f"\n[+] Report saved to {args.output}") + + +if __name__ == "__main__": + main() diff --git a/personas/_shared/skills/analyzing-heap-spray-exploitation/LICENSE b/personas/_shared/skills/analyzing-heap-spray-exploitation/LICENSE new file mode 100644 index 0000000..d885118 --- /dev/null +++ b/personas/_shared/skills/analyzing-heap-spray-exploitation/LICENSE @@ -0,0 +1,201 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to the Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by the Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding any notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. Please do not remove or change + the license header comment from a contributed file except when + necessary. + + Copyright 2026 mukul975 + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/personas/_shared/skills/analyzing-heap-spray-exploitation/SKILL.md b/personas/_shared/skills/analyzing-heap-spray-exploitation/SKILL.md new file mode 100644 index 0000000..5cc7823 --- /dev/null +++ b/personas/_shared/skills/analyzing-heap-spray-exploitation/SKILL.md @@ -0,0 +1,59 @@ +--- +name: analyzing-heap-spray-exploitation +description: Detect and analyze heap spray attacks in memory dumps using Volatility3 plugins to identify NOP sled patterns, + shellcode landing zones, and suspicious large allocations in process virtual address space. +domain: cybersecurity +subdomain: malware-analysis +tags: +- malware-analysis +- memory-forensics +- heap-spray +- volatility3 +- exploit-analysis +version: '1.0' +author: mahipal +license: Apache-2.0 +nist_csf: +- DE.AE-02 +- RS.AN-03 +- ID.RA-01 +- DE.CM-01 +--- +# Analyzing Heap Spray Exploitation + +## Overview + +Heap spraying is an exploitation technique that fills large regions of a process's heap with attacker-controlled data (typically NOP sleds followed by shellcode) to increase the reliability of code execution exploits. This skill covers detecting heap spray artifacts in memory dumps using Volatility3's malfind, vadinfo, and memmap plugins, identifying suspicious contiguous memory allocations, scanning for NOP sled patterns (0x90, 0x0c0c0c0c), and extracting embedded shellcode for analysis. + + +## When to Use + +- When investigating security incidents that require analyzing heap spray exploitation +- When building detection rules or threat hunting queries for this domain +- When SOC analysts need structured procedures for this analysis type +- When validating security monitoring coverage for related attack techniques + +## Prerequisites + +- Python 3.9+ with `volatility3` framework installed +- Memory dump file (.raw, .vmem, .dmp format) +- Understanding of virtual memory layout and VAD (Virtual Address Descriptor) trees +- Familiarity with common shellcode patterns and NOP sled encodings + +## Steps + +### Step 1: Identify Suspicious Processes +Use Volatility3 windows.malfind to scan for processes with executable injected memory regions. + +### Step 2: Analyze VAD Entries +Examine VAD tree entries using windows.vadinfo for large contiguous allocations with RWX permissions. + +### Step 3: Scan for NOP Sled Patterns +Search suspicious memory regions for NOP sled signatures (0x90 sequences, 0x0c0c0c0c patterns). + +### Step 4: Extract and Analyze Shellcode +Dump suspicious memory regions and identify shellcode using byte pattern analysis. + +## Expected Output + +JSON report with suspicious processes, heap spray indicators, NOP sled locations, memory region sizes, and extracted shellcode hashes. diff --git a/personas/_shared/skills/analyzing-heap-spray-exploitation/references/api-reference.md b/personas/_shared/skills/analyzing-heap-spray-exploitation/references/api-reference.md new file mode 100644 index 0000000..230dec1 --- /dev/null +++ b/personas/_shared/skills/analyzing-heap-spray-exploitation/references/api-reference.md @@ -0,0 +1,55 @@ +# API Reference: Analyzing Heap Spray Exploitation + +## Volatility3 Plugins for Heap Spray Analysis + +| Plugin | Command | Purpose | +|--------|---------|---------| +| malfind | `vol -f dump.raw windows.malfind` | Find injected executable memory regions | +| vadinfo | `vol -f dump.raw windows.vadinfo` | Virtual Address Descriptor details | +| memmap | `vol -f dump.raw windows.memmap --pid PID --dump` | Dump process memory to files | +| pslist | `vol -f dump.raw windows.pslist` | List running processes | +| handles | `vol -f dump.raw windows.handles --pid PID` | List process handles | + +## Common Heap Spray NOP Sled Patterns + +| Pattern | Hex | Description | +|---------|-----|-------------| +| x86 NOP | 0x90909090 | Classic NOP instruction | +| 0x0C landing | 0x0C0C0C0C | Common heap spray address target | +| 0x0D landing | 0x0D0D0D0D | Alternative spray address | +| 0x0A landing | 0x0A0A0A0A | Alternative spray address | +| 0x41 fill | 0x41414141 | "AAAA" padding fill | + +## Shellcode Signatures + +| Bytes | Mnemonic | Context | +|-------|----------|---------| +| FC E8 | CLD; CALL | Common shellcode prologue | +| 60 E8 | PUSHAD; CALL | Register-saving shellcode start | +| 31 C0 50 68 | XOR EAX; PUSH; PUSH | Stack setup for API call | +| E8 FF FF FF FF | CALL $+5 | Self-locating shellcode (GetPC) | + +## Detection Thresholds + +| Indicator | Threshold | Meaning | +|-----------|-----------|---------| +| Large allocation | >= 1 MB per region | Suspicious heap allocation | +| Total spray size | >= 50 MB per process | Strong heap spray indicator | +| NOP sled count | >= 20 repeated bytes | NOP sled detected | +| RWX permissions | PAGE_EXECUTE_READWRITE | Injected executable code | + +## Install Volatility3 + +```bash +pip install volatility3 +# Or from source: +git clone https://github.com/volatilityfoundation/volatility3.git +cd volatility3 && pip install -e . +``` + +## References + +- Volatility3 GitHub: https://github.com/volatilityfoundation/volatility3 +- Volatility3 malfind: https://volatility3.readthedocs.io/en/latest/ +- Heap Spray Techniques: https://www.corelan.be/index.php/2011/12/31/exploit-writing-tutorial-part-11-heap-spraying-demystified/ +- DFRWS 2025 Workshop: https://webdiis.unizar.es/~ricardo/dfrws-eu-25-workshop/ diff --git a/personas/_shared/skills/analyzing-heap-spray-exploitation/scripts/agent.py b/personas/_shared/skills/analyzing-heap-spray-exploitation/scripts/agent.py new file mode 100644 index 0000000..d67b295 --- /dev/null +++ b/personas/_shared/skills/analyzing-heap-spray-exploitation/scripts/agent.py @@ -0,0 +1,219 @@ +#!/usr/bin/env python3 +"""Agent for analyzing heap spray exploitation in memory dumps. + +Detects heap spray artifacts using Volatility3 by scanning for +NOP sled patterns, large contiguous allocations, and injected +executable regions in process virtual address space. +""" +# For authorized forensic analysis only + +import argparse +import hashlib +import json +import os +import re +import subprocess +from collections import defaultdict +from datetime import datetime +from pathlib import Path + +NOP_PATTERNS = { + "x86_nop": b"\x90" * 16, + "heap_spray_0c": b"\x0c" * 16, + "heap_spray_0d": b"\x0d" * 16, + "heap_spray_0a": b"\x0a" * 16, + "heap_spray_04": b"\x04" * 16, + "heap_spray_41": b"\x41" * 16, +} + +SHELLCODE_MARKERS = [ + b"\xfc\xe8", # CLD; CALL + b"\x60\xe8", # PUSHAD; CALL + b"\xeb\x10\x5a", # JMP SHORT; POP EDX + b"\x31\xc0\x50\x68", # XOR EAX; PUSH; PUSH + b"\xe8\xff\xff\xff\xff", # CALL $+5 (self-locating) +] + +SUSPICIOUS_ALLOC_THRESHOLD = 0x100000 # 1 MB + + +class HeapSprayAnalyzer: + """Detects heap spray exploitation artifacts in memory dumps.""" + + def __init__(self, memory_dump, output_dir="./heap_spray_analysis"): + self.memory_dump = memory_dump + self.output_dir = Path(output_dir) + self.output_dir.mkdir(parents=True, exist_ok=True) + self.findings = [] + + def _run_vol3(self, plugin, extra_args=None): + """Run a Volatility3 plugin and return stdout.""" + cmd = ["vol", "-f", self.memory_dump, plugin] + if extra_args: + cmd.extend(extra_args) + try: + result = subprocess.run(cmd, capture_output=True, text=True, timeout=300) + return result.stdout + except (FileNotFoundError, subprocess.TimeoutExpired): + return "" + + def run_malfind(self): + """Run windows.malfind to detect injected executable memory.""" + output = self._run_vol3("windows.malfind") + entries = [] + current = {} + for line in output.splitlines(): + parts = line.split() + if len(parts) >= 6 and parts[0].isdigit(): + if current: + entries.append(current) + current = { + "pid": int(parts[0]), + "process": parts[1], + "start_addr": parts[2], + "end_addr": parts[3], + "protection": parts[5] if len(parts) > 5 else "", + } + elif current and line.strip().startswith("0x"): + hex_match = re.findall(r"[0-9a-fA-F]{2}", line.split(" ")[0] if " " in line else line) + if "hex_bytes" not in current: + current["hex_bytes"] = "" + current["hex_bytes"] += "".join(hex_match) + if current: + entries.append(current) + return entries + + def run_vadinfo(self): + """Run windows.vadinfo to find large suspicious allocations.""" + output = self._run_vol3("windows.vadinfo") + large_allocs = [] + for line in output.splitlines(): + parts = line.split() + if len(parts) >= 5 and parts[0].isdigit(): + try: + pid = int(parts[0]) + start = int(parts[2], 16) if parts[2].startswith("0x") else 0 + end = int(parts[3], 16) if parts[3].startswith("0x") else 0 + size = end - start + if size >= SUSPICIOUS_ALLOC_THRESHOLD: + large_allocs.append({ + "pid": pid, "process": parts[1], + "start": hex(start), "end": hex(end), + "size_bytes": size, "size_mb": round(size / (1024 * 1024), 2), + }) + except (ValueError, IndexError): + continue + return large_allocs + + def scan_dump_for_patterns(self, dump_path): + """Scan a memory dump file for NOP sled and shellcode patterns.""" + matches = {"nop_sleds": [], "shellcode_markers": []} + try: + with open(dump_path, "rb") as f: + data = f.read() + except (FileNotFoundError, PermissionError): + return matches + + for name, pattern in NOP_PATTERNS.items(): + offset = 0 + count = 0 + while True: + idx = data.find(pattern, offset) + if idx == -1: + break + count += 1 + offset = idx + len(pattern) + if count > 100: + break + if count > 0: + matches["nop_sleds"].append({"pattern": name, "occurrences": count}) + + for marker in SHELLCODE_MARKERS: + idx = data.find(marker) + if idx != -1: + context = data[idx:idx + 64] + matches["shellcode_markers"].append({ + "offset": hex(idx), + "bytes": context.hex()[:128], + "sha256": hashlib.sha256(context).hexdigest(), + }) + return matches + + def dump_process_memory(self, pid): + """Dump a process's memory using Volatility3 memmap.""" + dump_dir = self.output_dir / f"pid_{pid}" + dump_dir.mkdir(exist_ok=True) + self._run_vol3("windows.memmap", ["--pid", str(pid), "--dump", + "--output-dir", str(dump_dir)]) + dumps = list(dump_dir.glob("*.dmp")) + return [str(d) for d in dumps] + + def analyze(self): + """Run full heap spray analysis pipeline.""" + malfind_results = self.run_malfind() + large_allocs = self.run_vadinfo() + + spray_candidates = defaultdict(list) + for alloc in large_allocs: + spray_candidates[alloc["pid"]].append(alloc) + + for pid, allocs in spray_candidates.items(): + total_mb = sum(a["size_mb"] for a in allocs) + if total_mb > 50: + self.findings.append({ + "severity": "high", "type": "Heap Spray Indicator", + "detail": f"PID {pid}: {total_mb:.1f} MB in {len(allocs)} large allocations", + }) + + for entry in malfind_results: + hex_bytes = entry.get("hex_bytes", "") + if hex_bytes.count("90") > 20 or hex_bytes.count("0c") > 20: + self.findings.append({ + "severity": "critical", "type": "NOP Sled in Injected Region", + "detail": f"PID {entry['pid']} ({entry['process']}): " + f"NOP sled at {entry['start_addr']}", + }) + + return { + "malfind_entries": malfind_results, + "large_allocations": large_allocs, + "spray_candidate_pids": list(spray_candidates.keys()), + } + + def generate_report(self): + analysis = self.analyze() + + report = { + "report_date": datetime.utcnow().isoformat(), + "memory_dump": self.memory_dump, + "malfind_count": len(analysis["malfind_entries"]), + "large_allocation_count": len(analysis["large_allocations"]), + **analysis, + "findings": self.findings, + "total_findings": len(self.findings), + } + out = self.output_dir / "heap_spray_report.json" + with open(out, "w") as f: + json.dump(report, f, indent=2, default=str) + print(json.dumps(report, indent=2, default=str)) + return report + + +def main(): + parser = argparse.ArgumentParser( + description="Analyze memory dumps for heap spray exploitation artifacts" + ) + parser.add_argument("memory_dump", help="Path to memory dump file (.raw, .vmem, .dmp)") + parser.add_argument("--output-dir", default="./heap_spray_analysis", + help="Output directory for report and dumps") + parser.add_argument("--alloc-threshold", type=int, default=0x100000, + help="Minimum allocation size in bytes to flag (default: 1MB)") + args = parser.parse_args() + + os.makedirs(args.output_dir, exist_ok=True) + analyzer = HeapSprayAnalyzer(args.memory_dump, output_dir=args.output_dir) + analyzer.generate_report() + + +if __name__ == "__main__": + main() diff --git a/personas/_shared/skills/analyzing-indicators-of-compromise/LICENSE b/personas/_shared/skills/analyzing-indicators-of-compromise/LICENSE new file mode 100644 index 0000000..d885118 --- /dev/null +++ b/personas/_shared/skills/analyzing-indicators-of-compromise/LICENSE @@ -0,0 +1,201 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to the Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by the Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding any notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. Please do not remove or change + the license header comment from a contributed file except when + necessary. + + Copyright 2026 mukul975 + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/personas/_shared/skills/analyzing-indicators-of-compromise/SKILL.md b/personas/_shared/skills/analyzing-indicators-of-compromise/SKILL.md new file mode 100644 index 0000000..3539430 --- /dev/null +++ b/personas/_shared/skills/analyzing-indicators-of-compromise/SKILL.md @@ -0,0 +1,163 @@ +--- +name: analyzing-indicators-of-compromise +description: 'Analyzes indicators of compromise (IOCs) including IP addresses, domains, file hashes, URLs, and email artifacts + to determine maliciousness confidence, campaign attribution, and blocking priority. Use when triaging IOCs from phishing + emails, security alerts, or external threat feeds; enriching raw IOCs with multi-source intelligence; or making block/monitor/whitelist + decisions. Activates for requests involving VirusTotal, AbuseIPDB, MalwareBazaar, MISP, or IOC enrichment pipelines. + + ' +domain: cybersecurity +subdomain: threat-intelligence +tags: +- IOC +- VirusTotal +- AbuseIPDB +- MalwareBazaar +- MISP +- threat-intelligence +- STIX +- NIST-CSF +version: 1.0.0 +author: mahipal +license: Apache-2.0 +atlas_techniques: +- AML.T0052 +nist_csf: +- ID.RA-01 +- ID.RA-05 +- DE.CM-01 +- DE.AE-02 +--- +# Analyzing Indicators of Compromise + +## When to Use + +Use this skill when: +- A phishing email or alert generates IOCs (URLs, IP addresses, file hashes) requiring rapid triage +- Automated feeds deliver bulk IOCs that need confidence scoring before ingestion into blocking controls +- An incident investigation requires contextual enrichment of observed network artifacts + +**Do not use** this skill in isolation for high-stakes blocking decisions — always combine automated enrichment with analyst judgment, especially for shared infrastructure (CDNs, cloud providers). + +## Prerequisites + +- VirusTotal API key (free or Enterprise) for multi-AV and sandbox lookup +- AbuseIPDB API key for IP reputation checks +- MISP instance or TIP for cross-referencing against known campaigns +- Python with `requests` and `vt-py` libraries, or SOAR platform with pre-built connectors + +## Workflow + +### Step 1: Normalize and Classify IOC Types + +Before enriching, classify each IOC: +- **IPv4/IPv6 address**: Check if RFC 1918 private (skip external enrichment), validate format +- **Domain/FQDN**: Defang for safe handling (`evil[.]com`), extract registered domain via tldextract +- **URL**: Extract domain + path separately; check for redirectors +- **File hash**: Identify hash type (MD5/SHA-1/SHA-256); prefer SHA-256 for uniqueness +- **Email address**: Split into domain (check MX/DMARC) and local part for pattern analysis + +Defang IOCs in documentation (replace `.` with `[.]` and `://` with `[://]`) to prevent accidental clicks. + +### Step 2: Multi-Source Enrichment + +**VirusTotal (file hash, URL, IP, domain)**: +```python +import vt + +client = vt.Client("YOUR_VT_API_KEY") + +# File hash lookup +file_obj = client.get_object(f"/files/{sha256_hash}") +detections = file_obj.last_analysis_stats +print(f"Malicious: {detections['malicious']}/{sum(detections.values())}") + +# Domain analysis +domain_obj = client.get_object(f"/domains/{domain}") +print(domain_obj.last_analysis_stats) +print(domain_obj.reputation) +client.close() +``` + +**AbuseIPDB (IP addresses)**: +```python +import requests + +response = requests.get( + "https://api.abuseipdb.com/api/v2/check", + headers={"Key": "YOUR_KEY", "Accept": "application/json"}, + params={"ipAddress": "1.2.3.4", "maxAgeInDays": 90} +) +data = response.json()["data"] +print(f"Confidence: {data['abuseConfidenceScore']}%, Reports: {data['totalReports']}") +``` + +**MalwareBazaar (file hashes)**: +```python +response = requests.post( + "https://mb-api.abuse.ch/api/v1/", + data={"query": "get_info", "hash": sha256_hash} +) +result = response.json() +if result["query_status"] == "ok": + print(result["data"][0]["tags"], result["data"][0]["signature"]) +``` + +### Step 3: Contextualize with Campaign Attribution + +Query MISP for existing events matching the IOC: +```python +from pymisp import PyMISP + +misp = PyMISP("https://misp.example.com", "API_KEY") +results = misp.search(value="evil-domain.com", type_attribute="domain") +for event in results: + print(event["Event"]["info"], event["Event"]["threat_level_id"]) +``` + +Check Shodan for IP context (hosting provider, open ports, banners) to identify if the IP belongs to bulletproof hosting or a legitimate cloud provider (false positive risk). + +### Step 4: Assign Confidence Score and Disposition + +Apply a tiered decision framework: +- **Block (High Confidence ≥ 70%)**: ≥15 AV detections on VT, AbuseIPDB score ≥70, matches known malware family or campaign +- **Monitor/Alert (Medium 40–69%)**: 5–14 AV detections, moderate AbuseIPDB score, no campaign attribution +- **Whitelist/Investigate (Low <40%)**: ≤4 AV detections, no abuse reports, legitimate service (Google, Cloudflare CDN IPs) +- **False Positive**: Legitimate business service incorrectly flagged; document and exclude from future alerts + +### Step 5: Document and Distribute + +Record findings in TIP/MISP with: +- All enrichment data collected (timestamps, source, score) +- Disposition decision and rationale +- Blocking actions taken (firewall, proxy, DNS sinkhole) +- Related incident ticket number + +Export to STIX indicator object with confidence field set appropriately. + +## Key Concepts + +| Term | Definition | +|------|-----------| +| **IOC** | Indicator of Compromise — observable network or host artifact indicating potential compromise | +| **Enrichment** | Process of adding contextual data to a raw IOC from multiple intelligence sources | +| **Defanging** | Modifying IOCs (replacing `.` with `[.]`) to prevent accidental activation in documentation | +| **False Positive Rate** | Percentage of benign artifacts incorrectly flagged as malicious; critical for tuning block thresholds | +| **Sinkhole** | DNS server redirecting malicious domain lookups to a benign IP for detection without blocking traffic entirely | +| **TTL** | Time-to-live for an IOC in blocking controls; IP indicators should expire after 30 days, domains after 90 days | + +## Tools & Systems + +- **VirusTotal**: Multi-engine malware scanner and threat intelligence platform with 70+ AV engines, sandbox reports, and community comments +- **AbuseIPDB**: Community-maintained IP reputation database with 90-day abuse report history +- **MalwareBazaar (abuse.ch)**: Free malware hash repository with YARA rule associations and malware family tagging +- **URLScan.io**: Free URL analysis service that captures screenshots, DOM, and network requests for phishing URL triage +- **Shodan**: Internet-wide scan data providing hosting provider, open ports, and banner information for IP enrichment + +## Common Pitfalls + +- **Blocking shared infrastructure**: CDN IPs (Cloudflare 104.21.x.x, AWS CloudFront) may legitimately host malicious content but blocking the IP disrupts thousands of legitimate sites. +- **VT score obsession**: Low VT detection count does not mean benign — zero-day malware and custom APT tools often score 0 initially. Check sandbox behavior, MISP, and passive DNS. +- **Missing defanging**: Pasting live IOCs in emails or Confluence docs can trigger automated URL scanners or phishing tools. +- **No expiration policy**: IOCs without TTLs accumulate in blocklists indefinitely, generating false positives as infrastructure is repurposed by legitimate users. +- **Over-relying on single source**: VirusTotal aggregates AV opinions — all may be wrong or lag behind emerging malware. Use 3+ independent sources for high-stakes decisions. diff --git a/personas/_shared/skills/analyzing-indicators-of-compromise/references/api-reference.md b/personas/_shared/skills/analyzing-indicators-of-compromise/references/api-reference.md new file mode 100644 index 0000000..f460a9c --- /dev/null +++ b/personas/_shared/skills/analyzing-indicators-of-compromise/references/api-reference.md @@ -0,0 +1,120 @@ +# API Reference: IOC Enrichment Tools + +## VirusTotal API v3 + +### File Hash Lookup +```bash +curl -H "x-apikey: $VT_KEY" \ + "https://www.virustotal.com/api/v3/files/" +``` + +### Domain Lookup +```bash +curl -H "x-apikey: $VT_KEY" \ + "https://www.virustotal.com/api/v3/domains/" +``` + +### IP Lookup +```bash +curl -H "x-apikey: $VT_KEY" \ + "https://www.virustotal.com/api/v3/ip_addresses/" +``` + +### Key Response Fields +| Field | Description | +|-------|-------------| +| `last_analysis_stats.malicious` | Number of AV engines detecting as malicious | +| `last_analysis_stats.undetected` | AV engines finding clean | +| `reputation` | Community reputation score | +| `popular_threat_classification` | Threat label consensus | + +### Python (vt-py) +```python +import vt +client = vt.Client("API_KEY") +file_obj = client.get_object(f"/files/{sha256}") +stats = file_obj.last_analysis_stats +client.close() +``` + +## AbuseIPDB API v2 + +### Check IP +```bash +curl -G "https://api.abuseipdb.com/api/v2/check" \ + -H "Key: $ABUSE_KEY" -H "Accept: application/json" \ + -d "ipAddress=1.2.3.4" -d "maxAgeInDays=90" +``` + +### Response Fields +| Field | Description | +|-------|-------------| +| `abuseConfidenceScore` | 0-100 abuse confidence | +| `totalReports` | Report count in timeframe | +| `countryCode` | Source country | +| `isp` | Internet service provider | +| `isTor` | Tor exit node flag | + +## MalwareBazaar API (abuse.ch) + +### Hash Lookup +```bash +curl -X POST "https://mb-api.abuse.ch/api/v1/" \ + -d "query=get_info" -d "hash=" +``` + +### Response Fields +| Field | Description | +|-------|-------------| +| `signature` | Malware family name | +| `tags` | Associated tags | +| `file_type` | File type identification | +| `first_seen` | First submission date | +| `reporter` | Submitting analyst | + +## URLScan.io API + +### Submit URL for Scan +```bash +curl -X POST "https://urlscan.io/api/v1/scan/" \ + -H "API-Key: $KEY" -H "Content-Type: application/json" \ + -d '{"url": "http://suspicious.com", "visibility": "private"}' +``` + +### Retrieve Results +```bash +curl "https://urlscan.io/api/v1/result//" +``` + +## Shodan API + +### IP Lookup +```bash +curl "https://api.shodan.io/shodan/host/?key=$SHODAN_KEY" +``` + +### Response Fields +| Field | Description | +|-------|-------------| +| `ports` | Open ports list | +| `os` | Operating system | +| `org` | Organization | +| `asn` | Autonomous system number | +| `hostnames` | Associated hostnames | + +## IOC Confidence Scoring Framework + +| Score | Disposition | Criteria | +|-------|-------------|----------| +| >= 70 | BLOCK | 15+ VT detections, AbuseIPDB >= 70%, or MalwareBazaar match | +| 40-69 | MONITOR | 5-14 VT detections, moderate abuse score | +| < 40 | INVESTIGATE | Low detection, no campaign attribution | + +## Defanging Convention + +| Original | Defanged | +|----------|----------| +| `http://` | `hxxp://` | +| `https://` | `hxxps://` | +| `.com` | `[.]com` | +| `evil.com` | `evil[.]com` | diff --git a/personas/_shared/skills/analyzing-indicators-of-compromise/scripts/agent.py b/personas/_shared/skills/analyzing-indicators-of-compromise/scripts/agent.py new file mode 100644 index 0000000..8c4d389 --- /dev/null +++ b/personas/_shared/skills/analyzing-indicators-of-compromise/scripts/agent.py @@ -0,0 +1,251 @@ +#!/usr/bin/env python3 +"""IOC analysis and enrichment agent using VirusTotal, AbuseIPDB, and MalwareBazaar APIs.""" + +import re +import os +import json +import datetime + +try: + import requests + HAS_REQUESTS = True +except ImportError: + HAS_REQUESTS = False + + +def classify_ioc(value): + """Classify an IOC by type: ipv4, domain, url, sha256, sha1, md5, email.""" + value = value.strip() + if re.match(r"^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$", value): + return "ipv4" + if re.match(r"^[a-fA-F0-9]{64}$", value): + return "sha256" + if re.match(r"^[a-fA-F0-9]{40}$", value): + return "sha1" + if re.match(r"^[a-fA-F0-9]{32}$", value): + return "md5" + if re.match(r"^https?://", value): + return "url" + if re.match(r"^[^@]+@[^@]+\.[^@]+$", value): + return "email" + if re.match(r"^[a-zA-Z0-9][a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$", value): + return "domain" + return "unknown" + + +def defang_ioc(value): + """Defang an IOC for safe documentation.""" + value = value.replace("http://", "hxxp://") + value = value.replace("https://", "hxxps://") + value = re.sub(r"\.(?=\w)", "[.]", value) + return value + + +def refang_ioc(value): + """Refang a defanged IOC for querying APIs.""" + value = value.replace("hxxp://", "http://") + value = value.replace("hxxps://", "https://") + value = value.replace("[.]", ".") + value = value.replace("[://]", "://") + return value + + +def is_private_ip(ip): + """Check if an IP is RFC 1918 private.""" + octets = [int(o) for o in ip.split(".")] + if octets[0] == 10: + return True + if octets[0] == 172 and 16 <= octets[1] <= 31: + return True + if octets[0] == 192 and octets[1] == 168: + return True + if octets[0] == 127: + return True + return False + + +def query_virustotal_hash(sha256, api_key): + """Query VirusTotal for a file hash.""" + url = f"https://www.virustotal.com/api/v3/files/{sha256}" + resp = requests.get(url, headers={"x-apikey": api_key}, timeout=30) + if resp.status_code == 200: + data = resp.json().get("data", {}).get("attributes", {}) + stats = data.get("last_analysis_stats", {}) + return { + "sha256": sha256, + "malicious": stats.get("malicious", 0), + "total": sum(stats.values()), + "type_description": data.get("type_description", ""), + "popular_threat_name": data.get("popular_threat_classification", {}).get( + "suggested_threat_label", ""), + "tags": data.get("tags", []), + } + return None + + +def query_virustotal_domain(domain, api_key): + """Query VirusTotal for domain reputation.""" + url = f"https://www.virustotal.com/api/v3/domains/{domain}" + resp = requests.get(url, headers={"x-apikey": api_key}, timeout=30) + if resp.status_code == 200: + data = resp.json().get("data", {}).get("attributes", {}) + stats = data.get("last_analysis_stats", {}) + return { + "domain": domain, + "malicious": stats.get("malicious", 0), + "suspicious": stats.get("suspicious", 0), + "reputation": data.get("reputation", 0), + "registrar": data.get("registrar", ""), + "creation_date": data.get("creation_date", ""), + } + return None + + +def query_abuseipdb(ip, api_key, max_age_days=90): + """Query AbuseIPDB for IP reputation.""" + url = "https://api.abuseipdb.com/api/v2/check" + resp = requests.get(url, headers={"Key": api_key, "Accept": "application/json"}, + params={"ipAddress": ip, "maxAgeInDays": max_age_days}, timeout=30) + if resp.status_code == 200: + data = resp.json().get("data", {}) + return { + "ip": ip, + "abuse_confidence": data.get("abuseConfidenceScore", 0), + "total_reports": data.get("totalReports", 0), + "country": data.get("countryCode", ""), + "isp": data.get("isp", ""), + "domain": data.get("domain", ""), + "is_tor": data.get("isTor", False), + } + return None + + +def query_malwarebazaar(sha256): + """Query MalwareBazaar for file hash information.""" + url = "https://mb-api.abuse.ch/api/v1/" + resp = requests.post(url, data={"query": "get_info", "hash": sha256}, timeout=30) + if resp.status_code == 200: + result = resp.json() + if result.get("query_status") == "ok" and result.get("data"): + entry = result["data"][0] + return { + "sha256": sha256, + "signature": entry.get("signature", ""), + "tags": entry.get("tags", []), + "file_type": entry.get("file_type", ""), + "reporter": entry.get("reporter", ""), + "first_seen": entry.get("first_seen", ""), + } + return None + + +def score_ioc(vt_result=None, abuse_result=None, mb_result=None): + """Assign a confidence score and disposition to an IOC.""" + score = 0 + reasons = [] + if vt_result: + malicious = vt_result.get("malicious", 0) + if malicious >= 15: + score += 40 + reasons.append(f"VT: {malicious} detections (high)") + elif malicious >= 5: + score += 20 + reasons.append(f"VT: {malicious} detections (moderate)") + elif malicious > 0: + score += 5 + reasons.append(f"VT: {malicious} detections (low)") + if abuse_result: + abuse_score = abuse_result.get("abuse_confidence", 0) + if abuse_score >= 70: + score += 30 + reasons.append(f"AbuseIPDB: {abuse_score}% confidence") + elif abuse_score >= 30: + score += 15 + reasons.append(f"AbuseIPDB: {abuse_score}% confidence") + if mb_result: + score += 30 + reasons.append(f"MalwareBazaar: {mb_result.get('signature', 'known malware')}") + + if score >= 70: + disposition = "BLOCK" + elif score >= 40: + disposition = "MONITOR" + else: + disposition = "INVESTIGATE" + + return {"score": score, "disposition": disposition, "reasons": reasons} + + +def enrich_ioc(value, vt_key=None, abuse_key=None): + """Enrich a single IOC with multi-source intelligence.""" + ioc_type = classify_ioc(value) + result = { + "ioc": value, + "type": ioc_type, + "defanged": defang_ioc(value), + "enrichment": {}, + "timestamp": datetime.datetime.utcnow().isoformat() + "Z", + } + if not HAS_REQUESTS: + result["error"] = "requests library not installed" + return result + if ioc_type == "ipv4" and is_private_ip(value): + result["note"] = "RFC 1918 private IP - skipping external enrichment" + return result + if ioc_type in ("sha256", "sha1", "md5") and vt_key: + result["enrichment"]["virustotal"] = query_virustotal_hash(value, vt_key) + result["enrichment"]["malwarebazaar"] = query_malwarebazaar(value) + elif ioc_type == "ipv4": + if abuse_key: + result["enrichment"]["abuseipdb"] = query_abuseipdb(value, abuse_key) + if vt_key: + result["enrichment"]["virustotal"] = query_virustotal_domain(value, vt_key) + elif ioc_type == "domain" and vt_key: + result["enrichment"]["virustotal"] = query_virustotal_domain(value, vt_key) + + scoring = score_ioc( + result["enrichment"].get("virustotal"), + result["enrichment"].get("abuseipdb"), + result["enrichment"].get("malwarebazaar"), + ) + result["score"] = scoring["score"] + result["disposition"] = scoring["disposition"] + result["reasons"] = scoring["reasons"] + return result + + +if __name__ == "__main__": + print("=" * 60) + print("IOC Analysis & Enrichment Agent") + print("VirusTotal, AbuseIPDB, MalwareBazaar integration") + print("=" * 60) + + demo_iocs = [ + "185.220.101.42", + "evil-domain.com", + "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "http://malicious-site.com/payload.exe", + "192.168.1.100", + ] + + print("\n--- IOC Classification & Defanging ---") + for ioc in demo_iocs: + ioc_type = classify_ioc(ioc) + defanged = defang_ioc(ioc) + private = " (private)" if ioc_type == "ipv4" and is_private_ip(ioc) else "" + print(f" {ioc_type:8s} | {defanged}{private}") + + vt_key = os.environ.get("VT_API_KEY") + abuse_key = os.environ.get("ABUSEIPDB_API_KEY") + + if vt_key or abuse_key: + print("\n--- Enrichment (live API queries) ---") + for ioc in demo_iocs: + result = enrich_ioc(ioc, vt_key, abuse_key) + print(f"\n {result['ioc']} ({result['type']})") + print(f" Disposition: {result.get('disposition', 'N/A')} " + f"(score: {result.get('score', 0)})") + for reason in result.get("reasons", []): + print(f" - {reason}") + else: + print("\n[*] Set VT_API_KEY and/or ABUSEIPDB_API_KEY environment variables for live enrichment.") diff --git a/personas/_shared/skills/analyzing-ios-app-security-with-objection/LICENSE b/personas/_shared/skills/analyzing-ios-app-security-with-objection/LICENSE new file mode 100644 index 0000000..d885118 --- /dev/null +++ b/personas/_shared/skills/analyzing-ios-app-security-with-objection/LICENSE @@ -0,0 +1,201 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to the Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by the Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding any notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. Please do not remove or change + the license header comment from a contributed file except when + necessary. + + Copyright 2026 mukul975 + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/personas/_shared/skills/analyzing-ios-app-security-with-objection/SKILL.md b/personas/_shared/skills/analyzing-ios-app-security-with-objection/SKILL.md new file mode 100644 index 0000000..3dea359 --- /dev/null +++ b/personas/_shared/skills/analyzing-ios-app-security-with-objection/SKILL.md @@ -0,0 +1,204 @@ +--- +name: analyzing-ios-app-security-with-objection +description: 'Performs runtime mobile security exploration of iOS applications using Objection, a Frida-powered toolkit that + enables security testers to interact with app internals without jailbreaking. Use when assessing iOS app security posture, + bypassing client-side protections, dumping keychain items, inspecting filesystem storage, and evaluating runtime behavior. + Activates for requests involving iOS security testing, Objection runtime analysis, Frida-based iOS assessment, or mobile + runtime exploration. + + ' +domain: cybersecurity +subdomain: mobile-security +author: mahipal +tags: +- mobile-security +- ios +- objection +- frida +- owasp-mobile +- penetration-testing +version: 1.0.0 +license: Apache-2.0 +atlas_techniques: +- AML.T0054 +nist_ai_rmf: +- MEASURE-2.7 +- MANAGE-2.4 +- GOVERN-6.2 +- MAP-5.1 +nist_csf: +- PR.PS-01 +- PR.AA-05 +- ID.RA-01 +- DE.CM-09 +--- +# Analyzing iOS App Security with Objection + +## When to Use + +Use this skill when: +- Performing runtime security assessment of iOS applications during authorized penetration tests +- Inspecting iOS keychain, filesystem, and memory for sensitive data exposure +- Bypassing client-side security controls (SSL pinning, jailbreak detection) during security testing +- Evaluating iOS app behavior at runtime without access to source code + +**Do not use** this skill on production devices without explicit authorization -- Objection modifies app runtime behavior and may trigger security monitoring. + +## Prerequisites + +- Python 3.10+ with pip +- Objection installed: `pip install objection` +- Frida installed: `pip install frida-tools` +- Target iOS device (jailbroken with Frida server, or non-jailbroken with repackaged IPA) +- For non-jailbroken: `objection patchipa` to inject Frida gadget into IPA +- macOS recommended for iOS testing (Xcode, ideviceinstaller) +- USB connection to target device or network Frida server + +## Workflow + +### Step 1: Prepare the Testing Environment + +**For jailbroken devices:** +```bash +# Install Frida server on device via Cydia/Sileo +# SSH to device and start Frida server +ssh root@ "/usr/sbin/frida-server -D" + +# Verify Frida connectivity +frida-ps -U # List processes on USB-connected device +``` + +**For non-jailbroken devices (authorized testing):** +```bash +# Patch IPA with Frida gadget +objection patchipa --source target.ipa --codesign-signature "Apple Development: test@example.com" + +# Install patched IPA +ideviceinstaller -i target-patched.ipa +``` + +### Step 2: Attach Objection to Target App + +```bash +# Attach to running app by bundle ID +objection --gadget "com.target.app" explore + +# Or spawn the app fresh +objection --gadget "com.target.app" explore --startup-command "ios hooking list classes" +``` + +Once attached, Objection provides an interactive REPL for runtime exploration. + +### Step 3: Assess Data Storage Security (MASVS-STORAGE) + +```bash +# Dump iOS Keychain items accessible to the app +ios keychain dump + +# List files in app sandbox +ios plist cat Info.plist +env # Show app environment paths + +# Inspect NSUserDefaults for sensitive data +ios nsuserdefaults get + +# List SQLite databases +sqlite connect app_data.db +sqlite execute query "SELECT * FROM credentials" + +# Check for sensitive data in pasteboard +ios pasteboard monitor +``` + +### Step 4: Evaluate Network Security (MASVS-NETWORK) + +```bash +# Disable SSL/TLS certificate pinning +ios sslpinning disable + +# Verify pinning is bypassed by observing traffic in Burp Suite proxy +# Monitor network-related class method calls +ios hooking watch class NSURLSession +ios hooking watch class NSURLConnection +``` + +### Step 5: Inspect Authentication and Authorization (MASVS-AUTH) + +```bash +# List all Objective-C classes +ios hooking list classes + +# Search for authentication-related classes +ios hooking search classes Auth +ios hooking search classes Login +ios hooking search classes Token + +# Hook authentication methods to observe parameters +ios hooking watch method "+[AuthManager validateToken:]" --dump-args --dump-return + +# Monitor biometric authentication calls +ios hooking watch class LAContext +``` + +### Step 6: Assess Binary Protections (MASVS-RESILIENCE) + +```bash +# Check jailbreak detection implementation +ios jailbreak disable + +# Simulate jailbreak detection bypass +ios jailbreak simulate + +# List loaded frameworks and libraries +memory list modules + +# Search memory for sensitive strings +memory search "password" --string +memory search "api_key" --string +memory search "Bearer" --string + +# Dump specific memory regions +memory dump all dump_output/ +``` + +### Step 7: Review Platform Interaction (MASVS-PLATFORM) + +```bash +# List URL schemes registered by the app +ios info binary +ios bundles list_frameworks + +# Hook URL scheme handlers +ios hooking watch method "-[AppDelegate application:openURL:options:]" --dump-args + +# Monitor clipboard access +ios pasteboard monitor + +# Check for custom keyboard restrictions +ios hooking search classes UITextField +``` + +## Key Concepts + +| Term | Definition | +|------|-----------| +| **Objection** | Runtime mobile exploration toolkit built on Frida that provides pre-built scripts for common security testing tasks | +| **Frida Gadget** | Shared library injected into app process to enable Frida instrumentation without jailbreak | +| **Keychain** | iOS secure credential storage system; Objection can dump items accessible to the target app's keychain access group | +| **SSL Pinning Bypass** | Runtime modification of certificate validation logic to allow proxy interception of HTTPS traffic | +| **Method Hooking** | Intercepting Objective-C/Swift method calls at runtime to observe arguments, return values, and modify behavior | + +## Tools & Systems + +- **Objection**: High-level Frida-powered mobile security exploration toolkit with pre-built commands +- **Frida**: Dynamic instrumentation framework providing JavaScript injection into native app processes +- **Frida-tools**: CLI utilities for Frida including frida-ps, frida-trace, and frida-discover +- **ideviceinstaller**: Cross-platform tool for installing/managing iOS apps via USB +- **Burp Suite**: HTTP proxy for intercepting traffic after SSL pinning bypass + +## Common Pitfalls + +- **App crashes on attach**: Some apps implement Frida detection. Use `--startup-command` to hook anti-Frida checks early in the app lifecycle. +- **Keychain access scope**: Objection can only dump keychain items within the app's access group. System keychain items require separate jailbreak-level tools. +- **Swift name mangling**: Swift method names are mangled in the runtime. Use `ios hooking list classes` with grep to find demangled names. +- **Non-persistent changes**: All Objection modifications are runtime-only and reset on app restart. Document findings immediately. diff --git a/personas/_shared/skills/analyzing-ios-app-security-with-objection/assets/template.md b/personas/_shared/skills/analyzing-ios-app-security-with-objection/assets/template.md new file mode 100644 index 0000000..5dcfbe7 --- /dev/null +++ b/personas/_shared/skills/analyzing-ios-app-security-with-objection/assets/template.md @@ -0,0 +1,80 @@ +# iOS Objection Security Assessment Report + +## Engagement Information + +| Field | Value | +|-------|-------| +| Application | [APP_NAME] | +| Bundle ID | [BUNDLE_ID] | +| iOS Version | [IOS_VERSION] | +| Device | [DEVICE_MODEL] | +| Device State | [Jailbroken/Non-Jailbroken] | +| Assessment Date | [DATE] | +| Analyst | [ANALYST] | +| Objection Version | [VERSION] | + +## Executive Summary + +[Brief narrative of findings from Objection runtime analysis] + +## Keychain Analysis + +| Service | Account | Data Type | Protection Class | Risk | +|---------|---------|-----------|-----------------|------| +| [SERVICE] | [ACCOUNT] | [TYPE] | [CLASS] | [RISK] | + +**Findings**: [Description of sensitive data found in keychain] + +## Data Storage Assessment + +### NSUserDefaults +| Key | Contains Sensitive Data | Risk | +|-----|----------------------|------| +| [KEY] | [YES/NO] | [RISK] | + +### SQLite Databases +| Database | Encrypted | Sensitive Tables | Risk | +|----------|-----------|-----------------|------| +| [DB_NAME] | [YES/NO] | [TABLES] | [RISK] | + +### Filesystem +| Path | Contents | Protection | Risk | +|------|----------|-----------|------| +| [PATH] | [DESCRIPTION] | [ATTRIBUTE] | [RISK] | + +## Network Security + +| Check | Result | Details | +|-------|--------|---------| +| SSL Pinning Present | [YES/NO] | [IMPLEMENTATION_DETAILS] | +| SSL Pinning Bypass | [SUCCESS/FAIL] | [METHOD_USED] | +| ATS Configuration | [STRICT/RELAXED] | [EXCEPTIONS] | + +## Binary Protection Assessment + +| Protection | Status | Details | +|-----------|--------|---------| +| Jailbreak Detection | [Present/Absent] | [BYPASS_DIFFICULTY] | +| Frida Detection | [Present/Absent] | [DETAILS] | +| Debug Detection | [Present/Absent] | [DETAILS] | +| Code Obfuscation | [Yes/No] | [DETAILS] | + +## Memory Analysis + +| Search Pattern | Found | Risk | Details | +|---------------|-------|------|---------| +| Passwords | [YES/NO] | [RISK] | [DETAILS] | +| Auth Tokens | [YES/NO] | [RISK] | [DETAILS] | +| API Keys | [YES/NO] | [RISK] | [DETAILS] | +| JWTs | [YES/NO] | [RISK] | [DETAILS] | + +## Recommendations + +### Critical +1. [RECOMMENDATION] + +### High +1. [RECOMMENDATION] + +### Medium +1. [RECOMMENDATION] diff --git a/personas/_shared/skills/analyzing-ios-app-security-with-objection/references/api-reference.md b/personas/_shared/skills/analyzing-ios-app-security-with-objection/references/api-reference.md new file mode 100644 index 0000000..d77e4fb --- /dev/null +++ b/personas/_shared/skills/analyzing-ios-app-security-with-objection/references/api-reference.md @@ -0,0 +1,105 @@ +# API Reference: iOS App Security with Objection + +## Objection CLI + +### Launch +```bash +objection -g com.example.app explore # Attach to running app +objection -g com.example.app explore -s "command" # Run startup command +objection patchipa --source app.ipa # Patch IPA with Frida gadget +``` + +### Keychain & Data Storage +```bash +ios keychain dump # Dump keychain items +ios keychain dump --json # JSON output +ios cookies get # List HTTP cookies +ios nsuserdefaults get # Read NSUserDefaults +ios plist cat Info.plist # Read plist file +``` + +### SSL Pinning +```bash +ios sslpinning disable # Bypass SSL pinning +ios sslpinning disable --quiet # Quiet mode +``` + +### Jailbreak Detection +```bash +ios jailbreak disable # Bypass jailbreak detection +ios jailbreak simulate # Simulate jailbroken device +``` + +### Hooking +```bash +ios hooking list classes # List all classes +ios hooking list classes --include Auth # Filter classes +ios hooking list class_methods ClassName # List methods +ios hooking watch method "-[Class method]" # Watch method calls +ios hooking set return_value "-[Class isJB]" false # Override return +``` + +### Filesystem +```bash +ls / # List app sandbox root +ls /Documents # List Documents directory +file download /path/to/file local.out # Download file +file upload local.file /remote/path # Upload file +``` + +### Memory +```bash +memory dump all dump.bin # Dump all memory +memory search "password" # Search memory for string +memory list modules # List loaded modules +memory list exports libModule.dylib # List module exports +``` + +## Frida CLI + +### Syntax +```bash +frida -U -n AppName # Attach by name +frida -U -f com.app.id # Spawn and attach +frida -U -n AppName -l script.js # Load script +frida-ps -U # List running processes +frida-ls-devices # List connected devices +``` + +### Common Frida Scripts +```javascript +// Hook method and log arguments +ObjC.choose(ObjC.classes.ClassName, { + onMatch: function(instance) { + Interceptor.attach(instance['- methodName:'].implementation, { + onEnter: function(args) { + console.log('arg1:', ObjC.Object(args[2])); + } + }); + }, onComplete: function() {} +}); +``` + +## OWASP Mobile Top 10 (2024) + +| ID | Category | Objection Check | +|----|----------|-----------------| +| M1 | Improper Credential Usage | `ios keychain dump` | +| M2 | Inadequate Supply Chain Security | Binary analysis | +| M3 | Insecure Authentication | Hook auth classes | +| M4 | Insufficient Input/Output Validation | Hook input methods | +| M5 | Insecure Communication | `ios sslpinning disable` | +| M6 | Inadequate Privacy Controls | `ios nsuserdefaults get` | +| M7 | Insufficient Binary Protections | Check PIE, ARC, stack canary | +| M8 | Security Misconfiguration | `ios plist cat Info.plist` | +| M9 | Insecure Data Storage | Filesystem + keychain review | +| M10 | Insufficient Cryptography | Hook crypto classes | + +## iOS App Sandbox Paths +| Path | Contents | +|------|----------| +| `/Documents` | User-generated data | +| `/Library/Caches` | Cached data | +| `/Library/Preferences` | Plist settings | +| `/tmp` | Temporary files | +| `/Library/Cookies` | Cookie storage | diff --git a/personas/_shared/skills/analyzing-ios-app-security-with-objection/references/standards.md b/personas/_shared/skills/analyzing-ios-app-security-with-objection/references/standards.md new file mode 100644 index 0000000..f8ee5a4 --- /dev/null +++ b/personas/_shared/skills/analyzing-ios-app-security-with-objection/references/standards.md @@ -0,0 +1,43 @@ +# Standards Reference: iOS App Security with Objection + +## OWASP Mobile Top 10 2024 Mapping + +| OWASP ID | Risk | Objection Testing Coverage | +|----------|------|---------------------------| +| M1 | Improper Credential Usage | Keychain dumping, memory string search for hardcoded credentials | +| M3 | Insecure Authentication/Authorization | Hook authentication methods, bypass biometric checks | +| M5 | Insecure Communication | SSL pinning bypass, network class hooking | +| M7 | Insufficient Binary Protections | Jailbreak detection bypass, Frida detection assessment | +| M8 | Security Misconfiguration | Info.plist review, URL scheme analysis, ATS configuration | +| M9 | Insecure Data Storage | NSUserDefaults inspection, SQLite database access, file system review | + +## OWASP MASVS v2.0 Control Mapping + +| MASVS Category | Objection Commands | Assessment Area | +|----------------|-------------------|-----------------| +| MASVS-STORAGE | `ios keychain dump`, `ios nsuserdefaults get`, `sqlite connect` | Sensitive data in keychain, NSUserDefaults, databases | +| MASVS-CRYPTO | `memory search`, hook crypto framework calls | Key storage, algorithm selection | +| MASVS-AUTH | Hook LAContext, authentication classes | Biometric bypass, session management | +| MASVS-NETWORK | `ios sslpinning disable`, hook NSURLSession | Certificate pinning, cleartext traffic | +| MASVS-PLATFORM | Hook URL scheme handlers, pasteboard monitor | Deep link security, clipboard exposure | +| MASVS-CODE | `memory list modules`, binary inspection | Debugging symbols, framework analysis | +| MASVS-RESILIENCE | `ios jailbreak disable`, Frida detection hooks | Anti-tampering, anti-debugging | + +## OWASP MASTG Test Cases + +| Test ID | Description | Objection Approach | +|---------|-------------|-------------------| +| MASTG-TEST-0053 | Testing Local Storage for Sensitive Data | `ios keychain dump`, filesystem inspection | +| MASTG-TEST-0057 | Testing Backups for Sensitive Data | Check backup exclusion attributes | +| MASTG-TEST-0060 | Testing Custom URL Schemes | Hook `application:openURL:options:` | +| MASTG-TEST-0063 | Testing for Sensitive Data in Logs | Monitor NSLog calls via hooking | +| MASTG-TEST-0066 | Testing Enforced App Transport Security | Inspect Info.plist ATS configuration | + +## Apple Platform Security Requirements + +| Requirement | Assessment Method | +|-------------|-------------------| +| Keychain Access Control | Verify kSecAttrAccessible values via keychain dump | +| App Transport Security | Check Info.plist for NSAllowsArbitraryLoads exceptions | +| Data Protection API | Verify file protection attributes on sensitive files | +| Secure Enclave Usage | Hook SecKey operations for biometric-protected keys | diff --git a/personas/_shared/skills/analyzing-ios-app-security-with-objection/references/workflows.md b/personas/_shared/skills/analyzing-ios-app-security-with-objection/references/workflows.md new file mode 100644 index 0000000..aeae0a4 --- /dev/null +++ b/personas/_shared/skills/analyzing-ios-app-security-with-objection/references/workflows.md @@ -0,0 +1,83 @@ +# Workflows: iOS App Security with Objection + +## Workflow 1: iOS Runtime Security Assessment + +``` +[Setup Environment] --> [Prepare Device] --> [Attach Objection] --> [Runtime Analysis] + | | | | + v v v v +[Install Frida] [Jailbroken: Start [Connect via USB] [Data Storage Check] +[Install Objection] frida-server] [Spawn target app] [Network Security] + [Non-JB: Patch IPA] [Auth Mechanism Review] + [Binary Protection Test] + | + v + [Document Findings] + [Generate Report] +``` + +## Workflow 2: SSL Pinning Bypass for Traffic Interception + +``` +[Configure Burp Proxy] --> [Set device proxy] --> [Attach Objection] + | + v + [ios sslpinning disable] + | + v + [Navigate app in browser/UI] + | + v + [Capture HTTPS traffic in Burp] + [Analyze API endpoints] + [Test authentication flows] + [Check for sensitive data in transit] +``` + +## Workflow 3: Keychain and Data Storage Assessment + +``` +[Attach Objection] --> [ios keychain dump] --> [Analyze keychain items] + | | + v v + [ios nsuserdefaults get] [Check protection classes] + | [Identify sensitive tokens] + v [Verify encryption at rest] + [List app sandbox files] + | + v + [sqlite connect *.db] + [Query sensitive tables] + | + v + [memory search "password"] + [memory search "token"] + [memory search "secret"] +``` + +## Workflow 4: Jailbreak Detection Assessment + +``` +[Attach Objection] --> [ios jailbreak disable] --> [Navigate app] + | | + v [App functions normally?] + [Hook detection methods] / \ + [Monitor file checks] [Yes] [No] + [Monitor Cydia URL scheme] | | + | [Detection [Additional detection + v bypassed] methods exist] + [Document detection | + methods found] [Hook deeper: search + [Assess bypass for custom checks] + difficulty] [Frida script for + targeted bypass] +``` + +## Decision Matrix: Testing Approach + +| Device State | IPA Access | Approach | +|-------------|-----------|----------| +| Jailbroken | Not needed | Direct Frida server + Objection attach | +| Non-jailbroken | Available | Patch IPA with `objection patchipa` | +| Non-jailbroken | Not available | Request IPA from client or use device management | +| Emulator | N/A | Limited: Frida on Corellium or similar platform | diff --git a/personas/_shared/skills/analyzing-ios-app-security-with-objection/scripts/agent.py b/personas/_shared/skills/analyzing-ios-app-security-with-objection/scripts/agent.py new file mode 100644 index 0000000..e6ef52d --- /dev/null +++ b/personas/_shared/skills/analyzing-ios-app-security-with-objection/scripts/agent.py @@ -0,0 +1,211 @@ +#!/usr/bin/env python3 +"""iOS app security analysis agent using Objection/Frida concepts. + +Performs runtime security assessment of iOS apps including SSL pinning bypass, +keychain dumping, filesystem inspection, and jailbreak detection bypass. +""" + +import subprocess +import json +import sys + + +def run_objection(command, app_id=None, timeout=30): + """Execute an Objection command against a target app.""" + cmd = ["objection"] + if app_id: + cmd.extend(["-g", app_id]) + cmd.extend(["explore", "-c", command]) + try: + result = subprocess.run(cmd, capture_output=True, text=True, timeout=timeout) + return result.stdout, result.returncode + except FileNotFoundError: + return "objection not installed (pip install objection)", 1 + except subprocess.TimeoutExpired: + return "Command timed out", 1 + + +def run_frida(script_code, app_id, timeout=30): + """Execute a Frida script against target app.""" + cmd = ["frida", "-U", "-n", app_id, "-e", script_code] + try: + result = subprocess.run(cmd, capture_output=True, text=True, timeout=timeout) + return result.stdout, result.returncode + except FileNotFoundError: + return "frida not installed (pip install frida-tools)", 1 + except subprocess.TimeoutExpired: + return "Command timed out", 1 + + +def dump_keychain(app_id): + """Dump keychain items accessible by the application.""" + return run_objection("ios keychain dump", app_id) + + +def dump_cookies(app_id): + """Dump HTTP cookies stored by the application.""" + return run_objection("ios cookies get", app_id) + + +def list_classes(app_id, filter_str=None): + """List Objective-C classes loaded in the app.""" + cmd = "ios hooking list classes" + if filter_str: + cmd += f" --include {filter_str}" + return run_objection(cmd, app_id) + + +def check_ssl_pinning(app_id): + """Check and bypass SSL certificate pinning.""" + return run_objection("ios sslpinning disable", app_id) + + +def check_jailbreak_detection(app_id): + """Check for and bypass jailbreak detection.""" + return run_objection("ios jailbreak disable", app_id) + + +def inspect_filesystem(app_id, path="/"): + """Inspect the application's filesystem sandbox.""" + return run_objection(f"ls {path}", app_id) + + +def dump_plist(app_id): + """Dump application plist configuration files.""" + return run_objection("ios plist cat Info.plist", app_id) + + +def check_pasteboard(app_id): + """Check pasteboard/clipboard for sensitive data.""" + return run_objection("ios pasteboard monitor", app_id) + + +def search_binary_strings(app_id, pattern): + """Search for strings in the app binary.""" + return run_objection(f"memory search '{pattern}'", app_id) + + +OWASP_MOBILE_CHECKS = { + "M1_Improper_Platform_Usage": { + "checks": ["ios keychain dump", "ios plist cat Info.plist"], + "description": "Check for misuse of platform security features", + }, + "M2_Insecure_Data_Storage": { + "checks": ["ios keychain dump", "ios cookies get", "ios nsuserdefaults get"], + "description": "Check for sensitive data in insecure storage", + }, + "M3_Insecure_Communication": { + "checks": ["ios sslpinning disable"], + "description": "Test SSL/TLS implementation and certificate pinning", + }, + "M4_Insecure_Authentication": { + "checks": ["ios hooking list classes --include Auth", + "ios hooking list classes --include Login"], + "description": "Analyze authentication mechanisms", + }, + "M5_Insufficient_Cryptography": { + "checks": ["ios hooking list classes --include Crypto", + "ios hooking list classes --include AES"], + "description": "Review cryptographic implementations", + }, + "M8_Code_Tampering": { + "checks": ["ios jailbreak disable"], + "description": "Test runtime integrity and jailbreak detection", + }, + "M9_Reverse_Engineering": { + "checks": ["ios hooking list classes"], + "description": "Assess reverse engineering protections", + }, +} + + +def run_owasp_assessment(app_id): + """Run OWASP Mobile Top 10 security checks.""" + results = {} + for category, config in OWASP_MOBILE_CHECKS.items(): + category_results = {"description": config["description"], "findings": []} + for check in config["checks"]: + output, rc = run_objection(check, app_id) + category_results["findings"].append({ + "command": check, + "status": "success" if rc == 0 else "failed", + "output_preview": output[:200] if output else "", + }) + results[category] = category_results + return results + + +FRIDA_SCRIPTS = { + "ssl_pinning_bypass": """ +ObjC.choose(ObjC.classes.NSURLSessionConfiguration, { + onMatch: function(instance) { + instance['- setTLSMinimumSupportedProtocol:'](0); + }, onComplete: function() {} +}); +""", + "jailbreak_bypass": """ +var paths = ['/Applications/Cydia.app', '/usr/sbin/sshd', '/etc/apt']; +Interceptor.attach(ObjC.classes.NSFileManager['- fileExistsAtPath:'].implementation, { + onEnter: function(args) { this.path = ObjC.Object(args[2]).toString(); }, + onLeave: function(retval) { + if (paths.some(p => this.path.includes(p))) retval.replace(0); + } +}); +""", + "keychain_dump": """ +var kSecClass = ObjC.classes.__NSDictionary.dictionaryWithObject_forKey_( + ObjC.classes.__NSCFConstantString.alloc().initWithUTF8String_('genp'), + ObjC.classes.__NSCFConstantString.alloc().initWithUTF8String_('class') +); +console.log('Keychain query prepared'); +""", +} + + +def generate_report(app_id, assessment_results): + """Generate iOS security assessment report.""" + findings_count = sum( + len(cat["findings"]) for cat in assessment_results.values() + ) + return { + "app_identifier": app_id, + "assessment_framework": "OWASP Mobile Top 10", + "categories_tested": len(assessment_results), + "total_checks": findings_count, + "results": assessment_results, + } + + +if __name__ == "__main__": + print("=" * 60) + print("iOS App Security Analysis Agent (Objection/Frida)") + print("Runtime analysis, SSL bypass, keychain dump, OWASP checks") + print("=" * 60) + + app_id = sys.argv[1] if len(sys.argv) > 1 else None + + if not app_id: + print("\n[DEMO] Usage: python agent.py ") + print(" e.g. python agent.py com.example.app") + print("\nAvailable checks:") + for category, config in OWASP_MOBILE_CHECKS.items(): + print(f" {category}: {config['description']}") + print("\nFrida scripts available:") + for name in FRIDA_SCRIPTS: + print(f" {name}") + sys.exit(0) + + print(f"\n[*] Target: {app_id}") + print("[*] Running OWASP Mobile Top 10 assessment...") + + results = run_owasp_assessment(app_id) + report = generate_report(app_id, results) + + for category, data in results.items(): + status_counts = {"success": 0, "failed": 0} + for f in data["findings"]: + status_counts[f["status"]] += 1 + print(f"\n [{category}] {data['description']}") + print(f" Checks: {status_counts['success']} passed, {status_counts['failed']} failed") + + print(f"\n{json.dumps(report, indent=2, default=str)}") diff --git a/personas/_shared/skills/analyzing-ios-app-security-with-objection/scripts/process.py b/personas/_shared/skills/analyzing-ios-app-security-with-objection/scripts/process.py new file mode 100644 index 0000000..e2362fe --- /dev/null +++ b/personas/_shared/skills/analyzing-ios-app-security-with-objection/scripts/process.py @@ -0,0 +1,294 @@ +#!/usr/bin/env python3 +""" +Objection iOS Security Assessment Automation + +Automates common Objection commands for iOS app security testing. +Runs keychain dump, storage inspection, SSL pinning check, and jailbreak detection analysis. + +Usage: + python process.py --bundle-id com.target.app [--device-id UDID] [--output report.json] +""" + +import argparse +import json +import subprocess +import sys +import re +from datetime import datetime +from pathlib import Path + + +class ObjectionAssessor: + """Automates Objection-based iOS security assessment tasks.""" + + def __init__(self, bundle_id: str, device_id: str = None): + self.bundle_id = bundle_id + self.device_id = device_id + self.findings = [] + + def _run_objection_command(self, command: str, timeout: int = 30) -> str: + """Execute an Objection command and return output.""" + cmd = ["objection", "--gadget", self.bundle_id, "run", command] + if self.device_id: + cmd.insert(1, "--serial") + cmd.insert(2, self.device_id) + + try: + result = subprocess.run( + cmd, + capture_output=True, + text=True, + timeout=timeout, + ) + return result.stdout + result.stderr + except subprocess.TimeoutExpired: + return f"TIMEOUT: Command '{command}' exceeded {timeout}s" + except FileNotFoundError: + return "ERROR: Objection not found. Install with: pip install objection" + + def _run_frida_command(self, script: str, timeout: int = 15) -> str: + """Execute a Frida script snippet.""" + cmd = ["frida", "-U", "-n", self.bundle_id, "-e", script] + if self.device_id: + cmd.extend(["-D", self.device_id]) + + try: + result = subprocess.run( + cmd, + capture_output=True, + text=True, + timeout=timeout, + ) + return result.stdout + except (subprocess.TimeoutExpired, FileNotFoundError): + return "" + + def check_frida_connectivity(self) -> dict: + """Verify Frida can connect to the device.""" + cmd = ["frida-ps", "-U"] + if self.device_id: + cmd.extend(["-D", self.device_id]) + + try: + result = subprocess.run(cmd, capture_output=True, text=True, timeout=10) + connected = result.returncode == 0 + processes = len(result.stdout.strip().split("\n")) - 1 if connected else 0 + return { + "connected": connected, + "process_count": processes, + "target_running": self.bundle_id in result.stdout, + } + except (subprocess.TimeoutExpired, FileNotFoundError): + return {"connected": False, "process_count": 0, "target_running": False} + + def dump_keychain(self) -> dict: + """Dump keychain items accessible to the app.""" + output = self._run_objection_command("ios keychain dump") + items = [] + current_item = {} + + for line in output.split("\n"): + line = line.strip() + if "Service" in line and ":" in line: + if current_item: + items.append(current_item) + current_item = {"service": line.split(":", 1)[-1].strip()} + elif "Account" in line and ":" in line: + current_item["account"] = line.split(":", 1)[-1].strip() + elif "Data" in line and ":" in line: + data = line.split(":", 1)[-1].strip() + current_item["data_preview"] = data[:50] + "..." if len(data) > 50 else data + current_item["data_length"] = len(data) + + if current_item: + items.append(current_item) + + finding = { + "check": "keychain_dump", + "category": "MASVS-STORAGE", + "owasp_mobile": "M9", + "items_found": len(items), + "items": items[:20], + "severity": "HIGH" if items else "INFO", + "description": f"Found {len(items)} keychain items accessible to the application", + } + self.findings.append(finding) + return finding + + def check_nsuserdefaults(self) -> dict: + """Inspect NSUserDefaults for sensitive data.""" + output = self._run_objection_command("ios nsuserdefaults get") + sensitive_patterns = [ + "password", "token", "secret", "key", "auth", + "session", "credential", "api_key", "apikey", + ] + + sensitive_entries = [] + for line in output.split("\n"): + line_lower = line.lower() + for pattern in sensitive_patterns: + if pattern in line_lower: + sensitive_entries.append(line.strip()) + break + + finding = { + "check": "nsuserdefaults", + "category": "MASVS-STORAGE", + "owasp_mobile": "M9", + "sensitive_entries": len(sensitive_entries), + "entries": sensitive_entries[:10], + "severity": "HIGH" if sensitive_entries else "PASS", + "description": f"Found {len(sensitive_entries)} potentially sensitive NSUserDefaults entries", + } + self.findings.append(finding) + return finding + + def check_ssl_pinning(self) -> dict: + """Assess SSL pinning implementation.""" + output = self._run_objection_command("ios sslpinning disable") + pinning_detected = "pinning" in output.lower() or "hook" in output.lower() + + finding = { + "check": "ssl_pinning", + "category": "MASVS-NETWORK", + "owasp_mobile": "M5", + "pinning_detected": pinning_detected, + "bypass_output": output[:500], + "severity": "MEDIUM" if not pinning_detected else "INFO", + "description": "SSL pinning " + ("detected and bypassed" if pinning_detected else "not detected"), + } + self.findings.append(finding) + return finding + + def check_jailbreak_detection(self) -> dict: + """Assess jailbreak detection implementation.""" + output = self._run_objection_command("ios jailbreak disable") + detection_found = "hook" in output.lower() or "bypass" in output.lower() + + finding = { + "check": "jailbreak_detection", + "category": "MASVS-RESILIENCE", + "owasp_mobile": "M7", + "detection_implemented": detection_found, + "bypass_output": output[:500], + "severity": "MEDIUM" if not detection_found else "INFO", + "description": "Jailbreak detection " + ("found" if detection_found else "not found or not implemented"), + } + self.findings.append(finding) + return finding + + def search_sensitive_memory(self) -> dict: + """Search app memory for sensitive strings.""" + patterns = ["password", "Bearer ", "eyJ", "api_key", "secret"] + memory_findings = [] + + for pattern in patterns: + output = self._run_objection_command(f'memory search "{pattern}" --string') + matches = output.count("Found") + if matches > 0: + memory_findings.append({ + "pattern": pattern, + "matches": matches, + }) + + finding = { + "check": "memory_search", + "category": "MASVS-STORAGE", + "owasp_mobile": "M9", + "patterns_with_matches": len(memory_findings), + "details": memory_findings, + "severity": "HIGH" if memory_findings else "PASS", + "description": f"Found sensitive patterns in memory for {len(memory_findings)} search terms", + } + self.findings.append(finding) + return finding + + def get_app_info(self) -> dict: + """Gather basic app information.""" + output = self._run_objection_command("ios info binary") + env_output = self._run_objection_command("env") + + return { + "bundle_id": self.bundle_id, + "binary_info": output[:1000], + "environment": env_output[:1000], + } + + def generate_report(self) -> dict: + """Generate consolidated assessment report.""" + severity_counts = {"HIGH": 0, "MEDIUM": 0, "LOW": 0, "INFO": 0, "PASS": 0} + for f in self.findings: + sev = f.get("severity", "INFO") + severity_counts[sev] = severity_counts.get(sev, 0) + 1 + + return { + "assessment": { + "target": self.bundle_id, + "date": datetime.now().isoformat(), + "tool": "Objection (Frida-powered)", + "type": "iOS Runtime Security Assessment", + }, + "summary": { + "total_checks": len(self.findings), + "severity_breakdown": severity_counts, + "critical_findings": [ + f for f in self.findings if f.get("severity") in ("HIGH", "CRITICAL") + ], + }, + "findings": self.findings, + } + + +def main(): + parser = argparse.ArgumentParser( + description="Objection iOS Security Assessment Automation" + ) + parser.add_argument("--bundle-id", required=True, help="iOS app bundle identifier") + parser.add_argument("--device-id", help="Device UDID for targeting specific device") + parser.add_argument("--output", default="objection_report.json", help="Output report path") + parser.add_argument("--checks", nargs="+", + default=["keychain", "nsuserdefaults", "ssl", "jailbreak", "memory"], + help="Checks to run") + args = parser.parse_args() + + assessor = ObjectionAssessor(args.bundle_id, args.device_id) + + # Verify connectivity + connectivity = assessor.check_frida_connectivity() + if not connectivity["connected"]: + print("[-] ERROR: Cannot connect to device via Frida") + print(" Ensure Frida server is running on device or IPA is patched") + sys.exit(1) + + print(f"[+] Connected to device. Target running: {connectivity['target_running']}") + + # Run selected checks + check_map = { + "keychain": assessor.dump_keychain, + "nsuserdefaults": assessor.check_nsuserdefaults, + "ssl": assessor.check_ssl_pinning, + "jailbreak": assessor.check_jailbreak_detection, + "memory": assessor.search_sensitive_memory, + } + + for check in args.checks: + if check in check_map: + print(f"[*] Running check: {check}") + result = check_map[check]() + print(f" Severity: {result['severity']} - {result['description']}") + + # Generate report + report = assessor.generate_report() + + with open(args.output, "w") as f: + json.dump(report, f, indent=2) + print(f"\n[+] Report saved: {args.output}") + + # Summary + high_count = report["summary"]["severity_breakdown"].get("HIGH", 0) + if high_count > 0: + print(f"[!] {high_count} HIGH severity findings require attention") + + +if __name__ == "__main__": + main() diff --git a/personas/_shared/skills/analyzing-kubernetes-audit-logs/LICENSE b/personas/_shared/skills/analyzing-kubernetes-audit-logs/LICENSE new file mode 100644 index 0000000..d885118 --- /dev/null +++ b/personas/_shared/skills/analyzing-kubernetes-audit-logs/LICENSE @@ -0,0 +1,201 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to the Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by the Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding any notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. Please do not remove or change + the license header comment from a contributed file except when + necessary. + + Copyright 2026 mukul975 + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/personas/_shared/skills/analyzing-kubernetes-audit-logs/SKILL.md b/personas/_shared/skills/analyzing-kubernetes-audit-logs/SKILL.md new file mode 100644 index 0000000..c8b26fa --- /dev/null +++ b/personas/_shared/skills/analyzing-kubernetes-audit-logs/SKILL.md @@ -0,0 +1,73 @@ +--- +name: analyzing-kubernetes-audit-logs +description: 'Parses Kubernetes API server audit logs (JSON lines) to detect exec-into-pod, secret access, RBAC modifications, + privileged pod creation, and anonymous API access. Builds threat detection rules from audit event patterns. Use when investigating + Kubernetes cluster compromise or building k8s-specific SIEM detection rules. + + ' +domain: cybersecurity +subdomain: container-security +tags: +- analyzing +- kubernetes +- audit +- logs +version: '1.0' +author: mahipal +license: Apache-2.0 +nist_csf: +- PR.PS-01 +- PR.IR-01 +- ID.AM-08 +- DE.CM-01 +--- + +# Analyzing Kubernetes Audit Logs + + +## When to Use + +- When investigating security incidents that require analyzing kubernetes audit logs +- When building detection rules or threat hunting queries for this domain +- When SOC analysts need structured procedures for this analysis type +- When validating security monitoring coverage for related attack techniques + +## Prerequisites + +- Familiarity with container security concepts and tools +- Access to a test or lab environment for safe execution +- Python 3.8+ with required dependencies installed +- Appropriate authorization for any testing activities + +## Instructions + +Parse Kubernetes audit log files (JSON lines format) to detect security-relevant +events including unauthorized access, privilege escalation, and data exfiltration. + +```python +import json + +with open("/var/log/kubernetes/audit.log") as f: + for line in f: + event = json.loads(line) + verb = event.get("verb") + resource = event.get("objectRef", {}).get("resource") + user = event.get("user", {}).get("username") + if verb == "create" and resource == "pods/exec": + print(f"Pod exec by {user}") +``` + +Key events to detect: +1. pods/exec and pods/attach (shell into containers) +2. secrets access (get/list/watch) +3. clusterrolebindings creation (RBAC escalation) +4. Privileged pod creation +5. Anonymous or system:unauthenticated access + +## Examples + +```python +# Detect secret enumeration +if verb in ("get", "list") and resource == "secrets": + print(f"Secret access: {user} -> {event['objectRef'].get('name')}") +``` diff --git a/personas/_shared/skills/analyzing-kubernetes-audit-logs/references/api-reference.md b/personas/_shared/skills/analyzing-kubernetes-audit-logs/references/api-reference.md new file mode 100644 index 0000000..334701b --- /dev/null +++ b/personas/_shared/skills/analyzing-kubernetes-audit-logs/references/api-reference.md @@ -0,0 +1,57 @@ +# API Reference: Analyzing Kubernetes Audit Logs + +## Audit Log Format (JSON Lines) + +```json +{ + "kind": "Event", + "apiVersion": "audit.k8s.io/v1", + "level": "RequestResponse", + "verb": "create", + "user": {"username": "admin", "groups": ["system:masters"]}, + "sourceIPs": ["10.0.0.5"], + "objectRef": { + "resource": "pods", + "subresource": "exec", + "namespace": "default", + "name": "web-pod" + }, + "responseStatus": {"code": 200}, + "requestReceivedTimestamp": "2025-03-15T14:00:00Z" +} +``` + +## Security-Critical Audit Events + +| Event | objectRef | Severity | +|-------|-----------|----------| +| Pod exec | `resource: pods, subresource: exec` | HIGH | +| Secret access | `resource: secrets, verb: get/list` | HIGH | +| RBAC change | `resource: clusterrolebindings` | CRITICAL | +| Privileged pod | `requestObject.spec.containers[].securityContext.privileged` | CRITICAL | +| Anonymous access | `user.username: system:anonymous` | CRITICAL | + +## Audit Policy Levels + +| Level | Captures | +|-------|----------| +| None | No logging | +| Metadata | Timestamp, user, verb, resource | +| Request | Metadata + request body | +| RequestResponse | Request + response body | + +## Python Parsing + +```python +import json +with open("audit.log") as f: + for line in f: + event = json.loads(line) + print(event["verb"], event["objectRef"]["resource"]) +``` + +### References + +- K8s Auditing: https://kubernetes.io/docs/tasks/debug/debug-cluster/audit/ +- Audit policy: https://kubernetes.io/docs/reference/config-api/apiserver-audit.v1/ +- Datadog k8s audit: https://www.datadoghq.com/blog/monitor-kubernetes-audit-logs/ diff --git a/personas/_shared/skills/analyzing-kubernetes-audit-logs/scripts/agent.py b/personas/_shared/skills/analyzing-kubernetes-audit-logs/scripts/agent.py new file mode 100644 index 0000000..96d3f97 --- /dev/null +++ b/personas/_shared/skills/analyzing-kubernetes-audit-logs/scripts/agent.py @@ -0,0 +1,201 @@ +#!/usr/bin/env python3 +"""Agent for analyzing Kubernetes audit logs for security threats.""" + +import json +import argparse +from collections import defaultdict +from datetime import datetime + + +def parse_audit_log(log_path): + """Parse Kubernetes audit log file (JSON lines format).""" + events = [] + with open(log_path) as f: + for line in f: + line = line.strip() + if not line: + continue + try: + events.append(json.loads(line)) + except json.JSONDecodeError: + continue + return events + + +def detect_pod_exec(events): + """Detect kubectl exec and attach events (shell access to pods).""" + findings = [] + for event in events: + obj_ref = event.get("objectRef", {}) + subresource = obj_ref.get("subresource", "") + if subresource in ("exec", "attach"): + findings.append({ + "timestamp": event.get("requestReceivedTimestamp", ""), + "user": event.get("user", {}).get("username", ""), + "groups": event.get("user", {}).get("groups", []), + "verb": event.get("verb", ""), + "namespace": obj_ref.get("namespace", ""), + "pod": obj_ref.get("name", ""), + "subresource": subresource, + "source_ip": event.get("sourceIPs", [""])[0], + "severity": "HIGH", + }) + return findings + + +def detect_secret_access(events): + """Detect access to Kubernetes secrets.""" + findings = [] + for event in events: + obj_ref = event.get("objectRef", {}) + if obj_ref.get("resource") != "secrets": + continue + verb = event.get("verb", "") + if verb not in ("get", "list", "watch", "create", "update", "delete"): + continue + findings.append({ + "timestamp": event.get("requestReceivedTimestamp", ""), + "user": event.get("user", {}).get("username", ""), + "verb": verb, + "namespace": obj_ref.get("namespace", ""), + "secret_name": obj_ref.get("name", ""), + "source_ip": event.get("sourceIPs", [""])[0], + "severity": "HIGH" if verb in ("list", "delete") else "MEDIUM", + }) + return findings + + +def detect_rbac_changes(events): + """Detect RBAC role and binding modifications.""" + rbac_resources = {"clusterroles", "clusterrolebindings", "roles", "rolebindings"} + findings = [] + for event in events: + obj_ref = event.get("objectRef", {}) + resource = obj_ref.get("resource", "") + verb = event.get("verb", "") + if resource in rbac_resources and verb in ("create", "update", "patch", "delete"): + findings.append({ + "timestamp": event.get("requestReceivedTimestamp", ""), + "user": event.get("user", {}).get("username", ""), + "verb": verb, + "resource": resource, + "name": obj_ref.get("name", ""), + "namespace": obj_ref.get("namespace", ""), + "source_ip": event.get("sourceIPs", [""])[0], + "severity": "CRITICAL" if "cluster" in resource else "HIGH", + }) + return findings + + +def detect_privileged_pods(events): + """Detect creation of privileged pods.""" + findings = [] + for event in events: + if event.get("verb") != "create": + continue + obj_ref = event.get("objectRef", {}) + if obj_ref.get("resource") != "pods": + continue + request_obj = event.get("requestObject", {}) + spec = request_obj.get("spec", {}) + containers = spec.get("containers", []) + for container in containers: + sc = container.get("securityContext", {}) + if sc.get("privileged"): + findings.append({ + "timestamp": event.get("requestReceivedTimestamp", ""), + "user": event.get("user", {}).get("username", ""), + "namespace": obj_ref.get("namespace", ""), + "pod": obj_ref.get("name", ""), + "container": container.get("name", ""), + "severity": "CRITICAL", + }) + return findings + + +def detect_anonymous_access(events): + """Detect API access by anonymous or unauthenticated users.""" + findings = [] + anon_users = {"system:anonymous", "system:unauthenticated"} + for event in events: + user = event.get("user", {}).get("username", "") + groups = event.get("user", {}).get("groups", []) + if user in anon_users or "system:unauthenticated" in groups: + status_code = event.get("responseStatus", {}).get("code", 0) + if status_code < 400: + findings.append({ + "timestamp": event.get("requestReceivedTimestamp", ""), + "user": user, + "verb": event.get("verb", ""), + "resource": event.get("objectRef", {}).get("resource", ""), + "source_ip": event.get("sourceIPs", [""])[0], + "status_code": status_code, + "severity": "CRITICAL", + }) + return findings + + +def detect_forbidden_surge(events, threshold=20): + """Detect 403 surges indicating enumeration or brute force.""" + user_forbidden = defaultdict(int) + for event in events: + if event.get("responseStatus", {}).get("code") == 403: + user = event.get("user", {}).get("username", "") + user_forbidden[user] += 1 + surges = [] + for user, count in user_forbidden.items(): + if count >= threshold: + surges.append({"user": user, "forbidden_count": count, "severity": "MEDIUM"}) + return sorted(surges, key=lambda x: x["forbidden_count"], reverse=True) + + +def main(): + parser = argparse.ArgumentParser(description="Kubernetes Audit Log Analyzer") + parser.add_argument("--audit-log", required=True, help="Path to audit log file") + parser.add_argument("--output", default="k8s_audit_report.json") + parser.add_argument("--action", choices=[ + "exec", "secrets", "rbac", "privileged", "anonymous", "full_analysis" + ], default="full_analysis") + args = parser.parse_args() + + events = parse_audit_log(args.audit_log) + report = {"log_file": args.audit_log, "total_events": len(events), + "generated_at": datetime.utcnow().isoformat(), "findings": {}} + print(f"[+] Parsed {len(events)} audit events") + + if args.action in ("exec", "full_analysis"): + findings = detect_pod_exec(events) + report["findings"]["pod_exec"] = findings + print(f"[+] Pod exec/attach events: {len(findings)}") + + if args.action in ("secrets", "full_analysis"): + findings = detect_secret_access(events) + report["findings"]["secret_access"] = findings + print(f"[+] Secret access events: {len(findings)}") + + if args.action in ("rbac", "full_analysis"): + findings = detect_rbac_changes(events) + report["findings"]["rbac_changes"] = findings + print(f"[+] RBAC changes: {len(findings)}") + + if args.action in ("privileged", "full_analysis"): + findings = detect_privileged_pods(events) + report["findings"]["privileged_pods"] = findings + print(f"[+] Privileged pod creation: {len(findings)}") + + if args.action in ("anonymous", "full_analysis"): + findings = detect_anonymous_access(events) + report["findings"]["anonymous_access"] = findings + print(f"[+] Anonymous access events: {len(findings)}") + + forbidden = detect_forbidden_surge(events) + report["findings"]["forbidden_surges"] = forbidden + print(f"[+] 403 surges: {len(forbidden)}") + + with open(args.output, "w") as f: + json.dump(report, f, indent=2, default=str) + print(f"[+] Report saved to {args.output}") + + +if __name__ == "__main__": + main() diff --git a/personas/_shared/skills/analyzing-linux-audit-logs-for-intrusion/LICENSE b/personas/_shared/skills/analyzing-linux-audit-logs-for-intrusion/LICENSE new file mode 100644 index 0000000..d885118 --- /dev/null +++ b/personas/_shared/skills/analyzing-linux-audit-logs-for-intrusion/LICENSE @@ -0,0 +1,201 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to the Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by the Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding any notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. Please do not remove or change + the license header comment from a contributed file except when + necessary. + + Copyright 2026 mukul975 + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/personas/_shared/skills/analyzing-linux-audit-logs-for-intrusion/SKILL.md b/personas/_shared/skills/analyzing-linux-audit-logs-for-intrusion/SKILL.md new file mode 100644 index 0000000..6fb404c --- /dev/null +++ b/personas/_shared/skills/analyzing-linux-audit-logs-for-intrusion/SKILL.md @@ -0,0 +1,268 @@ +--- +name: analyzing-linux-audit-logs-for-intrusion +description: 'Uses the Linux Audit framework (auditd) with ausearch and aureport utilities to detect intrusion attempts, unauthorized + access, privilege escalation, and suspicious system activity. Covers audit rule configuration, log querying, timeline reconstruction, + and integration with SIEM platforms. Activates for requests involving auditd analysis, Linux audit log investigation, ausearch + queries, aureport summaries, or host-based intrusion detection on Linux. + + ' +domain: cybersecurity +subdomain: incident-response +tags: +- auditd +- ausearch +- aureport +- linux-security +- intrusion-detection +- HIDS +- forensics +version: 1.0.0 +author: mahipal +license: Apache-2.0 +nist_csf: +- RS.MA-01 +- RS.MA-02 +- RS.AN-03 +- RC.RP-01 +--- + +# Analyzing Linux Audit Logs for Intrusion + +## When to Use + +- Investigating suspected unauthorized access or privilege escalation on Linux hosts +- Hunting for evidence of exploitation, backdoor installation, or persistence mechanisms +- Auditing compliance with security baselines (CIS, STIG, PCI-DSS) that require system call monitoring +- Reconstructing a timeline of attacker actions during incident response +- Detecting file tampering on critical system files such as `/etc/passwd`, `/etc/shadow`, or SSH keys + +**Do not use** for network-level intrusion detection; use Suricata or Zeek for network traffic analysis. Auditd operates at the kernel level on individual hosts. + +## Prerequisites + +- Linux system with `auditd` package installed and the audit daemon running (`systemctl status auditd`) +- Root or sudo access to configure audit rules and query logs +- Audit rules deployed via `/etc/audit/rules.d/*.rules` or loaded with `auditctl` +- Recommended: Neo23x0/auditd ruleset from GitHub for comprehensive baseline coverage +- Familiarity with Linux syscalls (`execve`, `open`, `connect`, `ptrace`, etc.) +- Log storage with sufficient retention (default location: `/var/log/audit/audit.log`) + +## Workflow + +### Step 1: Verify Audit Daemon Status and Configuration + +Confirm the audit system is running and check the current rule set: + +```bash +# Check auditd service status +systemctl status auditd + +# Show current audit rules loaded in the kernel +auditctl -l + +# Show audit daemon configuration +cat /etc/audit/auditd.conf | grep -E "log_file|max_log_file|num_logs|space_left_action" + +# Check if the audit backlog is being exceeded (dropped events) +auditctl -s +``` + +If the backlog limit is being reached, increase it: + +```bash +auditctl -b 8192 +``` + +### Step 2: Deploy Intrusion-Focused Audit Rules + +Add rules that target common intrusion indicators. Place these in `/etc/audit/rules.d/intrusion.rules`: + +```bash +# Monitor credential files for unauthorized reads or modifications +-w /etc/passwd -p wa -k credential_access +-w /etc/shadow -p rwa -k credential_access +-w /etc/gshadow -p rwa -k credential_access +-w /etc/sudoers -p wa -k privilege_escalation +-w /etc/sudoers.d/ -p wa -k privilege_escalation + +# Monitor SSH configuration and authorized keys +-w /etc/ssh/sshd_config -p wa -k sshd_config_change +-w /root/.ssh/authorized_keys -p wa -k ssh_key_tampering + +# Monitor user and group management commands +-w /usr/sbin/useradd -p x -k user_management +-w /usr/sbin/usermod -p x -k user_management +-w /usr/sbin/groupadd -p x -k user_management + +# Detect process injection via ptrace +-a always,exit -F arch=b64 -S ptrace -F a0=0x4 -k process_injection +-a always,exit -F arch=b64 -S ptrace -F a0=0x5 -k process_injection +-a always,exit -F arch=b64 -S ptrace -F a0=0x6 -k process_injection + +# Monitor execution of programs from unusual directories +-a always,exit -F arch=b64 -S execve -F exe=/tmp -k exec_from_tmp +-a always,exit -F arch=b64 -S execve -F exe=/dev/shm -k exec_from_shm + +# Detect kernel module loading (rootkit installation) +-a always,exit -F arch=b64 -S init_module -S finit_module -k kernel_module_load +-a always,exit -F arch=b64 -S delete_module -k kernel_module_remove +-w /sbin/insmod -p x -k kernel_module_tool +-w /sbin/modprobe -p x -k kernel_module_tool + +# Monitor network socket creation for reverse shells +-a always,exit -F arch=b64 -S socket -F a0=2 -k network_socket_created +-a always,exit -F arch=b64 -S connect -F a0=2 -k network_connection + +# Detect cron job modifications (persistence) +-w /etc/crontab -p wa -k cron_persistence +-w /etc/cron.d/ -p wa -k cron_persistence +-w /var/spool/cron/ -p wa -k cron_persistence + +# Monitor log deletion or tampering +-w /var/log/ -p wa -k log_tampering +``` + +Reload rules after editing: + +```bash +augenrules --load +auditctl -l | wc -l # Confirm rule count +``` + +### Step 3: Search for Intrusion Indicators with ausearch + +Use `ausearch` to query the audit log for specific events: + +```bash +# Search for all failed login attempts in the last 24 hours +ausearch -m USER_LOGIN --success no -ts recent + +# Search for commands executed by a specific user +ausearch -ua 1001 -m EXECVE -ts today + +# Search for all file access events on /etc/shadow +ausearch -f /etc/shadow -ts this-week + +# Search for privilege escalation via sudo +ausearch -m USER_CMD -ts today + +# Search for kernel module loading events +ausearch -k kernel_module_load -ts this-month + +# Search for processes executed from /tmp (common attack staging) +ausearch -k exec_from_tmp -ts this-week + +# Search for SSH key modifications +ausearch -k ssh_key_tampering -ts this-month + +# Search for a specific event by audit event ID +ausearch -a 12345 + +# Search events in a specific time range +ausearch -ts 03/15/2026 08:00:00 -te 03/15/2026 18:00:00 + +# Interpret syscall numbers and format output readably +ausearch -k credential_access -i -ts today +``` + +### Step 4: Generate Summary Reports with aureport + +Use `aureport` to produce aggregate summaries for triage: + +```bash +# Summary of all authentication events +aureport -au -ts this-week --summary + +# Report of all failed events (login, access, etc.) +aureport --failed --summary -ts today + +# Report of executable runs +aureport -x --summary -ts today + +# Report of all anomaly events (segfaults, promiscuous mode, etc.) +aureport --anomaly -ts this-week + +# Report of file access events +aureport -f --summary -ts today + +# Report of all events by key (maps to your custom rule keys) +aureport -k --summary -ts this-month + +# Report of all system calls +aureport -s --summary -ts today + +# Report of events grouped by user +aureport -u --summary -ts this-week + +# Detailed time-based event report for timeline building +aureport -ts 03/15/2026 08:00:00 -te 03/15/2026 18:00:00 --summary +``` + +### Step 5: Reconstruct the Attack Timeline + +Combine ausearch queries to build a chronological narrative: + +```bash +# Step 5a: Identify the initial access timestamp +ausearch -m USER_LOGIN -ua 0 --success yes -ts this-week -i | head -50 + +# Step 5b: Trace what the attacker did after gaining access +# Get all events from the compromised account within the incident window +ausearch -ua -ts "03/15/2026 14:00:00" -te "03/15/2026 18:00:00" -i \ + | aureport -f -i + +# Step 5c: Extract all commands executed during the incident window +ausearch -m EXECVE -ts "03/15/2026 14:00:00" -te "03/15/2026 18:00:00" -i + +# Step 5d: Check for persistence mechanisms installed +ausearch -k cron_persistence -ts "03/15/2026 14:00:00" -i +ausearch -k ssh_key_tampering -ts "03/15/2026 14:00:00" -i + +# Step 5e: Check for lateral movement (outbound connections) +ausearch -k network_connection -ts "03/15/2026 14:00:00" -i +``` + +### Step 6: Forward Audit Logs to SIEM + +Configure `audisp-remote` or `auditbeat` to ship logs to a central SIEM for correlation: + +```bash +# Option A: Using audisp-remote plugin +# Edit /etc/audit/plugins.d/au-remote.conf +active = yes +direction = out +path = /sbin/audisp-remote +type = always + +# Configure remote target in /etc/audit/audisp-remote.conf +remote_server = siem.internal.corp +port = 6514 +transport = tcp + +# Option B: Using Elastic Auditbeat +# Install auditbeat and configure /etc/auditbeat/auditbeat.yml +# Auditbeat reads directly from the kernel audit framework +``` + +## Key Concepts + +| Term | Definition | +|------|------------| +| **auditd** | The Linux Audit daemon that receives audit events from the kernel and writes them to `/var/log/audit/audit.log` | +| **auditctl** | Command-line utility to control the audit system: add/remove rules, check status, set backlog size | +| **ausearch** | Query tool that searches audit logs by message type, user, file, key, time range, or event ID | +| **aureport** | Reporting tool that generates aggregate summaries of audit events for triage and compliance | +| **audit rule key (-k)** | A user-defined label attached to an audit rule, enabling fast filtering of related events with ausearch and aureport | +| **syscall auditing** | Kernel-level monitoring of system calls (execve, open, connect, ptrace) that captures process and file activity | +| **augenrules** | Utility that merges all files in `/etc/audit/rules.d/` into `/etc/audit/audit.rules` and loads them into the kernel | + +## Verification + +- [ ] auditd is running and rules are loaded (`auditctl -l` returns expected rule count) +- [ ] No audit backlog overflow (`auditctl -s` shows `backlog: 0` or low value, lost: 0) +- [ ] ausearch returns events for each custom key (`ausearch -k -ts today` returns results) +- [ ] aureport generates non-empty summaries for authentication, executable, and file events +- [ ] Timeline reconstruction produces a coherent chronological sequence of attacker actions +- [ ] Critical file watches trigger alerts on test modifications (`touch /etc/shadow` generates an event) +- [ ] Logs are forwarding to central SIEM (verify with a test event and confirm receipt) +- [ ] Audit rules persist across reboot (rules in `/etc/audit/rules.d/`, not only via `auditctl`) diff --git a/personas/_shared/skills/analyzing-linux-audit-logs-for-intrusion/references/api-reference.md b/personas/_shared/skills/analyzing-linux-audit-logs-for-intrusion/references/api-reference.md new file mode 100644 index 0000000..ea9f41a --- /dev/null +++ b/personas/_shared/skills/analyzing-linux-audit-logs-for-intrusion/references/api-reference.md @@ -0,0 +1,89 @@ +# API Reference: Analyzing Linux Audit Logs for Intrusion + +## Audit Log Location +``` +/var/log/audit/audit.log +``` + +## ausearch CLI +```bash +# Search by key +ausearch -k file_access + +# Search by message type +ausearch -m EXECVE + +# Failed events only +ausearch --success no + +# By user +ausearch -ua 1000 + +# CSV output for Python processing +ausearch --format csv > audit_events.csv + +# By time range +ausearch --start today --end now +ausearch --start 01/15/2025 00:00:00 --end 01/16/2025 00:00:00 +``` + +## aureport CLI +```bash +# Summary report +aureport --summary + +# Authentication report +aureport -au + +# Failed events +aureport --failed + +# Executable report +aureport -x + +# File access report +aureport -f + +# Anomaly report +aureport --anomaly +``` + +## Audit Rules (auditctl) +```bash +# Monitor sensitive files +auditctl -w /etc/passwd -p rwxa -k passwd_access +auditctl -w /etc/shadow -p rwxa -k shadow_access +auditctl -w /etc/sudoers -p rwxa -k sudoers_access + +# Monitor privilege escalation +auditctl -a always,exit -F arch=b64 -S execve -F euid=0 -F uid!=0 -k priv_esc + +# Monitor module loading +auditctl -a always,exit -F arch=b64 -S init_module -S finit_module -k modules + +# Monitor network connections +auditctl -a always,exit -F arch=b64 -S connect -k network_connect +``` + +## Audit Log Fields +| Field | Description | +|-------|------------| +| type | Event type (SYSCALL, PATH, EXECVE, USER_CMD) | +| msg | audit(timestamp:event_id) | +| syscall | System call number | +| uid/euid | User ID / Effective UID | +| comm | Command name | +| exe | Executable path | +| key | Audit rule key | +| success | yes/no | +| name | File path (in PATH records) | + +## Suspicious Syscalls +| Syscall | Concern | +|---------|---------| +| execve | Program execution | +| ptrace | Process debugging/injection | +| init_module | Kernel rootkit loading | +| connect | Outbound connection | +| setuid | Privilege change | +| open_by_handle_at | Container escape | diff --git a/personas/_shared/skills/analyzing-linux-audit-logs-for-intrusion/scripts/agent.py b/personas/_shared/skills/analyzing-linux-audit-logs-for-intrusion/scripts/agent.py new file mode 100644 index 0000000..7727e70 --- /dev/null +++ b/personas/_shared/skills/analyzing-linux-audit-logs-for-intrusion/scripts/agent.py @@ -0,0 +1,221 @@ +#!/usr/bin/env python3 +"""Linux audit log analysis agent for intrusion detection. + +Parses /var/log/audit/audit.log entries to detect privilege escalation, +unauthorized file access, suspicious syscalls, and process execution anomalies. +""" + +import argparse +import json +import re +import sys +import datetime +import collections +import subprocess + + +SUSPICIOUS_SYSCALLS = { + "execve": "Program execution", + "connect": "Network connection", + "bind": "Port binding", + "ptrace": "Process tracing/debugging", + "init_module": "Kernel module loading", + "finit_module": "Kernel module loading", + "delete_module": "Kernel module unloading", + "mount": "Filesystem mount", + "umount2": "Filesystem unmount", + "setuid": "UID change", + "setgid": "GID change", + "sethostname": "Hostname change", + "open_by_handle_at": "File open by handle (container escape)", +} + +SENSITIVE_PATHS = [ + "/etc/passwd", "/etc/shadow", "/etc/sudoers", + "/etc/ssh/sshd_config", "/root/.ssh/authorized_keys", + "/etc/crontab", "/var/spool/cron", +] + +SUSPICIOUS_COMMANDS = [ + "curl", "wget", "nc", "ncat", "nmap", "tcpdump", + "python", "perl", "ruby", "gcc", "cc", "make", + "useradd", "usermod", "groupadd", "visudo", + "iptables", "ip6tables", "nft", +] + + +def parse_audit_log(log_path, max_lines=50000): + """Parse raw audit.log file into structured events.""" + events = [] + current = {} + try: + with open(log_path, "r") as f: + for i, line in enumerate(f): + if i >= max_lines: + break + match = re.match( + r"type=(\S+)\s+msg=audit\((\d+\.\d+):(\d+)\):\s*(.*)", line + ) + if not match: + continue + event_type = match.group(1) + timestamp = float(match.group(2)) + event_id = match.group(3) + data_str = match.group(4) + fields = dict(re.findall(r'(\w+)=("[^"]*"|\S+)', data_str)) + for k, v in fields.items(): + fields[k] = v.strip('"') + event = { + "type": event_type, + "timestamp": datetime.datetime.fromtimestamp(timestamp).isoformat(), + "event_id": event_id, + **fields, + } + events.append(event) + except FileNotFoundError: + return {"error": f"Log file not found: {log_path}"} + return events + + +def detect_privilege_escalation(events): + """Detect privilege escalation indicators in audit events.""" + findings = [] + for e in events: + if e.get("type") == "SYSCALL" and e.get("syscall_name") in ("setuid", "setgid", "execve"): + if e.get("uid") != "0" and e.get("euid") == "0": + findings.append({ + "type": "privilege_escalation", + "detail": f"UID {e.get('uid')} escalated to eUID 0", + "command": e.get("comm", ""), + "exe": e.get("exe", ""), + "timestamp": e.get("timestamp"), + "severity": "CRITICAL", + }) + if e.get("type") == "USER_CMD" and "sudo" in e.get("cmd", "").lower(): + findings.append({ + "type": "sudo_usage", + "user": e.get("acct", e.get("uid", "")), + "command": e.get("cmd", ""), + "timestamp": e.get("timestamp"), + "severity": "MEDIUM", + }) + return findings + + +def detect_file_access(events): + """Detect access to sensitive files.""" + findings = [] + for e in events: + if e.get("type") in ("PATH", "SYSCALL"): + path = e.get("name", e.get("exe", "")) + for sensitive in SENSITIVE_PATHS: + if sensitive in path: + findings.append({ + "type": "sensitive_file_access", + "path": path, + "syscall": e.get("syscall_name", e.get("syscall", "")), + "user": e.get("uid", ""), + "timestamp": e.get("timestamp"), + "severity": "HIGH", + }) + break + return findings + + +def detect_suspicious_commands(events): + """Detect execution of suspicious commands.""" + findings = [] + for e in events: + if e.get("type") in ("EXECVE", "SYSCALL"): + comm = e.get("comm", "").lower() + exe = e.get("exe", "").lower() + for cmd in SUSPICIOUS_COMMANDS: + if cmd in comm or cmd in exe: + findings.append({ + "type": "suspicious_command", + "command": comm, + "exe": exe, + "user": e.get("uid", ""), + "timestamp": e.get("timestamp"), + "severity": "MEDIUM", + }) + break + return findings + + +def run_ausearch(key=None, message_type=None, success=None): + """Run ausearch command and return results.""" + cmd = ["ausearch"] + if key: + cmd.extend(["-k", key]) + if message_type: + cmd.extend(["-m", message_type]) + if success is not None: + cmd.extend(["--success", "yes" if success else "no"]) + cmd.extend(["--format", "csv"]) + try: + result = subprocess.run(cmd, capture_output=True, text=True, timeout=30) + return {"output": result.stdout[:5000], "exit_code": result.returncode} + except (FileNotFoundError, subprocess.TimeoutExpired) as e: + return {"error": str(e)} + + +def generate_summary(events, findings): + """Generate audit log analysis summary.""" + event_types = collections.Counter(e.get("type") for e in events) + finding_types = collections.Counter(f.get("type") for f in findings) + severity_counts = collections.Counter(f.get("severity") for f in findings) + return { + "total_events": len(events), + "event_types": dict(event_types.most_common(10)), + "total_findings": len(findings), + "finding_types": dict(finding_types), + "by_severity": dict(severity_counts), + } + + +def main(): + parser = argparse.ArgumentParser(description="Linux audit log intrusion detection agent") + parser.add_argument("log_file", nargs="?", default="/var/log/audit/audit.log", + help="Path to audit.log (default: /var/log/audit/audit.log)") + parser.add_argument("--max-lines", type=int, default=50000, help="Max log lines to parse") + parser.add_argument("--ausearch-key", help="Run ausearch with this key") + parser.add_argument("--output", "-o", help="Output JSON report path") + args = parser.parse_args() + + print("[*] Linux Audit Log Intrusion Detection Agent") + + if args.ausearch_key: + result = run_ausearch(key=args.ausearch_key) + print(json.dumps(result, indent=2)) + sys.exit(0) + + events = parse_audit_log(args.log_file, args.max_lines) + if isinstance(events, dict) and "error" in events: + print(f"[!] {events['error']}") + print("[DEMO] Specify a valid audit.log path or run on a Linux system") + print(json.dumps({"demo": True, "monitored_syscalls": len(SUSPICIOUS_SYSCALLS)}, indent=2)) + sys.exit(0) + + findings = [] + findings.extend(detect_privilege_escalation(events)) + findings.extend(detect_file_access(events)) + findings.extend(detect_suspicious_commands(events)) + + summary = generate_summary(events, findings) + print(f"[*] Events parsed: {summary['total_events']}") + print(f"[*] Findings: {summary['total_findings']}") + print(f" By severity: {summary['by_severity']}") + for f in findings[:15]: + print(f" [{f['severity']}] {f['type']}: {f.get('command', f.get('path', ''))}") + + if args.output: + report = {"summary": summary, "findings": findings} + with open(args.output, "w") as f: + json.dump(report, f, indent=2, default=str) + + print(json.dumps(summary, indent=2)) + + +if __name__ == "__main__": + main() diff --git a/personas/_shared/skills/analyzing-linux-elf-malware/LICENSE b/personas/_shared/skills/analyzing-linux-elf-malware/LICENSE new file mode 100644 index 0000000..d885118 --- /dev/null +++ b/personas/_shared/skills/analyzing-linux-elf-malware/LICENSE @@ -0,0 +1,201 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to the Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by the Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding any notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. Please do not remove or change + the license header comment from a contributed file except when + necessary. + + Copyright 2026 mukul975 + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/personas/_shared/skills/analyzing-linux-elf-malware/SKILL.md b/personas/_shared/skills/analyzing-linux-elf-malware/SKILL.md new file mode 100644 index 0000000..66f0f45 --- /dev/null +++ b/personas/_shared/skills/analyzing-linux-elf-malware/SKILL.md @@ -0,0 +1,341 @@ +--- +name: analyzing-linux-elf-malware +description: 'Analyzes malicious Linux ELF (Executable and Linkable Format) binaries including botnets, cryptominers, ransomware, + and rootkits targeting Linux servers, containers, and cloud infrastructure. Covers static analysis, dynamic tracing, and + reverse engineering of x86_64 and ARM ELF samples. Activates for requests involving Linux malware analysis, ELF binary investigation, + Linux server compromise assessment, or container malware analysis. + + ' +domain: cybersecurity +subdomain: malware-analysis +tags: +- malware +- Linux +- ELF +- reverse-engineering +- server-malware +version: 1.0.0 +author: mahipal +license: Apache-2.0 +nist_csf: +- DE.AE-02 +- RS.AN-03 +- ID.RA-01 +- DE.CM-01 +--- + +# Analyzing Linux ELF Malware + +## When to Use + +- A Linux server or container has been compromised and suspicious ELF binaries are found +- Analyzing Linux botnets (Mirai, Gafgyt, XorDDoS), cryptominers, or ransomware +- Investigating malware targeting cloud infrastructure, Docker containers, or Kubernetes pods +- Reverse engineering Linux rootkits and kernel modules +- Analyzing cross-platform malware compiled for Linux x86_64, ARM, or MIPS architectures + +**Do not use** for Windows PE binary analysis; use PEStudio, Ghidra, or IDA for Windows malware. + +## Prerequisites + +- Ghidra or IDA with Linux ELF support for disassembly and decompilation +- Linux analysis VM (Ubuntu 22.04 recommended) with development tools installed +- strace, ltrace, and GDB for dynamic analysis and debugging +- readelf, objdump, and nm from GNU binutils for static inspection +- Radare2 for quick binary triage and scripted analysis +- Docker for isolated container-based malware execution + +## Workflow + +### Step 1: Identify ELF Binary Properties + +Examine the ELF header and basic properties: + +```bash +# File type identification +file suspect_binary + +# Detailed ELF header analysis +readelf -h suspect_binary + +# Section headers +readelf -S suspect_binary + +# Program headers (segments) +readelf -l suspect_binary + +# Symbol table (if not stripped) +readelf -s suspect_binary +nm suspect_binary 2>/dev/null + +# Dynamic linking information +readelf -d suspect_binary +ldd suspect_binary 2>/dev/null # Only on matching architecture! + +# Compute hashes +md5sum suspect_binary +sha256sum suspect_binary + +# Check for packing/UPX +upx -t suspect_binary +``` + +```python +# Python-based ELF analysis +from elftools.elf.elffile import ELFFile +import hashlib + +with open("suspect_binary", "rb") as f: + data = f.read() + sha256 = hashlib.sha256(data).hexdigest() + +with open("suspect_binary", "rb") as f: + elf = ELFFile(f) + + print(f"SHA-256: {sha256}") + print(f"Class: {elf.elfclass}-bit") + print(f"Endian: {elf.little_endian and 'Little' or 'Big'}") + print(f"Machine: {elf.header.e_machine}") + print(f"Type: {elf.header.e_type}") + print(f"Entry Point: 0x{elf.header.e_entry:X}") + + # Check if stripped + symtab = elf.get_section_by_name('.symtab') + print(f"Stripped: {'Yes' if symtab is None else 'No'}") + + # Section entropy analysis + import math + from collections import Counter + for section in elf.iter_sections(): + data = section.data() + if len(data) > 0: + entropy = -sum((c/len(data)) * math.log2(c/len(data)) + for c in Counter(data).values() if c > 0) + if entropy > 7.0: + print(f" [!] High entropy section: {section.name} ({entropy:.2f})") +``` + +### Step 2: Extract Strings and Indicators + +Search for embedded IOCs and functionality clues: + +```bash +# ASCII strings +strings suspect_binary > strings_output.txt + +# Search for network indicators +grep -iE "(http|https|ftp)://" strings_output.txt +grep -iE "([0-9]{1,3}\.){3}[0-9]{1,3}" strings_output.txt +grep -iE "[a-zA-Z0-9.-]+\.(com|net|org|io|ru|cn)" strings_output.txt + +# Search for shell commands +grep -iE "(bash|sh|wget|curl|chmod|/tmp/|/dev/)" strings_output.txt + +# Search for crypto mining indicators +grep -iE "(stratum|xmr|monero|pool\.|mining)" strings_output.txt + +# Search for SSH/credential theft +grep -iE "(ssh|authorized_keys|id_rsa|shadow|passwd)" strings_output.txt + +# Search for persistence mechanisms +grep -iE "(crontab|systemd|init\.d|rc\.local|ld\.so\.preload)" strings_output.txt + +# FLOSS for obfuscated strings (if available) +floss suspect_binary +``` + +### Step 3: Analyze System Calls and Library Usage + +Identify what system calls and libraries the malware uses: + +```bash +# List imported functions (dynamically linked) +readelf -r suspect_binary | grep -E "socket|connect|exec|fork|open|write|bind|listen" + +# Trace system calls during execution (in isolated VM only) +strace -f -e trace=network,process,file -o strace_output.txt ./suspect_binary + +# Trace library calls +ltrace -f -o ltrace_output.txt ./suspect_binary + +# Key system calls to watch: +# Network: socket, connect, bind, listen, accept, sendto, recvfrom +# Process: fork, execve, clone, kill, ptrace +# File: open, read, write, unlink, rename, chmod +# Persistence: inotify_add_watch (file monitoring) +``` + +### Step 4: Dynamic Analysis with GDB + +Debug the malware to observe runtime behavior: + +```bash +# Start GDB with the binary +gdb ./suspect_binary + +# Set breakpoints on key functions +(gdb) break main +(gdb) break socket +(gdb) break connect +(gdb) break execve +(gdb) break fork + +# Run and analyze +(gdb) run +(gdb) info registers # View register state +(gdb) x/20s $rdi # Examine string argument +(gdb) bt # Backtrace +(gdb) continue + +# For stripped binaries, break on entry point +(gdb) break *0x400580 # Entry point from readelf +(gdb) run + +# Monitor network connections during execution +# In another terminal: +ss -tlnp # List listening sockets +ss -tnp # List established connections +``` + +### Step 5: Reverse Engineer with Ghidra + +Perform deep code analysis on the ELF binary: + +``` +Ghidra Analysis for Linux ELF: +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +1. Import: File -> Import -> Select ELF binary + - Ghidra auto-detects ELF format and architecture + - Accept default analysis options + +2. Key analysis targets: + - main() function (or entry point if stripped) + - Socket creation and connection functions + - Command dispatch logic (switch/case on received data) + - Encryption/encoding routines + - Persistence installation code + - Self-propagation/scanning functions + +3. For Mirai-like botnets, look for: + - Credential list for brute-forcing (telnet/SSH) + - Attack module selection (UDP flood, SYN flood, ACK flood) + - Scanner module (port scanning for vulnerable devices) + - Killer module (killing competing botnets) + +4. For cryptominers, look for: + - Mining pool connection (stratum protocol) + - Wallet address strings + - CPU/GPU utilization functions + - Process hiding techniques +``` + +### Step 6: Analyze Linux-Specific Persistence + +Check for persistence mechanisms: + +```bash +# Check for LD_PRELOAD rootkit +strings suspect_binary | grep "ld.so.preload" +# Malware writing to /etc/ld.so.preload can hook all dynamic library calls + +# Check for crontab persistence +strings suspect_binary | grep -i "cron" + +# Check for systemd service creation +strings suspect_binary | grep -iE "systemd|\.service|systemctl" + +# Check for init script creation +strings suspect_binary | grep -iE "init\.d|rc\.local|update-rc" + +# Check for SSH key injection +strings suspect_binary | grep -i "authorized_keys" + +# Check for kernel module (rootkit) loading +strings suspect_binary | grep -iE "insmod|modprobe|init_module" + +# Check for process hiding +strings suspect_binary | grep -iE "proc|readdir|getdents" +``` + +## Key Concepts + +| Term | Definition | +|------|------------| +| **ELF (Executable and Linkable Format)** | Standard binary format for Linux executables, shared libraries, and core dumps containing headers, sections, and segments | +| **Stripped Binary** | ELF binary with debug symbols removed, making reverse engineering more difficult as function names are lost | +| **LD_PRELOAD** | Linux environment variable specifying shared libraries to load before all others; abused by rootkits to intercept system library calls | +| **strace** | Linux system call tracer that logs all system calls and signals made by a process, revealing file, network, and process operations | +| **GOT/PLT** | Global Offset Table and Procedure Linkage Table; ELF structures for dynamic linking that can be hijacked for function hooking | +| **Statically Linked** | Binary compiled with all library code included; common in IoT malware to run on systems without matching shared libraries | +| **Mirai** | Prolific Linux botnet targeting IoT devices via telnet brute-force; source code leaked, leading to many variants | + +## Tools & Systems + +- **Ghidra**: NSA reverse engineering tool with full ELF support for x86, x86_64, ARM, MIPS, and other Linux architectures +- **Radare2**: Open-source reverse engineering framework with command-line interface for quick binary analysis and scripting +- **strace**: Linux system call tracing tool for observing binary behavior including file, network, and process operations +- **GDB**: GNU Debugger for setting breakpoints, examining memory, and stepping through Linux binary execution +- **pyelftools**: Python library for parsing ELF files programmatically for automated analysis pipelines + +## Common Scenarios + +### Scenario: Analyzing a Cryptominer Found on a Compromised Linux Server + +**Context**: A cloud server shows 100% CPU usage. Investigation reveals an unknown binary running from /tmp with a suspicious name. The binary needs analysis to confirm it is a cryptominer and identify the attacker's wallet and pool. + +**Approach**: +1. Copy the binary to an analysis VM and compute SHA-256 hash +2. Run `file` and `readelf` to identify architecture and linking type +3. Extract strings and search for mining pool addresses (stratum+tcp://) and wallet addresses +4. Run with strace in a sandbox to observe network connections (mining pool connection) +5. Import into Ghidra to identify the mining algorithm and configuration extraction +6. Check for persistence mechanisms (crontab, systemd service, SSH keys) +7. Document all IOCs including pool address, wallet, C2 for updates, and persistence artifacts + +**Pitfalls**: +- Running `ldd` on malware outside a sandbox (ldd can execute code in the binary) +- Not checking for ARM/MIPS architecture before attempting x86_64 execution +- Missing companion scripts (.sh files) that may handle persistence and cleanup +- Ignoring the initial access vector (how the miner was deployed: SSH brute force, web exploit, container escape) + +## Output Format + +``` +LINUX ELF MALWARE ANALYSIS REPORT +==================================== +File: /tmp/.X11-unix/.rsync +SHA-256: e3b0c44298fc1c149afbf4c8996fb924... +Type: ELF 64-bit LSB executable, x86-64 +Linking: Statically linked (all libraries embedded) +Stripped: Yes +Size: 2,847,232 bytes +Packer: UPX 3.96 (unpacked for analysis) + +CLASSIFICATION +Family: XMRig Cryptominer (modified) +Variant: Custom build with C2 update mechanism + +FUNCTIONALITY +[*] XMR (Monero) mining via RandomX algorithm +[*] Stratum pool connection for work submission +[*] C2 check-in for configuration updates +[*] Process name masquerading (argv[0] = "[kworker/0:0]") +[*] Competitor process killing (kills other miners) +[*] SSH key injection for re-access + +NETWORK INDICATORS +Mining Pool: stratum+tcp://pool.minexmr[.]com:4444 +C2 Server: hxxp://update.malicious[.]com/config +Wallet: 49jZ5Q3b...Monero_Wallet_Address... + +PERSISTENCE +[1] Crontab entry: */5 * * * * /tmp/.X11-unix/.rsync +[2] SSH key added to /root/.ssh/authorized_keys +[3] Systemd service: /etc/systemd/system/rsync-daemon.service +[4] Modified /etc/ld.so.preload for process hiding + +PROCESS HIDING +LD_PRELOAD: /usr/lib/.libsystem.so +Hook: readdir() to hide /tmp/.X11-unix/.rsync from ls +Hook: fopen() to hide from /proc/*/maps reading +``` diff --git a/personas/_shared/skills/analyzing-linux-elf-malware/references/api-reference.md b/personas/_shared/skills/analyzing-linux-elf-malware/references/api-reference.md new file mode 100644 index 0000000..126d04b --- /dev/null +++ b/personas/_shared/skills/analyzing-linux-elf-malware/references/api-reference.md @@ -0,0 +1,119 @@ +# API Reference: Linux ELF Malware Analysis Tools + +## readelf - ELF Binary Inspection + +### Syntax +```bash +readelf -h # ELF header +readelf -S # Section headers +readelf -l # Program headers (segments) +readelf -s # Symbol table +readelf -d # Dynamic section +readelf -r # Relocation entries +readelf -n # Notes section +``` + +### Key ELF Header Fields +| Field | Description | +|-------|-------------| +| `Class` | 32-bit or 64-bit | +| `Machine` | Architecture (x86-64, ARM, MIPS) | +| `Type` | EXEC (executable), DYN (shared object) | +| `Entry point` | Code execution start address | + +## pyelftools - Python ELF Parsing + +### Usage +```python +from elftools.elf.elffile import ELFFile + +with open("binary", "rb") as f: + elf = ELFFile(f) + elf.elfclass # 32 or 64 + elf.little_endian # True/False + elf.header.e_machine # Architecture + elf.header.e_entry # Entry point + elf.num_sections() # Section count + elf.get_section_by_name(".symtab") # Symbol table +``` + +## strings - String Extraction + +### Syntax +```bash +strings # ASCII strings (default min 4) +strings -n 8 # Minimum 8 characters +strings -e l # 16-bit little-endian (Unicode) +strings -t x # Print offset in hex +``` + +## strace - System Call Tracing + +### Syntax +```bash +strace -f ./binary # Follow forks +strace -e trace=network ./binary # Network calls only +strace -e trace=file ./binary # File operations only +strace -e trace=process ./binary # Process operations +strace -o output.txt ./binary # Log to file +strace -c ./binary # Summary statistics +``` + +### Key System Calls +| Call | Category | +|------|----------| +| `socket`, `connect`, `bind` | Network | +| `fork`, `execve`, `clone` | Process | +| `open`, `read`, `write`, `unlink` | File I/O | +| `ptrace` | Anti-debug/injection | + +## ltrace - Library Call Tracing + +### Syntax +```bash +ltrace -f ./binary # Follow child processes +ltrace -e malloc+free ./binary # Specific functions +ltrace -o output.txt ./binary # Log to file +``` + +## GDB - GNU Debugger + +### Syntax +```bash +gdb ./binary +(gdb) break main +(gdb) break *0x400580 # Break at address +(gdb) run +(gdb) info registers +(gdb) x/20s $rdi # Examine string at RDI +(gdb) x/10i $rip # Disassemble at RIP +(gdb) bt # Backtrace +``` + +## UPX - Packer Detection/Unpacking + +### Syntax +```bash +upx -t # Test if packed +upx -d # Decompress/unpack +upx -l # List compression details +``` + +## objdump - Disassembly + +### Syntax +```bash +objdump -d # Disassemble .text +objdump -D # Disassemble all sections +objdump -M intel -d # Intel syntax +objdump -t # Symbol table +``` + +## nm - Symbol Listing + +### Syntax +```bash +nm # List symbols +nm -D # Dynamic symbols only +nm -u # Undefined (imported) symbols +``` diff --git a/personas/_shared/skills/analyzing-linux-elf-malware/scripts/agent.py b/personas/_shared/skills/analyzing-linux-elf-malware/scripts/agent.py new file mode 100644 index 0000000..d3455e4 --- /dev/null +++ b/personas/_shared/skills/analyzing-linux-elf-malware/scripts/agent.py @@ -0,0 +1,230 @@ +#!/usr/bin/env python3 +"""Linux ELF malware static analysis agent using pyelftools and binary inspection.""" + +import hashlib +import math +import os +import sys +import subprocess +from collections import Counter + +try: + from elftools.elf.elffile import ELFFile + HAS_ELFTOOLS = True +except ImportError: + HAS_ELFTOOLS = False + + +def compute_hashes(filepath): + """Compute MD5, SHA1, and SHA256 hashes of a file.""" + md5 = hashlib.md5() + sha1 = hashlib.sha1() + sha256 = hashlib.sha256() + with open(filepath, "rb") as f: + for chunk in iter(lambda: f.read(65536), b""): + md5.update(chunk) + sha1.update(chunk) + sha256.update(chunk) + return {"md5": md5.hexdigest(), "sha1": sha1.hexdigest(), "sha256": sha256.hexdigest()} + + +def calculate_entropy(data): + """Calculate Shannon entropy of binary data.""" + if not data: + return 0.0 + counter = Counter(data) + length = len(data) + return -sum((c / length) * math.log2(c / length) for c in counter.values()) + + +def analyze_elf_header(filepath): + """Parse ELF header and extract key properties.""" + if not HAS_ELFTOOLS: + return {"error": "pyelftools not installed: pip install pyelftools"} + with open(filepath, "rb") as f: + elf = ELFFile(f) + symtab = elf.get_section_by_name(".symtab") + info = { + "class": f"{elf.elfclass}-bit", + "endian": "Little" if elf.little_endian else "Big", + "machine": elf.header.e_machine, + "type": elf.header.e_type, + "entry_point": f"0x{elf.header.e_entry:X}", + "stripped": symtab is None, + "num_sections": elf.num_sections(), + "num_segments": elf.num_segments(), + } + return info + + +def analyze_sections(filepath): + """Analyze ELF sections for entropy and suspicious characteristics.""" + if not HAS_ELFTOOLS: + return [] + sections = [] + with open(filepath, "rb") as f: + elf = ELFFile(f) + for section in elf.iter_sections(): + data = section.data() + if len(data) == 0: + continue + entropy = calculate_entropy(data) + sections.append({ + "name": section.name, + "type": section["sh_type"], + "size": len(data), + "entropy": round(entropy, 4), + "high_entropy": entropy > 7.0, + "flags": section["sh_flags"], + }) + return sections + + +def extract_strings(filepath, min_length=6): + """Extract ASCII strings from the binary and categorize by type.""" + stdout, _, rc = subprocess.run( + ["strings", "-n", str(min_length), filepath], + capture_output=True, text=True, timeout=120 + ).stdout, "", 0 + if not stdout: + return {} + all_strings = stdout.strip().splitlines() + categorized = { + "urls": [], "ips": [], "domains": [], "shell_commands": [], + "crypto_mining": [], "persistence": [], "ssh_related": [], + "total": len(all_strings), + } + for s in all_strings: + s_lower = s.lower() + if any(proto in s_lower for proto in ["http://", "https://", "ftp://"]): + categorized["urls"].append(s) + if any(p in s_lower for p in ["stratum", "xmr", "monero", "pool.", "mining"]): + categorized["crypto_mining"].append(s) + if any(p in s_lower for p in ["crontab", "systemd", "init.d", "rc.local", + "ld.so.preload", "systemctl"]): + categorized["persistence"].append(s) + if any(p in s_lower for p in ["ssh", "authorized_keys", "id_rsa", "shadow", "passwd"]): + categorized["ssh_related"].append(s) + if any(p in s_lower for p in ["bash", "wget", "curl", "chmod", "/tmp/", "/dev/"]): + categorized["shell_commands"].append(s) + import re + if re.match(r"\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}", s): + categorized["ips"].append(s) + if re.match(r"[a-zA-Z0-9.-]+\.(com|net|org|io|ru|cn|xyz)", s): + categorized["domains"].append(s) + return categorized + + +def check_packing(filepath): + """Check if the binary is packed with UPX or other packers.""" + with open(filepath, "rb") as f: + data = f.read(4096) + indicators = [] + if b"UPX!" in data: + indicators.append("UPX packer detected (UPX! magic)") + if b"UPX0" in data or b"UPX1" in data: + indicators.append("UPX section names found") + stdout, _, _ = subprocess.run(["upx", "-t", filepath], + capture_output=True, text=True, + stderr=subprocess.STDOUT, timeout=120).stdout, "", 0 + if stdout and "packed" in stdout.lower(): + indicators.append("UPX verification confirms packing") + return indicators + + +def analyze_dynamic_linking(filepath): + """Analyze dynamic linking information and imported functions.""" + stdout, _, rc = subprocess.run(["readelf", "-d", filepath], + capture_output=True, text=True, timeout=120).stdout, "", 0 + dynamic_info = {"libraries": [], "rpath": None} + if stdout: + for line in stdout.splitlines(): + if "NEEDED" in line: + lib = line.split("[")[-1].rstrip("]") if "[" in line else "" + dynamic_info["libraries"].append(lib) + if "RPATH" in line or "RUNPATH" in line: + dynamic_info["rpath"] = line.split("[")[-1].rstrip("]") + + readelf_proc = subprocess.run( + ["readelf", "-r", filepath], + capture_output=True, text=True, + timeout=120, + ) + import re as _re + suspicious_funcs = _re.compile(r'socket|connect|exec|fork|open|write|bind|listen|send|recv') + stdout2 = "\n".join( + line for line in (readelf_proc.stdout or "").splitlines() + if suspicious_funcs.search(line) + ) + dynamic_info["suspicious_imports"] = [ + line.strip() for line in (stdout2 or "").splitlines() if line.strip() + ] + return dynamic_info + + +def detect_malware_type(strings_data): + """Classify malware type based on extracted strings.""" + classifications = [] + if strings_data.get("crypto_mining"): + classifications.append("Cryptominer") + if any("flood" in s.lower() or "ddos" in s.lower() + for s in strings_data.get("shell_commands", [])): + classifications.append("DDoS Botnet") + if strings_data.get("ssh_related") and strings_data.get("persistence"): + classifications.append("Backdoor/Trojan") + if any("insmod" in s or "modprobe" in s or "init_module" in s + for s in strings_data.get("shell_commands", [])): + classifications.append("Rootkit") + if any("ransom" in s.lower() or "encrypt" in s.lower() or "bitcoin" in s.lower() + for cat in strings_data.values() if isinstance(cat, list) for s in cat): + classifications.append("Ransomware") + return classifications or ["Unknown"] + + +if __name__ == "__main__": + print("=" * 60) + print("Linux ELF Malware Analysis Agent") + print("Static analysis with pyelftools, strings, readelf") + print("=" * 60) + + target = sys.argv[1] if len(sys.argv) > 1 else None + + if target and os.path.exists(target): + print(f"\n[*] Analyzing: {target}") + print(f"[*] Size: {os.path.getsize(target)} bytes") + + hashes = compute_hashes(target) + print(f"[*] MD5: {hashes['md5']}") + print(f"[*] SHA256: {hashes['sha256']}") + + elf_info = analyze_elf_header(target) + print(f"\n--- ELF Header ---") + for k, v in elf_info.items(): + print(f" {k}: {v}") + + packing = check_packing(target) + if packing: + for p in packing: + print(f"[!] {p}") + + sections = analyze_sections(target) + high_ent = [s for s in sections if s.get("high_entropy")] + if high_ent: + print(f"\n[!] High entropy sections (possible packing/encryption):") + for s in high_ent: + print(f" {s['name']}: entropy={s['entropy']}, size={s['size']}") + + strings_data = extract_strings(target) + print(f"\n--- Strings Analysis ({strings_data.get('total', 0)} total) ---") + for category in ["urls", "ips", "domains", "crypto_mining", "persistence", "ssh_related"]: + items = strings_data.get(category, []) + if items: + print(f" {category}: {len(items)}") + for item in items[:5]: + print(f" - {item}") + + classification = detect_malware_type(strings_data) + print(f"\n[*] Classification: {', '.join(classification)}") + else: + print(f"\n[DEMO] Usage: python agent.py ") + print("[*] Provide a Linux ELF binary for analysis.") diff --git a/personas/_shared/skills/analyzing-linux-kernel-rootkits/LICENSE b/personas/_shared/skills/analyzing-linux-kernel-rootkits/LICENSE new file mode 100644 index 0000000..d885118 --- /dev/null +++ b/personas/_shared/skills/analyzing-linux-kernel-rootkits/LICENSE @@ -0,0 +1,201 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to the Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by the Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding any notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. Please do not remove or change + the license header comment from a contributed file except when + necessary. + + Copyright 2026 mukul975 + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/personas/_shared/skills/analyzing-linux-kernel-rootkits/SKILL.md b/personas/_shared/skills/analyzing-linux-kernel-rootkits/SKILL.md new file mode 100644 index 0000000..6b89330 --- /dev/null +++ b/personas/_shared/skills/analyzing-linux-kernel-rootkits/SKILL.md @@ -0,0 +1,135 @@ +--- +name: analyzing-linux-kernel-rootkits +description: Detect kernel-level rootkits in Linux memory dumps using Volatility3 linux plugins (check_syscall, lsmod, hidden_modules), + rkhunter system scanning, and /proc vs /sys discrepancy analysis to identify hooked syscalls, hidden kernel modules, and + tampered system structures. +domain: cybersecurity +subdomain: digital-forensics +tags: +- rootkit +- linux +- kernel +- volatility3 +- memory-forensics +- malware-analysis +- rkhunter +- forensics +version: '1.0' +author: mahipal +license: Apache-2.0 +nist_csf: +- RS.AN-01 +- RS.AN-03 +- DE.AE-02 +- RS.MA-01 +--- + +# Analyzing Linux Kernel Rootkits + +## Overview + +Linux kernel rootkits operate at ring 0, modifying kernel data structures to hide processes, files, network connections, and kernel modules from userspace tools. Detection requires either memory forensics (analyzing physical memory dumps with Volatility3) or cross-view analysis (comparing /proc, /sys, and kernel data structures for inconsistencies). This skill covers using Volatility3 Linux plugins to detect syscall table hooks, hidden kernel modules, and modified function pointers, supplemented by live system scanning with rkhunter and chkrootkit. + + +## When to Use + +- When investigating security incidents that require analyzing linux kernel rootkits +- When building detection rules or threat hunting queries for this domain +- When SOC analysts need structured procedures for this analysis type +- When validating security monitoring coverage for related attack techniques + +## Prerequisites + +- Volatility3 installed (pip install volatility3) +- Linux memory dump (acquired via LiME, AVML, or /proc/kcore) +- Volatility3 Linux symbol table (ISF) matching the target kernel version +- rkhunter and chkrootkit for live system scanning +- Reference known-good kernel image for comparison + +## Steps + +### Step 1: Acquire Memory Dump +Capture Linux physical memory using LiME kernel module or AVML for cloud instances. + +### Step 2: Analyze with Volatility3 +Run linux.check_syscall, linux.lsmod, linux.hidden_modules, and linux.check_idt plugins to detect rootkit artifacts. + +### Step 3: Cross-View Analysis +Compare module lists from /proc/modules, lsmod, and /sys/module to identify modules hidden from one view but present in another. + +### Step 4: Live System Scanning +Run rkhunter and chkrootkit to detect known rootkit signatures, suspicious files, and modified system binaries. + +## Expected Output + +JSON report containing detected syscall hooks, hidden kernel modules, modified IDT entries, suspicious /proc discrepancies, and rkhunter findings. + +## Example Output + +```text +$ sudo python3 rootkit_analyzer.py --memory /evidence/linux-mem.lime --profile Ubuntu2204 + +Linux Kernel Rootkit Analysis Report +===================================== +Memory Image: /evidence/linux-mem.lime +Kernel Version: 5.15.0-91-generic (Ubuntu 22.04 LTS) +Analysis Time: 2024-01-18 09:15:32 UTC + +[+] Scanning syscall table for hooks... + Syscall Table Base: 0xffffffff82200300 + Total syscalls checked: 449 + + HOOKED SYSCALLS DETECTED: + ┌─────────┬──────────────────┬──────────────────────┬──────────────────────┐ + │ NR │ Syscall │ Expected Address │ Current Address │ + ├─────────┼──────────────────┼──────────────────────┼──────────────────────┤ + │ 0 │ sys_read │ 0xffffffff8139a0e0 │ 0xffffffffc0a12000 │ + │ 2 │ sys_open │ 0xffffffff8139b340 │ 0xffffffffc0a12180 │ + │ 78 │ sys_getdents64 │ 0xffffffff813f5210 │ 0xffffffffc0a12300 │ + │ 62 │ sys_kill │ 0xffffffff8110c4a0 │ 0xffffffffc0a12480 │ + └─────────┴──────────────────┴──────────────────────┴──────────────────────┘ + WARNING: 4 syscall hooks detected - rootkit behavior confirmed + +[+] Checking for hidden kernel modules... + Loaded modules (lsmod): 147 + Modules in kobject list: 149 + HIDDEN MODULES: + - "netfilter_helper" at 0xffffffffc0a10000 (size: 12288) + - "kworker_sched" at 0xffffffffc0a14000 (size: 8192) + +[+] Scanning /proc for discrepancies... + Processes in task_struct list: 234 + Processes visible in /proc: 231 + HIDDEN PROCESSES: + - PID 31337 cmd: "[kworker/0:3]" (disguised as kernel thread) + - PID 31442 cmd: "rsyslogd" (fake, real rsyslogd is PID 892) + - PID 31500 cmd: "" (unnamed process) + +[+] Checking IDT entries... + IDT entries scanned: 256 + Modified entries: 0 (clean) + +[+] Running rkhunter scan... + Checking for known rootkits: 68 variants checked + Diamorphine rootkit: WARNING - signatures match + System binary checks: + /usr/bin/ps: MODIFIED (SHA-256 mismatch) + /usr/bin/netstat: MODIFIED (SHA-256 mismatch) + /usr/bin/ls: MODIFIED (SHA-256 mismatch) + /usr/sbin/ss: OK + +[+] Network analysis... + Hidden connections (not in /proc/net/tcp): + ESTABLISHED 0.0.0.0:0 -> 198.51.100.47:4443 (PID 31337) + ESTABLISHED 0.0.0.0:0 -> 198.51.100.47:8080 (PID 31442) + +Summary: + Rootkit Type: Loadable Kernel Module (LKM) + Probable Family: Diamorphine variant + Syscall Hooks: 4 (read, open, getdents64, kill) + Hidden Modules: 2 + Hidden Processes: 3 + Hidden Connections: 2 (C2: 198.51.100.47) + Modified Binaries: 3 (/usr/bin/ps, netstat, ls) + Risk Level: CRITICAL +``` diff --git a/personas/_shared/skills/analyzing-linux-kernel-rootkits/references/api-reference.md b/personas/_shared/skills/analyzing-linux-kernel-rootkits/references/api-reference.md new file mode 100644 index 0000000..7423fa0 --- /dev/null +++ b/personas/_shared/skills/analyzing-linux-kernel-rootkits/references/api-reference.md @@ -0,0 +1,92 @@ +# API Reference: Analyzing Linux Kernel Rootkits + +## Volatility3 Linux Plugins + +```bash +# Check syscall table for hooks +vol -f memory.lime linux.check_syscall.Check_syscall + +# List loaded kernel modules +vol -f memory.lime linux.lsmod.Lsmod + +# Detect hidden kernel modules +vol -f memory.lime linux.hidden_modules.Hidden_modules + +# Check IDT for hooks +vol -f memory.lime linux.check_idt.Check_idt + +# List processes (detect hidden) +vol -f memory.lime linux.pslist.PsList +vol -f memory.lime linux.pstree.PsTree + +# Check for modified cred structures +vol -f memory.lime linux.check_creds.Check_creds + +# Network connections +vol -f memory.lime linux.sockstat.Sockstat + +# JSON output +vol -f memory.lime linux.check_syscall.Check_syscall -r json > syscalls.json +``` + +## Memory Acquisition Tools + +| Tool | Command | Use Case | +|------|---------|----------| +| LiME | `insmod lime.ko "path=/tmp/mem.lime format=lime"` | Linux kernel module | +| AVML | `avml /tmp/memory.raw` | Azure/cloud instances | +| /proc/kcore | `dd if=/proc/kcore of=mem.raw` | Quick (partial) dump | + +## Volatility3 Symbol Tables (ISF) + +```bash +# Generate ISF from running kernel +vol -f memory.lime banners.Banners +# Download matching ISF from: +# https://github.com/volatilityfoundation/volatility3#symbol-tables +``` + +## rkhunter Commands + +```bash +# Full system scan +rkhunter --check --skip-keypress --report-warnings-only + +# Update signatures +rkhunter --update + +# Check specific tests +rkhunter --check --enable rootkits,trojans,os_specific + +# Output to log file +rkhunter --check --logfile /var/log/rkhunter.log +``` + +## Known Linux Rootkits Detected + +| Rootkit | Technique | Volatility Plugin | +|---------|-----------|-------------------| +| Diamorphine | Hidden module + syscall hook | check_syscall, hidden_modules | +| Reptile | Syscall hook + port knocking | check_syscall | +| KBeast | Syscall hook + /proc hiding | check_syscall, hidden_modules | +| Adore-ng | VFS hook + hidden files | lsmod, check_syscall | +| Jynx2 | LD_PRELOAD userspace | pslist (parent check) | + +## Cross-View Detection + +```bash +# Compare /proc/modules vs /sys/module +diff <(cat /proc/modules | awk '{print $1}' | sort) \ + <(ls /sys/module/ | sort) + +# Check for hidden processes +diff <(ls /proc/ | grep -E '^[0-9]+$' | sort -n) \ + <(ps -eo pid --no-headers | sort -n) +``` + +### References + +- Volatility3 Linux Plugins: https://volatility3.readthedocs.io/en/latest/volatility3.plugins.linux.html +- LiME: https://github.com/504ensicsLabs/LiME +- rkhunter: http://rkhunter.sourceforge.net/ +- MITRE T1014 Rootkit: https://attack.mitre.org/techniques/T1014/ diff --git a/personas/_shared/skills/analyzing-linux-kernel-rootkits/scripts/agent.py b/personas/_shared/skills/analyzing-linux-kernel-rootkits/scripts/agent.py new file mode 100644 index 0000000..6627a27 --- /dev/null +++ b/personas/_shared/skills/analyzing-linux-kernel-rootkits/scripts/agent.py @@ -0,0 +1,176 @@ +#!/usr/bin/env python3 +"""Linux Kernel Rootkit Detection Agent - analyzes memory dumps with Volatility3 and live system with rkhunter.""" + +import json +import argparse +import logging +import subprocess +import os +from datetime import datetime + +logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s") +logger = logging.getLogger(__name__) + + +def run_vol3_plugin(memory_dump, plugin, isf_url=None): + """Run a Volatility3 Linux plugin and return parsed output.""" + cmd = ["vol", "-f", memory_dump, plugin, "-r", "json"] + if isf_url: + cmd.extend(["--isf", isf_url]) + result = subprocess.run(cmd, capture_output=True, text=True, timeout=600) + try: + return json.loads(result.stdout) if result.stdout else [] + except json.JSONDecodeError: + logger.error("Volatility3 %s output parse failed", plugin) + return [] + + +def check_syscall_hooks(memory_dump, isf_url=None): + """Detect hooked system calls using linux.check_syscall.""" + results = run_vol3_plugin(memory_dump, "linux.check_syscall.Check_syscall", isf_url) + hooked = [] + for entry in results: + row = entry.get("__children", [entry]) if isinstance(entry, dict) else [entry] + for item in row: + symbol = item.get("Symbol", item.get("symbol", "")) + module = item.get("Module", item.get("module", "")) + if module and module != "kernel": + hooked.append({ + "syscall_number": item.get("Index", item.get("index", "")), + "expected_handler": symbol, + "actual_module": module, + "severity": "critical", + "indicator": "syscall_hook", + }) + return hooked + + +def detect_hidden_modules(memory_dump, isf_url=None): + """Detect hidden kernel modules using cross-view analysis.""" + lsmod_results = run_vol3_plugin(memory_dump, "linux.lsmod.Lsmod", isf_url) + hidden_results = run_vol3_plugin(memory_dump, "linux.hidden_modules.Hidden_modules", isf_url) + lsmod_names = set() + for entry in lsmod_results: + name = entry.get("Name", entry.get("name", "")) + if name: + lsmod_names.add(name) + hidden = [] + for entry in hidden_results: + name = entry.get("Name", entry.get("name", "")) + if name: + hidden.append({ + "module_name": name, + "in_lsmod": name in lsmod_names, + "severity": "critical", + "indicator": "hidden_kernel_module", + "detail": f"Module '{name}' hidden from standard listing", + }) + return hidden + + +def check_idt_hooks(memory_dump, isf_url=None): + """Check Interrupt Descriptor Table for hooks.""" + results = run_vol3_plugin(memory_dump, "linux.check_idt.Check_idt", isf_url) + hooked = [] + for entry in results: + module = entry.get("Module", entry.get("module", "")) + if module and module != "kernel": + hooked.append({ + "interrupt": entry.get("Index", ""), + "handler_module": module, + "severity": "critical", + "indicator": "idt_hook", + }) + return hooked + + +def run_rkhunter(): + """Run rkhunter rootkit scanner on live system.""" + cmd = ["rkhunter", "--check", "--skip-keypress", "--report-warnings-only", "--nocolors"] + result = subprocess.run(cmd, capture_output=True, text=True, timeout=300) + findings = [] + for line in result.stdout.split("\n"): + line = line.strip() + if "Warning:" in line or "[ Warning ]" in line: + findings.append({ + "tool": "rkhunter", + "finding": line.replace("Warning:", "").strip(), + "severity": "high", + }) + return findings + + +def check_proc_sys_discrepancy(): + """Compare /proc/modules with /sys/module for hidden modules.""" + findings = [] + proc_modules = set() + sys_modules = set() + try: + with open("/proc/modules") as f: + for line in f: + proc_modules.add(line.split()[0]) + except (FileNotFoundError, PermissionError): + return findings + try: + sys_modules = set(os.listdir("/sys/module")) + except (FileNotFoundError, PermissionError): + return findings + only_in_sys = sys_modules - proc_modules + for mod in only_in_sys: + if not os.path.exists(f"/sys/module/{mod}/initstate"): + continue + findings.append({ + "module": mod, "indicator": "proc_sys_discrepancy", + "severity": "high", + "detail": f"Module '{mod}' in /sys/module but missing from /proc/modules", + }) + return findings + + +def generate_report(syscall_hooks, hidden_mods, idt_hooks, rkhunter_findings, proc_findings, source): + all_findings = syscall_hooks + hidden_mods + idt_hooks + rkhunter_findings + proc_findings + critical = sum(1 for f in all_findings if f.get("severity") == "critical") + return { + "timestamp": datetime.utcnow().isoformat(), + "analysis_source": source, + "syscall_hooks": syscall_hooks, + "hidden_modules": hidden_mods, + "idt_hooks": idt_hooks, + "rkhunter_warnings": rkhunter_findings, + "proc_sys_discrepancies": proc_findings, + "total_findings": len(all_findings), + "critical_findings": critical, + "rootkit_detected": critical > 0, + } + + +def main(): + parser = argparse.ArgumentParser(description="Linux Kernel Rootkit Detection Agent") + parser.add_argument("--memory-dump", help="Path to Linux memory dump for Volatility3 analysis") + parser.add_argument("--isf-url", help="Volatility3 ISF symbol table URL") + parser.add_argument("--live-scan", action="store_true", help="Run rkhunter + /proc analysis on live system") + parser.add_argument("--output", default="rootkit_detection_report.json") + args = parser.parse_args() + + syscall_hooks, hidden_mods, idt_hooks = [], [], [] + rkhunter_findings, proc_findings = [], [] + source = "none" + if args.memory_dump: + source = f"memory_dump:{args.memory_dump}" + syscall_hooks = check_syscall_hooks(args.memory_dump, args.isf_url) + hidden_mods = detect_hidden_modules(args.memory_dump, args.isf_url) + idt_hooks = check_idt_hooks(args.memory_dump, args.isf_url) + if args.live_scan: + source = "live_system" if source == "none" else source + "+live_system" + rkhunter_findings = run_rkhunter() + proc_findings = check_proc_sys_discrepancy() + report = generate_report(syscall_hooks, hidden_mods, idt_hooks, rkhunter_findings, proc_findings, source) + with open(args.output, "w") as f: + json.dump(report, f, indent=2, default=str) + logger.info("Rootkit scan: %d findings (%d critical), rootkit detected: %s", + report["total_findings"], report["critical_findings"], report["rootkit_detected"]) + print(json.dumps(report, indent=2, default=str)) + + +if __name__ == "__main__": + main() diff --git a/personas/_shared/skills/analyzing-linux-system-artifacts/LICENSE b/personas/_shared/skills/analyzing-linux-system-artifacts/LICENSE new file mode 100644 index 0000000..d885118 --- /dev/null +++ b/personas/_shared/skills/analyzing-linux-system-artifacts/LICENSE @@ -0,0 +1,201 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to the Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by the Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding any notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. Please do not remove or change + the license header comment from a contributed file except when + necessary. + + Copyright 2026 mukul975 + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/personas/_shared/skills/analyzing-linux-system-artifacts/SKILL.md b/personas/_shared/skills/analyzing-linux-system-artifacts/SKILL.md new file mode 100644 index 0000000..1bff8d2 --- /dev/null +++ b/personas/_shared/skills/analyzing-linux-system-artifacts/SKILL.md @@ -0,0 +1,332 @@ +--- +name: analyzing-linux-system-artifacts +description: Examine Linux system artifacts including auth logs, cron jobs, shell history, and system configuration to uncover + evidence of compromise or unauthorized activity. +domain: cybersecurity +subdomain: digital-forensics +tags: +- forensics +- linux-forensics +- system-artifacts +- log-analysis +- persistence-detection +- incident-investigation +version: '1.0' +author: mahipal +license: Apache-2.0 +nist_csf: +- RS.AN-01 +- RS.AN-03 +- DE.AE-02 +- RS.MA-01 +--- + +# Analyzing Linux System Artifacts + +## When to Use +- When investigating a compromised Linux server or workstation +- For identifying persistence mechanisms (cron, systemd, SSH keys) +- When tracing user activity through shell history and authentication logs +- During incident response to determine the scope of a Linux-based breach +- For detecting rootkits, backdoors, and unauthorized modifications + +## Prerequisites +- Forensic image or live access to the Linux system (read-only) +- Understanding of Linux file system hierarchy (FHS) +- Knowledge of common Linux logging locations (/var/log/) +- Tools: chkrootkit, rkhunter, AIDE, auditd logs +- Familiarity with systemd, cron, and PAM configurations +- Root access for complete artifact collection + +## Workflow + +### Step 1: Mount and Collect System Artifacts + +```bash +# Mount forensic image read-only +mount -o ro,loop,offset=$((2048*512)) /cases/case-2024-001/images/linux_evidence.dd /mnt/evidence + +# Create collection directories +mkdir -p /cases/case-2024-001/linux/{logs,config,users,persistence,network} + +# Collect authentication logs +cp /mnt/evidence/var/log/auth.log* /cases/case-2024-001/linux/logs/ +cp /mnt/evidence/var/log/secure* /cases/case-2024-001/linux/logs/ +cp /mnt/evidence/var/log/syslog* /cases/case-2024-001/linux/logs/ +cp /mnt/evidence/var/log/kern.log* /cases/case-2024-001/linux/logs/ +cp /mnt/evidence/var/log/audit/audit.log* /cases/case-2024-001/linux/logs/ +cp /mnt/evidence/var/log/wtmp /cases/case-2024-001/linux/logs/ +cp /mnt/evidence/var/log/btmp /cases/case-2024-001/linux/logs/ +cp /mnt/evidence/var/log/lastlog /cases/case-2024-001/linux/logs/ +cp /mnt/evidence/var/log/faillog /cases/case-2024-001/linux/logs/ + +# Collect user artifacts +for user_dir in /mnt/evidence/home/*/; do + username=$(basename "$user_dir") + mkdir -p /cases/case-2024-001/linux/users/$username + cp "$user_dir"/.bash_history /cases/case-2024-001/linux/users/$username/ 2>/dev/null + cp "$user_dir"/.zsh_history /cases/case-2024-001/linux/users/$username/ 2>/dev/null + cp -r "$user_dir"/.ssh/ /cases/case-2024-001/linux/users/$username/ 2>/dev/null + cp "$user_dir"/.bashrc /cases/case-2024-001/linux/users/$username/ 2>/dev/null + cp "$user_dir"/.profile /cases/case-2024-001/linux/users/$username/ 2>/dev/null + cp "$user_dir"/.viminfo /cases/case-2024-001/linux/users/$username/ 2>/dev/null + cp "$user_dir"/.wget-hsts /cases/case-2024-001/linux/users/$username/ 2>/dev/null + cp "$user_dir"/.python_history /cases/case-2024-001/linux/users/$username/ 2>/dev/null +done + +# Collect root user artifacts +cp /mnt/evidence/root/.bash_history /cases/case-2024-001/linux/users/root/ 2>/dev/null +cp -r /mnt/evidence/root/.ssh/ /cases/case-2024-001/linux/users/root/ 2>/dev/null + +# Collect system configuration +cp /mnt/evidence/etc/passwd /cases/case-2024-001/linux/config/ +cp /mnt/evidence/etc/shadow /cases/case-2024-001/linux/config/ +cp /mnt/evidence/etc/group /cases/case-2024-001/linux/config/ +cp /mnt/evidence/etc/sudoers /cases/case-2024-001/linux/config/ +cp -r /mnt/evidence/etc/sudoers.d/ /cases/case-2024-001/linux/config/ +cp /mnt/evidence/etc/hosts /cases/case-2024-001/linux/config/ +cp /mnt/evidence/etc/resolv.conf /cases/case-2024-001/linux/config/ +cp -r /mnt/evidence/etc/ssh/ /cases/case-2024-001/linux/config/ +``` + +### Step 2: Analyze User Accounts and Authentication + +```bash +# Analyze user accounts for anomalies +python3 << 'PYEOF' +print("=== USER ACCOUNT ANALYSIS ===\n") + +# Parse /etc/passwd +with open('/cases/case-2024-001/linux/config/passwd') as f: + for line in f: + parts = line.strip().split(':') + if len(parts) >= 7: + username, _, uid, gid, comment, home, shell = parts[0], parts[1], int(parts[2]), int(parts[3]), parts[4], parts[5], parts[6] + + # Flag accounts with UID 0 (root equivalent) + if uid == 0 and username != 'root': + print(f" ALERT: UID 0 account: {username} (shell: {shell})") + + # Flag accounts with login shells that shouldn't have them + if shell not in ('/bin/false', '/usr/sbin/nologin', '/bin/sync') and uid >= 1000: + print(f" User: {username} (UID:{uid}, Shell:{shell}, Home:{home})") + + # Flag system accounts with login shells + if uid < 1000 and uid > 0 and shell in ('/bin/bash', '/bin/sh', '/bin/zsh'): + print(f" WARNING: System account with shell: {username} (UID:{uid}, Shell:{shell})") + +# Parse /etc/shadow for account status +print("\n=== PASSWORD STATUS ===") +with open('/cases/case-2024-001/linux/config/shadow') as f: + for line in f: + parts = line.strip().split(':') + if len(parts) >= 3: + username = parts[0] + pwd_hash = parts[1] + last_change = parts[2] + + if pwd_hash and pwd_hash not in ('*', '!', '!!', ''): + hash_type = 'Unknown' + if pwd_hash.startswith('$6$'): hash_type = 'SHA-512' + elif pwd_hash.startswith('$5$'): hash_type = 'SHA-256' + elif pwd_hash.startswith('$y$'): hash_type = 'yescrypt' + elif pwd_hash.startswith('$1$'): hash_type = 'MD5 (WEAK)' + print(f" {username}: {hash_type} hash, last changed: day {last_change}") +PYEOF + +# Analyze login history +last -f /cases/case-2024-001/linux/logs/wtmp > /cases/case-2024-001/linux/analysis/login_history.txt +lastb -f /cases/case-2024-001/linux/logs/btmp > /cases/case-2024-001/linux/analysis/failed_logins.txt 2>/dev/null +``` + +### Step 3: Examine Persistence Mechanisms + +```bash +# Check cron jobs for all users +echo "=== CRON JOBS ===" > /cases/case-2024-001/linux/persistence/cron_analysis.txt + +# System cron +for cronfile in /mnt/evidence/etc/crontab /mnt/evidence/etc/cron.d/*; do + echo "--- $cronfile ---" >> /cases/case-2024-001/linux/persistence/cron_analysis.txt + cat "$cronfile" 2>/dev/null >> /cases/case-2024-001/linux/persistence/cron_analysis.txt + echo "" >> /cases/case-2024-001/linux/persistence/cron_analysis.txt +done + +# User cron tabs +for cronfile in /mnt/evidence/var/spool/cron/crontabs/*; do + echo "--- User crontab: $(basename $cronfile) ---" >> /cases/case-2024-001/linux/persistence/cron_analysis.txt + cat "$cronfile" 2>/dev/null >> /cases/case-2024-001/linux/persistence/cron_analysis.txt + echo "" >> /cases/case-2024-001/linux/persistence/cron_analysis.txt +done + +# Check systemd services for persistence +echo "=== SYSTEMD SERVICES ===" > /cases/case-2024-001/linux/persistence/systemd_analysis.txt +find /mnt/evidence/etc/systemd/system/ -name "*.service" -newer /mnt/evidence/etc/os-release \ + >> /cases/case-2024-001/linux/persistence/systemd_analysis.txt + +for svc in /mnt/evidence/etc/systemd/system/*.service; do + echo "--- $(basename $svc) ---" >> /cases/case-2024-001/linux/persistence/systemd_analysis.txt + cat "$svc" >> /cases/case-2024-001/linux/persistence/systemd_analysis.txt + echo "" >> /cases/case-2024-001/linux/persistence/systemd_analysis.txt +done + +# Check authorized SSH keys (backdoor detection) +echo "=== SSH AUTHORIZED KEYS ===" > /cases/case-2024-001/linux/persistence/ssh_keys.txt +find /mnt/evidence/home/ /mnt/evidence/root/ -name "authorized_keys" -exec sh -c \ + 'echo "--- {} ---"; cat {}; echo ""' \; >> /cases/case-2024-001/linux/persistence/ssh_keys.txt + +# Check rc.local and init scripts +cat /mnt/evidence/etc/rc.local 2>/dev/null > /cases/case-2024-001/linux/persistence/rc_local.txt + +# Check /etc/profile.d/ for login-triggered scripts +ls -la /mnt/evidence/etc/profile.d/ > /cases/case-2024-001/linux/persistence/profile_scripts.txt + +# Check for LD_PRELOAD hijacking +grep -r "LD_PRELOAD" /mnt/evidence/etc/ 2>/dev/null > /cases/case-2024-001/linux/persistence/ld_preload.txt +cat /mnt/evidence/etc/ld.so.preload 2>/dev/null >> /cases/case-2024-001/linux/persistence/ld_preload.txt +``` + +### Step 4: Analyze Shell History and Command Execution + +```bash +# Analyze bash history for each user +python3 << 'PYEOF' +import os, glob + +print("=== SHELL HISTORY ANALYSIS ===\n") + +suspicious_commands = [ + 'wget', 'curl', 'nc ', 'ncat', 'netcat', 'python -c', 'python3 -c', + 'perl -e', 'base64', 'chmod 777', 'chmod +s', '/dev/tcp', '/dev/udp', + 'nmap', 'masscan', 'hydra', 'john', 'hashcat', 'passwd', 'useradd', + 'iptables -F', 'ufw disable', 'history -c', 'rm -rf /', 'dd if=', + 'crontab', 'at ', 'systemctl enable', 'ssh-keygen', 'scp ', 'rsync', + 'tar czf', 'zip -r', 'openssl enc', 'gpg --encrypt', 'shred', + 'chattr', 'setfacl', 'awk', '/tmp/', '/dev/shm/' +] + +for hist_file in glob.glob('/cases/case-2024-001/linux/users/*/.bash_history'): + username = hist_file.split('/')[-2] + print(f"User: {username}") + + with open(hist_file, 'r', errors='ignore') as f: + lines = f.readlines() + + print(f" Total commands: {len(lines)}") + flagged = [] + for i, line in enumerate(lines): + line = line.strip() + for cmd in suspicious_commands: + if cmd in line.lower(): + flagged.append((i+1, line)) + break + + if flagged: + print(f" Suspicious commands: {len(flagged)}") + for lineno, cmd in flagged: + print(f" Line {lineno}: {cmd[:120]}") + print() +PYEOF +``` + +### Step 5: Check for Rootkits and Modified Binaries + +```bash +# Check for known rootkit indicators +# Compare system binary hashes against known-good +find /mnt/evidence/usr/bin/ /mnt/evidence/usr/sbin/ /mnt/evidence/bin/ /mnt/evidence/sbin/ \ + -type f -executable -exec sha256sum {} \; > /cases/case-2024-001/linux/analysis/binary_hashes.txt + +# Check for SUID/SGID binaries (potential privilege escalation) +find /mnt/evidence/ -perm -4000 -type f 2>/dev/null > /cases/case-2024-001/linux/analysis/suid_files.txt +find /mnt/evidence/ -perm -2000 -type f 2>/dev/null > /cases/case-2024-001/linux/analysis/sgid_files.txt + +# Check for suspicious files in /tmp and /dev/shm +find /mnt/evidence/tmp/ /mnt/evidence/dev/shm/ -type f 2>/dev/null \ + -exec file {} \; > /cases/case-2024-001/linux/analysis/tmp_files.txt + +# Check for hidden files and directories +find /mnt/evidence/ -name ".*" -not -path "*/\." -type f 2>/dev/null | \ + head -100 > /cases/case-2024-001/linux/analysis/hidden_files.txt + +# Check kernel modules +ls -la /mnt/evidence/lib/modules/$(ls /mnt/evidence/lib/modules/ | head -1)/extra/ 2>/dev/null \ + > /cases/case-2024-001/linux/analysis/extra_modules.txt + +# Check for modified PAM configuration (authentication backdoors) +diff /mnt/evidence/etc/pam.d/ /cases/baseline/pam.d/ 2>/dev/null \ + > /cases/case-2024-001/linux/analysis/pam_changes.txt +``` + +## Key Concepts + +| Concept | Description | +|---------|-------------| +| /var/log/auth.log | Primary authentication log on Debian/Ubuntu systems | +| /var/log/secure | Primary authentication log on RHEL/CentOS systems | +| wtmp/btmp | Binary logs recording successful and failed login sessions | +| .bash_history | User command history file (can be cleared by attackers) | +| crontab | Scheduled task system commonly used for persistence | +| authorized_keys | SSH public keys granting passwordless access to an account | +| SUID bit | File permission allowing execution as the file owner (privilege escalation vector) | +| LD_PRELOAD | Environment variable that loads a shared library before all others (hooking technique) | + +## Tools & Systems + +| Tool | Purpose | +|------|---------| +| chkrootkit | Rootkit detection scanner for Linux systems | +| rkhunter | Rootkit Hunter - checks for rootkits, backdoors, and local exploits | +| AIDE | Advanced Intrusion Detection Environment - file integrity monitor | +| auditd | Linux audit framework for system call and file access monitoring | +| last/lastb | Parse wtmp/btmp for login and failed login history | +| Plaso/log2timeline | Super-timeline creation including Linux artifacts | +| osquery | SQL-based system querying for live forensic investigation | +| Velociraptor | Endpoint agent with Linux artifact collection capabilities | + +## Common Scenarios + +**Scenario 1: SSH Brute Force Followed by Compromise** +Analyze auth.log for failed SSH attempts followed by success, identify the attacking IP, check .bash_history for post-compromise commands, examine authorized_keys for added backdoor keys, check crontab for persistence, review network connections. + +**Scenario 2: Web Server Compromise via Application Vulnerability** +Examine web server access and error logs for exploitation attempts, check /tmp and /dev/shm for webshells, analyze the web server user's activity (www-data), check for privilege escalation via SUID binaries or kernel exploits, review outbound connections. + +**Scenario 3: Insider Threat on Database Server** +Analyze the suspect user's bash_history for database dump commands, check for large tar/zip files in home directory or /tmp, examine scp/rsync commands for data transfer, review cron jobs for automated exfiltration, check USB device logs. + +**Scenario 4: Crypto-Miner on Cloud Instance** +Check for high-CPU processes in /proc (live) or systemd service files, examine crontab entries for miner restart scripts, check /tmp for mining binaries, analyze network connections for mining pool communications, review authorized_keys for attacker access. + +## Output Format + +``` +Linux Forensics Summary: + System: webserver01 (Ubuntu 22.04 LTS) + Hostname: webserver01.corp.local + Kernel: 5.15.0-91-generic + + User Accounts: + Total: 25 (3 with UID 0 - 1 ANOMALOUS) + Interactive shells: 8 users + Recently created: admin2 (created 2024-01-15) + + Authentication Events: + Successful SSH logins: 456 + Failed SSH attempts: 12,345 (from 23 unique IPs) + Sudo executions: 89 + + Persistence Mechanisms Found: + Cron jobs: 3 suspicious (reverse shell, miner restart) + Systemd services: 1 unknown (update-checker.service) + SSH keys: 2 unauthorized keys in root authorized_keys + rc.local: Modified with download cradle + + Suspicious Activity: + - bash_history contains wget to pastebin URL + - SUID binary /tmp/.hidden/escalate found + - /dev/shm/ contains compiled ELF binary + - LD_PRELOAD in /etc/ld.so.preload pointing to /lib/.hidden.so + + Report: /cases/case-2024-001/linux/analysis/ +``` diff --git a/personas/_shared/skills/analyzing-linux-system-artifacts/references/api-reference.md b/personas/_shared/skills/analyzing-linux-system-artifacts/references/api-reference.md new file mode 100644 index 0000000..c1de815 --- /dev/null +++ b/personas/_shared/skills/analyzing-linux-system-artifacts/references/api-reference.md @@ -0,0 +1,114 @@ +# API Reference: Linux Forensic Artifact Analysis Tools + +## Key Artifact Locations + +| Artifact | Path | Description | +|----------|------|-------------| +| Auth logs | `/var/log/auth.log` (Debian) `/var/log/secure` (RHEL) | Authentication events | +| Login history | `/var/log/wtmp` | Successful logins (binary, use `last`) | +| Failed logins | `/var/log/btmp` | Failed logins (binary, use `lastb`) | +| Bash history | `~/.bash_history` | Command history per user | +| SSH keys | `~/.ssh/authorized_keys` | Authorized public keys | +| Crontab | `/etc/crontab`, `/var/spool/cron/crontabs/` | Scheduled tasks | +| Systemd services | `/etc/systemd/system/` | Service definitions | +| LD_PRELOAD | `/etc/ld.so.preload` | Shared library preloading | +| SUID binaries | `find / -perm -4000` | Setuid executables | + +## last / lastb - Login History + +### Syntax +```bash +last -f /var/log/wtmp # Successful logins +lastb -f /var/log/btmp # Failed logins +last -i -f /var/log/wtmp # Show IP addresses +last -s 2024-01-15 -t 2024-01-20 # Date range filter +``` + +### Output Format +``` +user pts/0 192.168.1.50 Mon Jan 15 09:00 still logged in +``` + +## chkrootkit - Rootkit Scanner + +### Syntax +```bash +chkrootkit # Full scan +chkrootkit -r /mnt/evidence # Scan mounted evidence +chkrootkit -q # Quiet (infected only) +``` + +## rkhunter - Rootkit Hunter + +### Syntax +```bash +rkhunter --check # Full system check +rkhunter --check --rootdir /mnt/ev # Check evidence root +rkhunter --list tests # List available tests +rkhunter --propupd # Update file properties DB +``` + +### Check Categories +| Check | Description | +|-------|-------------| +| `rootkits` | Known rootkit signatures | +| `trojans` | Trojanized system binaries | +| `properties` | File permission anomalies | +| `filesystem` | Hidden files and directories | + +## auditd Log Parsing + +### ausearch Syntax +```bash +ausearch -m execve -ts recent # Recent command execution +ausearch -m USER_AUTH -ts today # Authentication events +ausearch -k suspicious_activity # Custom audit rule key +ausearch -ua 0 -ts today # Root user actions +``` + +### aureport Syntax +```bash +aureport --auth # Authentication summary +aureport --login # Login summary +aureport --file # File access summary +aureport --summary # Overall summary +``` + +## osquery - SQL-based System Queries + +### Syntax +```bash +osqueryi "SELECT * FROM users WHERE uid = 0" +osqueryi "SELECT * FROM crontab" +osqueryi "SELECT * FROM authorized_keys" +osqueryi "SELECT * FROM suid_bin" +osqueryi "SELECT * FROM process_open_sockets" +``` + +### Key Tables +| Table | Content | +|-------|---------| +| `users` | User account information | +| `crontab` | Cron job entries | +| `authorized_keys` | SSH authorized keys | +| `suid_bin` | SUID binaries | +| `process_open_sockets` | Network connections by process | +| `shell_history` | Command history entries | + +## Plaso / log2timeline - Super Timeline + +### Syntax +```bash +log2timeline.py /cases/timeline.plaso /mnt/evidence +psort.py -o l2tcsv /cases/timeline.plaso > timeline.csv +psort.py -o l2tcsv /cases/timeline.plaso "date > '2024-01-15'" +``` + +## AIDE - File Integrity + +### Syntax +```bash +aide --init # Initialize database +aide --check # Check for changes +aide --compare # Compare databases +``` diff --git a/personas/_shared/skills/analyzing-linux-system-artifacts/scripts/agent.py b/personas/_shared/skills/analyzing-linux-system-artifacts/scripts/agent.py new file mode 100644 index 0000000..118f5bc --- /dev/null +++ b/personas/_shared/skills/analyzing-linux-system-artifacts/scripts/agent.py @@ -0,0 +1,263 @@ +#!/usr/bin/env python3 +"""Linux system artifact forensics agent for investigating compromised systems.""" + +import os +import sys +import glob +import shlex +import subprocess + + +def run_cmd(cmd): + """Execute a command and return output.""" + if isinstance(cmd, str): + cmd = shlex.split(cmd) + result = subprocess.run(cmd, capture_output=True, text=True, timeout=30) + return result.stdout.strip(), result.stderr.strip(), result.returncode + + +def analyze_passwd(passwd_path): + """Analyze /etc/passwd for suspicious accounts.""" + findings = [] + with open(passwd_path, "r") as f: + for line in f: + parts = line.strip().split(":") + if len(parts) < 7: + continue + username, _, uid, gid = parts[0], parts[1], int(parts[2]), int(parts[3]) + home, shell = parts[5], parts[6] + if uid == 0 and username != "root": + findings.append({ + "severity": "CRITICAL", + "finding": f"UID 0 account: {username} (shell: {shell})", + }) + login_shells = ["/bin/bash", "/bin/sh", "/bin/zsh", "/usr/bin/zsh"] + if uid < 1000 and uid > 0 and shell in login_shells: + findings.append({ + "severity": "WARNING", + "finding": f"System account with login shell: {username} (UID:{uid})", + }) + if uid >= 1000 and shell not in ["/bin/false", "/usr/sbin/nologin", "/bin/sync"]: + findings.append({ + "severity": "INFO", + "finding": f"Interactive user: {username} (UID:{uid}, Home:{home})", + }) + return findings + + +def analyze_shadow(shadow_path): + """Analyze /etc/shadow for password hash types and status.""" + findings = [] + with open(shadow_path, "r") as f: + for line in f: + parts = line.strip().split(":") + if len(parts) < 3: + continue + username = parts[0] + pwd_hash = parts[1] + if pwd_hash and pwd_hash not in ("*", "!", "!!", ""): + hash_type = "Unknown" + if pwd_hash.startswith("$6$"): + hash_type = "SHA-512" + elif pwd_hash.startswith("$5$"): + hash_type = "SHA-256" + elif pwd_hash.startswith("$y$"): + hash_type = "yescrypt" + elif pwd_hash.startswith("$1$"): + hash_type = "MD5 (WEAK)" + findings.append({ + "severity": "WARNING", + "finding": f"{username} uses weak MD5 password hash", + }) + findings.append({ + "severity": "INFO", + "finding": f"{username}: {hash_type} hash, last changed day {parts[2]}", + }) + return findings + + +def analyze_bash_history(history_path, username="unknown"): + """Analyze bash history for suspicious commands.""" + suspicious_patterns = [ + "wget", "curl", "nc ", "ncat", "netcat", "python -c", "python3 -c", + "perl -e", "base64", "chmod 777", "chmod +s", "/dev/tcp", "/dev/udp", + "nmap", "masscan", "hydra", "john", "hashcat", "passwd", "useradd", + "iptables -F", "ufw disable", "history -c", "rm -rf", "dd if=", + "crontab", "systemctl enable", "ssh-keygen", "scp ", "rsync", + "/tmp/", "/dev/shm/", "mkfifo", "socat", + ] + findings = [] + with open(history_path, "r", errors="ignore") as f: + lines = f.readlines() + for i, line in enumerate(lines): + line_stripped = line.strip() + for pattern in suspicious_patterns: + if pattern in line_stripped.lower(): + findings.append({ + "user": username, + "line_number": i + 1, + "command": line_stripped[:200], + "matched_pattern": pattern, + }) + break + return findings + + +def check_cron_persistence(evidence_root): + """Check cron jobs for persistence mechanisms.""" + findings = [] + cron_paths = [ + os.path.join(evidence_root, "etc/crontab"), + *glob.glob(os.path.join(evidence_root, "etc/cron.d/*")), + *glob.glob(os.path.join(evidence_root, "var/spool/cron/crontabs/*")), + ] + for cron_path in cron_paths: + if os.path.exists(cron_path) and os.path.isfile(cron_path): + with open(cron_path, "r", errors="ignore") as f: + for line in f: + line = line.strip() + if line and not line.startswith("#"): + suspicious = any( + p in line.lower() + for p in ["wget", "curl", "/tmp/", "/dev/shm/", "base64", + "python", "bash -i", "reverse", "nc ", "ncat"] + ) + if suspicious: + findings.append({ + "severity": "HIGH", + "source": cron_path, + "entry": line[:200], + }) + return findings + + +def check_ssh_keys(evidence_root): + """Check for unauthorized SSH authorized_keys.""" + findings = [] + key_files = glob.glob( + os.path.join(evidence_root, "home/*/.ssh/authorized_keys") + ) + glob.glob( + os.path.join(evidence_root, "root/.ssh/authorized_keys") + ) + for key_file in key_files: + if os.path.exists(key_file): + with open(key_file, "r") as f: + keys = [l.strip() for l in f if l.strip() and not l.startswith("#")] + if keys: + findings.append({ + "file": key_file, + "key_count": len(keys), + "keys": [k[:80] + "..." for k in keys], + }) + return findings + + +def check_systemd_persistence(evidence_root): + """Check for suspicious systemd service files.""" + findings = [] + service_dirs = [ + os.path.join(evidence_root, "etc/systemd/system"), + os.path.join(evidence_root, "usr/lib/systemd/system"), + ] + for svc_dir in service_dirs: + if not os.path.exists(svc_dir): + continue + for svc_file in glob.glob(os.path.join(svc_dir, "*.service")): + with open(svc_file, "r", errors="ignore") as f: + content = f.read() + suspicious = any( + p in content.lower() + for p in ["/tmp/", "/dev/shm/", "wget", "curl", "reverse", + "bash -i", "nc ", "python", "base64"] + ) + if suspicious: + findings.append({ + "severity": "HIGH", + "file": svc_file, + "preview": content[:300], + }) + return findings + + +def check_ld_preload(evidence_root): + """Check for LD_PRELOAD rootkit indicators.""" + findings = [] + preload_path = os.path.join(evidence_root, "etc/ld.so.preload") + if os.path.exists(preload_path): + with open(preload_path, "r") as f: + content = f.read().strip() + if content: + findings.append({ + "severity": "CRITICAL", + "finding": f"/etc/ld.so.preload contains: {content}", + }) + return findings + + +def find_suid_binaries(evidence_root): + """Find SUID/SGID binaries (potential privilege escalation).""" + result = subprocess.run( + ["find", evidence_root, "-perm", "-4000", "-type", "f"], + capture_output=True, text=True, timeout=30 + ) + stdout = result.stdout.strip() + return stdout.splitlines() if result.returncode == 0 and stdout else [] + + +def find_suspicious_tmp_files(evidence_root): + """Find suspicious files in /tmp and /dev/shm.""" + findings = [] + for tmp_dir in ["tmp", "dev/shm"]: + full_path = os.path.join(evidence_root, tmp_dir) + if os.path.exists(full_path): + for root, dirs, files in os.walk(full_path): + for fname in files: + fpath = os.path.join(root, fname) + findings.append(fpath) + return findings + + +if __name__ == "__main__": + print("=" * 60) + print("Linux System Artifacts Forensics Agent") + print("User accounts, persistence, shell history, rootkit detection") + print("=" * 60) + + evidence_root = sys.argv[1] if len(sys.argv) > 1 else "/mnt/evidence" + + if os.path.exists(evidence_root): + print(f"\n[*] Examining evidence root: {evidence_root}") + + passwd_path = os.path.join(evidence_root, "etc/passwd") + if os.path.exists(passwd_path): + print("\n--- User Account Analysis ---") + for f in analyze_passwd(passwd_path): + print(f" [{f['severity']}] {f['finding']}") + + print("\n--- Cron Persistence ---") + cron = check_cron_persistence(evidence_root) + for c in cron: + print(f" [{c['severity']}] {c['source']}: {c['entry'][:80]}") + + print("\n--- SSH Authorized Keys ---") + ssh = check_ssh_keys(evidence_root) + for s in ssh: + print(f" {s['file']}: {s['key_count']} keys") + + print("\n--- Systemd Persistence ---") + systemd = check_systemd_persistence(evidence_root) + for s in systemd: + print(f" [{s['severity']}] {s['file']}") + + print("\n--- LD_PRELOAD Rootkit Check ---") + ld = check_ld_preload(evidence_root) + for l in ld: + print(f" [{l['severity']}] {l['finding']}") + + print("\n--- Suspicious Temp Files ---") + tmp = find_suspicious_tmp_files(evidence_root) + for t in tmp[:20]: + print(f" {t}") + else: + print(f"\n[DEMO] Usage: python agent.py ") + print("[*] Mount a forensic image and provide the path for analysis.") diff --git a/personas/_shared/skills/analyzing-lnk-file-and-jump-list-artifacts/LICENSE b/personas/_shared/skills/analyzing-lnk-file-and-jump-list-artifacts/LICENSE new file mode 100644 index 0000000..d885118 --- /dev/null +++ b/personas/_shared/skills/analyzing-lnk-file-and-jump-list-artifacts/LICENSE @@ -0,0 +1,201 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to the Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by the Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding any notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. Please do not remove or change + the license header comment from a contributed file except when + necessary. + + Copyright 2026 mukul975 + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/personas/_shared/skills/analyzing-lnk-file-and-jump-list-artifacts/SKILL.md b/personas/_shared/skills/analyzing-lnk-file-and-jump-list-artifacts/SKILL.md new file mode 100644 index 0000000..aced390 --- /dev/null +++ b/personas/_shared/skills/analyzing-lnk-file-and-jump-list-artifacts/SKILL.md @@ -0,0 +1,286 @@ +--- +name: analyzing-lnk-file-and-jump-list-artifacts +description: Analyze Windows LNK shortcut files and Jump List artifacts to establish evidence of file access, program execution, + and user activity using LECmd, JLECmd, and manual binary parsing of the Shell Link Binary format. +domain: cybersecurity +subdomain: digital-forensics +tags: +- lnk-files +- jump-lists +- lecmd +- jlecmd +- windows-forensics +- shell-link +- user-activity +- file-access +- program-execution +- recent-files +version: '1.0' +author: mahipal +license: Apache-2.0 +nist_csf: +- RS.AN-01 +- RS.AN-03 +- DE.AE-02 +- RS.MA-01 +--- + +# Analyzing LNK File and Jump List Artifacts + +## Overview + +Windows LNK (shortcut) files and Jump Lists are critical forensic artifacts that provide evidence of file access, program execution, and user behavior. LNK files are created automatically when a user opens a file through Windows Explorer or the Open/Save dialog, storing metadata about the target file including its original path, timestamps, volume serial number, NetBIOS name, and MAC address of the host system. Jump Lists, introduced in Windows 7, extend this by maintaining per-application lists of recently and frequently accessed files. These artifacts persist even after the target files are deleted, making them invaluable for establishing that a user accessed specific files at specific times. + + +## When to Use + +- When investigating security incidents that require analyzing lnk file and jump list artifacts +- When building detection rules or threat hunting queries for this domain +- When SOC analysts need structured procedures for this analysis type +- When validating security monitoring coverage for related attack techniques + +## Prerequisites + +- LECmd (Eric Zimmerman) for LNK file parsing +- JLECmd (Eric Zimmerman) for Jump List parsing +- Python 3.8+ with pylnk3 or LnkParse3 libraries +- Forensic image or triage collection from Windows system +- Timeline Explorer for CSV analysis + +## LNK File Locations + +| Location | Description | +|----------|-------------| +| `%USERPROFILE%\AppData\Roaming\Microsoft\Windows\Recent\` | Recent files accessed | +| `%USERPROFILE%\Desktop\` | User-created shortcuts | +| `%USERPROFILE%\AppData\Roaming\Microsoft\Windows\Start Menu\` | Start Menu shortcuts | +| `%USERPROFILE%\AppData\Roaming\Microsoft\Office\Recent\` | Office recent documents | + +## LNK File Structure + +### Shell Link Header (76 bytes) + +| Offset | Size | Field | +|--------|------|-------| +| 0x00 | 4 | HeaderSize (always 0x0000004C) | +| 0x04 | 16 | LinkCLSID (always 00021401-0000-0000-C000-000000000046) | +| 0x14 | 4 | LinkFlags | +| 0x18 | 4 | FileAttributes | +| 0x1C | 8 | CreationTime (FILETIME) | +| 0x24 | 8 | AccessTime (FILETIME) | +| 0x2C | 8 | WriteTime (FILETIME) | +| 0x34 | 4 | FileSize of target | +| 0x38 | 4 | IconIndex | +| 0x3C | 4 | ShowCommand | +| 0x40 | 2 | HotKey | + +### Key Forensic Fields in LNK Files + +- **Target file timestamps**: Creation, access, modification times of the referenced file +- **Volume information**: Serial number, drive type, volume label +- **Network share information**: UNC path, share name +- **Machine identifiers**: NetBIOS name, MAC address (from TrackerDataBlock) +- **Distributed Link Tracking**: Machine ID and object GUID + +## Analysis with EZ Tools + +### LECmd - LNK File Parser + +```powershell +# Parse all LNK files in Recent folder +LECmd.exe -d "C:\Evidence\Users\suspect\AppData\Roaming\Microsoft\Windows\Recent" --csv C:\Output --csvf lnk_analysis.csv + +# Parse a single LNK file with full details +LECmd.exe -f "C:\Evidence\Users\suspect\Desktop\Confidential.docx.lnk" --json C:\Output + +# Parse LNK files with additional detail levels +LECmd.exe -d "C:\Evidence\Users\suspect\AppData\Roaming\Microsoft\Windows\Recent" --csv C:\Output --csvf lnk_all.csv --all +``` + +### JLECmd - Jump List Parser + +```powershell +# Parse Automatic Jump Lists +JLECmd.exe -d "C:\Evidence\Users\suspect\AppData\Roaming\Microsoft\Windows\Recent\AutomaticDestinations" --csv C:\Output --csvf jumplists_auto.csv + +# Parse Custom Jump Lists +JLECmd.exe -d "C:\Evidence\Users\suspect\AppData\Roaming\Microsoft\Windows\Recent\CustomDestinations" --csv C:\Output --csvf jumplists_custom.csv + +# Parse all jump lists with detailed output +JLECmd.exe -d "C:\Evidence\Users\suspect\AppData\Roaming\Microsoft\Windows\Recent\AutomaticDestinations" --csv C:\Output --csvf jumplists_auto.csv --ld +``` + +## Jump List Structure + +### Automatic Destinations (automaticDestinations-ms) + +These are OLE Compound files (Structured Storage) identified by AppID hash in the filename: + +| AppID Hash | Application | +|-----------|-------------| +| 5f7b5f1e01b83767 | Windows Explorer Pinned/Frequent | +| 1b4dd67f29cb1962 | Windows Explorer Recent | +| 9b9cdc69c1c24e2b | Notepad | +| a7bd71699cd38d1c | Notepad++ | +| 12dc1ea8e34b5a6 | Microsoft Paint | +| 7e4dca80246863e3 | Control Panel | +| 1cf97c38a5881255 | Microsoft Edge | +| f01b4d95cf55d32a | Windows Explorer | +| 9d1f905ce5044aee | Microsoft Excel | +| a4a5324453625195 | Microsoft Word | +| d00655d2aa12ff6d | Microsoft PowerPoint | +| bc03160ee1a59fc1 | Outlook | + +### Custom Destinations (customDestinations-ms) + +Created when users pin items to application jump lists. These files contain sequential LNK entries. + +## Python Analysis Script + +```python +import struct +import os +from datetime import datetime, timedelta + +FILETIME_EPOCH = datetime(1601, 1, 1) + +def filetime_to_datetime(filetime_bytes: bytes) -> datetime: + """Convert Windows FILETIME (100-ns intervals since 1601) to datetime.""" + ft = struct.unpack(" dict: + """Parse the Shell Link header from an LNK file.""" + with open(lnk_path, "rb") as f: + header = f.read(76) + + header_size = struct.unpack("