mirror of
https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git
synced 2026-07-25 13:50: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,84 @@
|
||||
# S3 Data Exfiltration Detection API Reference
|
||||
|
||||
## GuardDuty S3 Finding Types
|
||||
|
||||
| Finding Type | Description |
|
||||
|-------------|-------------|
|
||||
| `Exfiltration:S3/MaliciousIPCaller` | S3 accessed from known malicious IP |
|
||||
| `Exfiltration:S3/AnomalousBehavior` | Unusual S3 access pattern |
|
||||
| `UnauthorizedAccess:S3/TorIPCaller` | S3 accessed from Tor exit node |
|
||||
| `Discovery:S3/AnomalousBehavior` | Unusual ListObjects/HeadBucket |
|
||||
| `Impact:S3/AnomalousBehavior.Delete` | Anomalous object deletion |
|
||||
|
||||
## CloudTrail S3 Data Events
|
||||
|
||||
```bash
|
||||
# Enable S3 data events on trail
|
||||
aws cloudtrail put-event-selectors --trail-name mgmt-trail \
|
||||
--event-selectors '[{"ReadWriteType":"All","DataResources":[{"Type":"AWS::S3::Object","Values":["arn:aws:s3:::sensitive-bucket/"]}]}]'
|
||||
|
||||
# Query GetObject events via Athena
|
||||
SELECT eventtime, useridentity.arn, requestparameters,
|
||||
sourceipaddress, useragent
|
||||
FROM cloudtrail_logs
|
||||
WHERE eventname = 'GetObject'
|
||||
AND requestparameters LIKE '%sensitive-bucket%'
|
||||
ORDER BY eventtime DESC
|
||||
```
|
||||
|
||||
## S3 Access Monitoring
|
||||
|
||||
```bash
|
||||
# Check bucket policy
|
||||
aws s3api get-bucket-policy --bucket mybucket
|
||||
|
||||
# Check public access block
|
||||
aws s3api get-public-access-block --bucket mybucket
|
||||
|
||||
# Enable server access logging
|
||||
aws s3api put-bucket-logging --bucket mybucket \
|
||||
--bucket-logging-status '{"LoggingEnabled":{"TargetBucket":"log-bucket","TargetPrefix":"s3-logs/"}}'
|
||||
|
||||
# List bucket ACL
|
||||
aws s3api get-bucket-acl --bucket mybucket
|
||||
```
|
||||
|
||||
## S3 Data Event Log Fields
|
||||
|
||||
| Field | Description |
|
||||
|-------|-------------|
|
||||
| `eventName` | GetObject, PutObject, DeleteObject, CopyObject |
|
||||
| `requestParameters.bucketName` | Target bucket |
|
||||
| `requestParameters.key` | Object key accessed |
|
||||
| `sourceIPAddress` | Caller IP |
|
||||
| `userIdentity.arn` | Caller identity |
|
||||
| `additionalEventData.bytesTransferredOut` | Data volume |
|
||||
|
||||
## Athena Query - Detect Bulk Downloads
|
||||
|
||||
```sql
|
||||
SELECT useridentity.arn, sourceipaddress,
|
||||
COUNT(*) as object_count,
|
||||
SUM(CAST(json_extract_scalar(additionaleventdata, '$.bytesTransferredOut') AS bigint)) as bytes_out
|
||||
FROM cloudtrail_logs
|
||||
WHERE eventname = 'GetObject'
|
||||
AND eventtime > '2024-01-01'
|
||||
GROUP BY useridentity.arn, sourceipaddress
|
||||
HAVING COUNT(*) > 100
|
||||
ORDER BY object_count DESC
|
||||
```
|
||||
|
||||
## Bucket Policy - Restrict to VPC Endpoint
|
||||
|
||||
```json
|
||||
{
|
||||
"Statement": [{
|
||||
"Sid": "DenyNonVPC",
|
||||
"Effect": "Deny",
|
||||
"Principal": "*",
|
||||
"Action": "s3:GetObject",
|
||||
"Resource": "arn:aws:s3:::bucket/*",
|
||||
"Condition": {"StringNotEquals": {"aws:sourceVpce": "vpce-xxxxx"}}
|
||||
}]
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,221 @@
|
||||
#!/usr/bin/env python3
|
||||
"""S3 data exfiltration detection agent using CloudTrail and GuardDuty."""
|
||||
|
||||
import json
|
||||
import subprocess
|
||||
import sys
|
||||
from collections import Counter
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
|
||||
def aws_cli(args):
|
||||
"""Execute 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 and result.stdout.strip():
|
||||
return json.loads(result.stdout)
|
||||
return {"error": result.stderr.strip()} if result.returncode != 0 else {}
|
||||
except Exception as e:
|
||||
return {"error": str(e)}
|
||||
|
||||
|
||||
def get_guardduty_s3_findings():
|
||||
"""Get GuardDuty findings for S3 exfiltration activity."""
|
||||
det = aws_cli(["guardduty", "list-detectors"])
|
||||
detector_id = det.get("DetectorIds", [None])[0]
|
||||
if not detector_id:
|
||||
return {"error": "No GuardDuty detector found"}
|
||||
|
||||
s3_types = [
|
||||
"Exfiltration:S3/MaliciousIPCaller",
|
||||
"Exfiltration:S3/AnomalousBehavior",
|
||||
"UnauthorizedAccess:S3/MaliciousIPCaller.Custom",
|
||||
"UnauthorizedAccess:S3/TorIPCaller",
|
||||
"Discovery:S3/MaliciousIPCaller",
|
||||
"Discovery:S3/AnomalousBehavior",
|
||||
"Impact:S3/AnomalousBehavior.Delete",
|
||||
"Impact:S3/AnomalousBehavior.Permission",
|
||||
"Impact:S3/AnomalousBehavior.Write",
|
||||
]
|
||||
criteria = {"Criterion": {"type": {"Eq": s3_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 {"count": 0, "findings": []}
|
||||
|
||||
details = aws_cli(["guardduty", "get-findings", "--detector-id", detector_id,
|
||||
"--finding-ids"] + finding_ids[:25])
|
||||
parsed = []
|
||||
for f in details.get("Findings", []):
|
||||
s3 = f.get("Resource", {}).get("S3BucketDetails", [{}])
|
||||
bucket = s3[0] if s3 else {}
|
||||
parsed.append({
|
||||
"type": f.get("Type"),
|
||||
"severity": f.get("Severity"),
|
||||
"bucket": bucket.get("Name"),
|
||||
"region": f.get("Region"),
|
||||
"action": f.get("Service", {}).get("Action", {}),
|
||||
})
|
||||
return {"count": len(parsed), "findings": parsed}
|
||||
|
||||
|
||||
def query_cloudtrail_s3_gets(bucket_name, hours=24):
|
||||
"""Query CloudTrail for GetObject events on a specific bucket."""
|
||||
end_time = datetime.utcnow()
|
||||
start_time = end_time - timedelta(hours=hours)
|
||||
result = aws_cli([
|
||||
"cloudtrail", "lookup-events",
|
||||
"--lookup-attributes", json.dumps([
|
||||
{"AttributeKey": "EventName", "AttributeValue": "GetObject"}
|
||||
]),
|
||||
"--start-time", start_time.isoformat() + "Z",
|
||||
"--end-time", end_time.isoformat() + "Z",
|
||||
"--max-results", "50",
|
||||
])
|
||||
events = []
|
||||
for e in result.get("Events", []):
|
||||
detail = json.loads(e.get("CloudTrailEvent", "{}"))
|
||||
req = detail.get("requestParameters", {})
|
||||
if bucket_name and req.get("bucketName") != bucket_name:
|
||||
continue
|
||||
events.append({
|
||||
"time": e.get("EventTime"),
|
||||
"user": e.get("Username"),
|
||||
"source_ip": detail.get("sourceIPAddress"),
|
||||
"bucket": req.get("bucketName"),
|
||||
"key": req.get("key", "")[:100],
|
||||
"user_agent": detail.get("userAgent", "")[:80],
|
||||
})
|
||||
return {"bucket": bucket_name, "get_events": events, "count": len(events)}
|
||||
|
||||
|
||||
def detect_bulk_download_patterns(bucket_name, hours=24):
|
||||
"""Detect anomalous bulk download patterns from S3."""
|
||||
trail = query_cloudtrail_s3_gets(bucket_name, hours)
|
||||
events = trail.get("get_events", [])
|
||||
|
||||
by_user = Counter()
|
||||
by_ip = Counter()
|
||||
for e in events:
|
||||
by_user[e.get("user", "unknown")] += 1
|
||||
by_ip[e.get("source_ip", "unknown")] += 1
|
||||
|
||||
anomalies = []
|
||||
for user, count in by_user.items():
|
||||
if count > 50:
|
||||
anomalies.append({
|
||||
"type": "BULK_DOWNLOAD",
|
||||
"severity": "HIGH",
|
||||
"user": user,
|
||||
"object_count": count,
|
||||
"period_hours": hours,
|
||||
})
|
||||
for ip, count in by_ip.items():
|
||||
if count > 100:
|
||||
anomalies.append({
|
||||
"type": "HIGH_VOLUME_SOURCE",
|
||||
"severity": "HIGH",
|
||||
"source_ip": ip,
|
||||
"object_count": count,
|
||||
})
|
||||
|
||||
return {
|
||||
"bucket": bucket_name,
|
||||
"total_gets": len(events),
|
||||
"unique_users": len(by_user),
|
||||
"unique_ips": len(by_ip),
|
||||
"anomalies": anomalies,
|
||||
"top_users": by_user.most_common(10),
|
||||
"top_ips": by_ip.most_common(10),
|
||||
}
|
||||
|
||||
|
||||
def check_bucket_policy(bucket_name):
|
||||
"""Check S3 bucket policy for overly permissive access."""
|
||||
result = aws_cli(["s3api", "get-bucket-policy", "--bucket", bucket_name])
|
||||
if "error" in result:
|
||||
return result
|
||||
|
||||
policy = json.loads(result.get("Policy", "{}")) if isinstance(result.get("Policy"), str) else result
|
||||
issues = []
|
||||
for stmt in policy.get("Statement", []):
|
||||
principal = stmt.get("Principal", "")
|
||||
if principal == "*" or (isinstance(principal, dict) and principal.get("AWS") == "*"):
|
||||
if stmt.get("Effect") == "Allow":
|
||||
issues.append({
|
||||
"severity": "CRITICAL",
|
||||
"finding": "Bucket allows public access",
|
||||
"action": stmt.get("Action"),
|
||||
"sid": stmt.get("Sid", ""),
|
||||
})
|
||||
|
||||
return {"bucket": bucket_name, "policy_issues": issues, "issue_count": len(issues)}
|
||||
|
||||
|
||||
def check_s3_access_logging(bucket_name):
|
||||
"""Verify S3 server access logging is enabled."""
|
||||
result = aws_cli(["s3api", "get-bucket-logging", "--bucket", bucket_name])
|
||||
enabled = bool(result.get("LoggingEnabled"))
|
||||
return {
|
||||
"bucket": bucket_name,
|
||||
"logging_enabled": enabled,
|
||||
"target_bucket": result.get("LoggingEnabled", {}).get("TargetBucket") if enabled else None,
|
||||
}
|
||||
|
||||
|
||||
def block_external_access(bucket_name):
|
||||
"""Restrict S3 bucket to VPC endpoint access only."""
|
||||
policy = {
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [{
|
||||
"Sid": "DenyNonVPCAccess",
|
||||
"Effect": "Deny",
|
||||
"Principal": "*",
|
||||
"Action": "s3:GetObject",
|
||||
"Resource": f"arn:aws:s3:::{bucket_name}/*",
|
||||
"Condition": {
|
||||
"StringNotEquals": {"aws:sourceVpce": "vpce-REPLACE_WITH_ENDPOINT_ID"}
|
||||
},
|
||||
}],
|
||||
}
|
||||
return aws_cli([
|
||||
"s3api", "put-bucket-policy",
|
||||
"--bucket", bucket_name,
|
||||
"--policy", json.dumps(policy),
|
||||
])
|
||||
|
||||
|
||||
def generate_report(bucket_name=None):
|
||||
"""Generate S3 exfiltration detection report."""
|
||||
report = {
|
||||
"timestamp": datetime.utcnow().isoformat() + "Z",
|
||||
"guardduty_s3": get_guardduty_s3_findings(),
|
||||
}
|
||||
if bucket_name:
|
||||
report["bulk_download_analysis"] = detect_bulk_download_patterns(bucket_name)
|
||||
report["bucket_policy"] = check_bucket_policy(bucket_name)
|
||||
report["access_logging"] = check_s3_access_logging(bucket_name)
|
||||
return report
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
action = sys.argv[1] if len(sys.argv) > 1 else "report"
|
||||
if action == "report":
|
||||
bucket = sys.argv[2] if len(sys.argv) > 2 else None
|
||||
print(json.dumps(generate_report(bucket), indent=2, default=str))
|
||||
elif action == "findings":
|
||||
print(json.dumps(get_guardduty_s3_findings(), indent=2, default=str))
|
||||
elif action == "gets" and len(sys.argv) > 2:
|
||||
hours = int(sys.argv[3]) if len(sys.argv) > 3 else 24
|
||||
print(json.dumps(query_cloudtrail_s3_gets(sys.argv[2], hours), indent=2, default=str))
|
||||
elif action == "bulk" and len(sys.argv) > 2:
|
||||
print(json.dumps(detect_bulk_download_patterns(sys.argv[2]), indent=2, default=str))
|
||||
elif action == "policy" and len(sys.argv) > 2:
|
||||
print(json.dumps(check_bucket_policy(sys.argv[2]), indent=2))
|
||||
else:
|
||||
print("Usage: agent.py [report [bucket]|findings|gets <bucket> [hours]|bulk <bucket>|policy <bucket>]")
|
||||
Reference in New Issue
Block a user