mirror of
https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git
synced 2026-07-26 22:20:59 +03:00
Add folder anatomy (scripts/agent.py + references/api-reference.md) for 648 cybersecurity skills
Complete skill folder anatomy across all cybersecurity skills: - scripts/agent.py: 80-150 line Python agents using real libraries (impacket, boto3, azure-mgmt-*, kubernetes, pefile, yara, scapy, shodan, stix2, etc.) - references/api-reference.md: real API documentation with method signatures - LICENSE: MIT license for all skill folders
This commit is contained in:
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2025 Anthropic Agent Skills Contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -0,0 +1,95 @@
|
||||
# API Reference: Analyzing Security Logs with Splunk
|
||||
|
||||
## splunk-sdk (splunklib)
|
||||
|
||||
### Connection
|
||||
|
||||
```python
|
||||
import splunklib.client as client
|
||||
|
||||
service = client.connect(
|
||||
host="splunk.example.com",
|
||||
port=8089,
|
||||
username="admin",
|
||||
password="secret",
|
||||
autologin=True,
|
||||
)
|
||||
```
|
||||
|
||||
### Running Searches
|
||||
|
||||
```python
|
||||
import splunklib.results as results
|
||||
|
||||
# Blocking (synchronous) search
|
||||
job = service.jobs.create(
|
||||
"search index=windows EventCode=4625 | stats count by src_ip",
|
||||
**{"earliest_time": "-24h", "latest_time": "now", "exec_mode": "blocking"}
|
||||
)
|
||||
|
||||
# Read results as JSON
|
||||
reader = results.JSONResultsReader(job.results(output_mode="json"))
|
||||
for row in reader:
|
||||
if isinstance(row, dict):
|
||||
print(row)
|
||||
job.cancel()
|
||||
```
|
||||
|
||||
### Oneshot Search (Simple Queries)
|
||||
|
||||
```python
|
||||
result_stream = service.jobs.oneshot(
|
||||
"search index=windows EventCode=4624 | head 10",
|
||||
earliest_time="-1h",
|
||||
output_mode="json",
|
||||
)
|
||||
reader = results.JSONResultsReader(result_stream)
|
||||
```
|
||||
|
||||
### Saved Searches
|
||||
|
||||
```python
|
||||
# List saved searches
|
||||
for saved in service.saved_searches:
|
||||
print(saved.name)
|
||||
|
||||
# Run a saved search
|
||||
saved_search = service.saved_searches["My Alert"]
|
||||
job = saved_search.dispatch()
|
||||
```
|
||||
|
||||
### KV Store Lookups
|
||||
|
||||
```python
|
||||
collection = service.kvstore["threat_intel_iocs"]
|
||||
# Insert record
|
||||
collection.data.insert(json.dumps({"ip": "1.2.3.4", "threat": "C2"}))
|
||||
# Query records
|
||||
records = collection.data.query(query=json.dumps({"threat": "C2"}))
|
||||
```
|
||||
|
||||
### Key SPL Patterns for Security Analysis
|
||||
|
||||
| Pattern | SPL |
|
||||
|---------|-----|
|
||||
| Failed logons | `index=windows EventCode=4625 \| stats count by src_ip` |
|
||||
| Lateral movement | `index=windows EventCode=4624 Logon_Type=3 \| stats dc(host) by src_ip` |
|
||||
| Process creation | `index=sysmon EventCode=1 \| table _time, Image, CommandLine` |
|
||||
| C2 beaconing | `index=proxy \| timechart span=1m count by dest_ip` |
|
||||
| DNS tunneling | `index=dns \| stats count, avg(len(query)) by domain` |
|
||||
|
||||
### Splunk REST API Endpoints
|
||||
|
||||
| Endpoint | Method | Description |
|
||||
|----------|--------|-------------|
|
||||
| `/services/search/jobs` | POST | Create a new search job |
|
||||
| `/services/search/jobs/{sid}/results` | GET | Retrieve search results |
|
||||
| `/services/saved/searches` | GET | List saved searches |
|
||||
| `/services/data/indexes` | GET | List available indexes |
|
||||
| `/services/authentication/users` | GET | List Splunk users |
|
||||
|
||||
### References
|
||||
|
||||
- splunk-sdk PyPI: https://pypi.org/project/splunk-sdk/
|
||||
- Splunk REST API docs: https://docs.splunk.com/Documentation/Splunk/latest/RESTREF
|
||||
- Splunk SDK for Python: https://dev.splunk.com/enterprise/docs/devtools/python/sdk-python/
|
||||
@@ -0,0 +1,164 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Agent for analyzing security logs with Splunk using splunk-sdk."""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import json
|
||||
import time
|
||||
import argparse
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
import splunklib.client as client
|
||||
import splunklib.results as results
|
||||
|
||||
|
||||
def connect_splunk(host, port, username, password):
|
||||
"""Establish connection to Splunk instance."""
|
||||
service = client.connect(
|
||||
host=host,
|
||||
port=port,
|
||||
username=username,
|
||||
password=password,
|
||||
autologin=True,
|
||||
)
|
||||
return service
|
||||
|
||||
|
||||
def run_search(service, query, earliest="-24h", latest="now"):
|
||||
"""Execute a Splunk search and return parsed results."""
|
||||
kwargs_search = {
|
||||
"earliest_time": earliest,
|
||||
"latest_time": latest,
|
||||
"search_mode": "normal",
|
||||
"exec_mode": "blocking",
|
||||
}
|
||||
job = service.jobs.create(f"search {query}", **kwargs_search)
|
||||
reader = results.JSONResultsReader(job.results(output_mode="json"))
|
||||
rows = [row for row in reader if isinstance(row, dict)]
|
||||
job.cancel()
|
||||
return rows
|
||||
|
||||
|
||||
def detect_brute_force(service, threshold=10, earliest="-24h"):
|
||||
"""Detect brute force attacks via failed logon events (EventCode 4625)."""
|
||||
query = (
|
||||
'index=windows sourcetype="WinEventLog:Security" EventCode=4625 '
|
||||
f"| stats count as failed_attempts, dc(src_ip) as unique_sources, "
|
||||
f"values(src_ip) as source_ips by TargetUserName "
|
||||
f"| where failed_attempts > {threshold} "
|
||||
f"| sort -failed_attempts"
|
||||
)
|
||||
return run_search(service, query, earliest=earliest)
|
||||
|
||||
|
||||
def detect_lateral_movement(service, earliest="-24h"):
|
||||
"""Detect lateral movement via Type 3 network logons to multiple hosts."""
|
||||
query = (
|
||||
'index=windows sourcetype="WinEventLog:Security" EventCode=4624 '
|
||||
"Logon_Type=3 "
|
||||
"| stats dc(ComputerName) as unique_targets, values(ComputerName) as targets "
|
||||
"by TargetUserName, src_ip "
|
||||
"| where unique_targets > 3 "
|
||||
"| sort -unique_targets"
|
||||
)
|
||||
return run_search(service, query, earliest=earliest)
|
||||
|
||||
|
||||
def detect_suspicious_powershell(service, earliest="-24h"):
|
||||
"""Detect encoded or download-cradle PowerShell execution via Sysmon."""
|
||||
query = (
|
||||
'index=sysmon EventCode=1 Image="*\\\\powershell.exe" '
|
||||
'(CommandLine="*-enc*" OR CommandLine="*-encodedcommand*" '
|
||||
'OR CommandLine="*downloadstring*" OR CommandLine="*iex*") '
|
||||
"| table _time, host, User, ParentImage, CommandLine "
|
||||
"| sort _time"
|
||||
)
|
||||
return run_search(service, query, earliest=earliest)
|
||||
|
||||
|
||||
def detect_lsass_access(service, earliest="-24h"):
|
||||
"""Detect credential dumping via LSASS process access (Sysmon Event 10)."""
|
||||
query = (
|
||||
'index=sysmon EventCode=10 TargetImage="*\\\\lsass.exe" '
|
||||
"GrantedAccess=0x1010 "
|
||||
"| table _time, host, SourceImage, SourceUser, GrantedAccess"
|
||||
)
|
||||
return run_search(service, query, earliest=earliest)
|
||||
|
||||
|
||||
def build_incident_timeline(service, hosts, users, earliest="-24h", latest="now"):
|
||||
"""Build a unified incident timeline across multiple log sources."""
|
||||
host_filter = " OR ".join(f'host="{h}"' for h in hosts)
|
||||
user_filter = " OR ".join(f'user="{u}"' for u in users)
|
||||
query = (
|
||||
f"index=windows OR index=sysmon OR index=proxy OR index=firewall "
|
||||
f"({host_filter} OR {user_filter}) "
|
||||
'| eval event_summary=case('
|
||||
' sourcetype=="WinEventLog:Security" AND EventCode==4624, '
|
||||
' "Logon: ".TargetUserName." from ".src_ip, '
|
||||
' sourcetype=="WinEventLog:Security" AND EventCode==4625, '
|
||||
' "Failed logon: ".TargetUserName, '
|
||||
' EventCode==1, "Process: ".Image." by ".User, '
|
||||
' 1==1, sourcetype.": ".EventCode) '
|
||||
"| table _time, sourcetype, host, event_summary "
|
||||
"| sort _time"
|
||||
)
|
||||
return run_search(service, query, earliest=earliest, latest=latest)
|
||||
|
||||
|
||||
def generate_report(findings):
|
||||
"""Format investigation findings into a structured report."""
|
||||
report = {
|
||||
"report_type": "SPLUNK INVESTIGATION REPORT",
|
||||
"generated_at": datetime.utcnow().isoformat() + "Z",
|
||||
"findings": findings,
|
||||
}
|
||||
return json.dumps(report, indent=2, default=str)
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Splunk Security Log Analysis Agent")
|
||||
parser.add_argument("--host", default=os.getenv("SPLUNK_HOST", "localhost"))
|
||||
parser.add_argument("--port", type=int, default=int(os.getenv("SPLUNK_PORT", "8089")))
|
||||
parser.add_argument("--username", default=os.getenv("SPLUNK_USERNAME", "admin"))
|
||||
parser.add_argument("--password", default=os.getenv("SPLUNK_PASSWORD", ""))
|
||||
parser.add_argument("--earliest", default="-24h", help="Search earliest time")
|
||||
parser.add_argument("--action", choices=[
|
||||
"brute_force", "lateral_movement", "powershell",
|
||||
"lsass_access", "timeline", "full_investigation"
|
||||
], default="full_investigation")
|
||||
parser.add_argument("--hosts", nargs="*", default=[], help="Target hosts for timeline")
|
||||
parser.add_argument("--users", nargs="*", default=[], help="Target users for timeline")
|
||||
parser.add_argument("--threshold", type=int, default=10)
|
||||
args = parser.parse_args()
|
||||
|
||||
service = connect_splunk(args.host, args.port, args.username, args.password)
|
||||
findings = {}
|
||||
|
||||
if args.action in ("brute_force", "full_investigation"):
|
||||
findings["brute_force"] = detect_brute_force(service, args.threshold, args.earliest)
|
||||
print(f"[+] Brute force: {len(findings['brute_force'])} accounts targeted")
|
||||
|
||||
if args.action in ("lateral_movement", "full_investigation"):
|
||||
findings["lateral_movement"] = detect_lateral_movement(service, args.earliest)
|
||||
print(f"[+] Lateral movement: {len(findings['lateral_movement'])} suspicious paths")
|
||||
|
||||
if args.action in ("powershell", "full_investigation"):
|
||||
findings["suspicious_powershell"] = detect_suspicious_powershell(service, args.earliest)
|
||||
print(f"[+] Suspicious PowerShell: {len(findings['suspicious_powershell'])} events")
|
||||
|
||||
if args.action in ("lsass_access", "full_investigation"):
|
||||
findings["lsass_access"] = detect_lsass_access(service, args.earliest)
|
||||
print(f"[+] LSASS access: {len(findings['lsass_access'])} events")
|
||||
|
||||
if args.action == "timeline" and args.hosts:
|
||||
findings["timeline"] = build_incident_timeline(
|
||||
service, args.hosts, args.users, args.earliest
|
||||
)
|
||||
print(f"[+] Timeline: {len(findings['timeline'])} events")
|
||||
|
||||
print(generate_report(findings))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user