exaai-agent 2.0.9__py3-none-any.whl → 2.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {exaai_agent-2.0.9.dist-info → exaai_agent-2.2.0.dist-info}/METADATA +72 -42
- {exaai_agent-2.0.9.dist-info → exaai_agent-2.2.0.dist-info}/RECORD +34 -22
- {exaai_agent-2.0.9.dist-info → exaai_agent-2.2.0.dist-info}/WHEEL +1 -1
- exaaiagnt/dashboard/server.py +99 -0
- exaaiagnt/dashboard/templates/index.html +232 -0
- exaaiagnt/interface/cli.py +62 -30
- exaaiagnt/interface/main.py +11 -1
- exaaiagnt/interface/tui.py +12 -8
- exaaiagnt/llm/llm.py +43 -8
- exaaiagnt/llm/llm_traffic_controller.py +5 -3
- exaaiagnt/prompts/README.md +3 -1
- exaaiagnt/prompts/auto_loader.py +31 -0
- exaaiagnt/prompts/cloud/azure_cloud_security.jinja +126 -0
- exaaiagnt/prompts/cloud/gcp_cloud_security.jinja +158 -0
- exaaiagnt/prompts/cloud/kubernetes_security.jinja +97 -0
- exaaiagnt/prompts/vulnerabilities/prompt_injection.jinja +276 -0
- exaaiagnt/runtime/tool_manager.py +12 -3
- exaaiagnt/telemetry/tracer.py +17 -1
- exaaiagnt/tools/__init__.py +24 -0
- exaaiagnt/tools/executor.py +16 -4
- exaaiagnt/tools/k8s_scanner/__init__.py +29 -0
- exaaiagnt/tools/k8s_scanner/k8s_actions.py +319 -0
- exaaiagnt/tools/k8s_scanner/k8s_actions_schema.xml +36 -0
- exaaiagnt/tools/prompt_injection/__init__.py +26 -0
- exaaiagnt/tools/prompt_injection/prompt_injection_actions.py +712 -0
- exaaiagnt/tools/prompt_injection/prompt_injection_actions_schema.xml +28 -0
- exaaiagnt/tools/python/python_instance.py +16 -1
- exaaiagnt/tools/reporting/reporting_actions.py +34 -5
- exaaiagnt/tools/response_analyzer.py +5 -3
- exaaiagnt/tools/smart_fuzzer.py +5 -3
- exaaiagnt/tools/vuln_validator.py +5 -3
- exaaiagnt/tools/web_search/web_search_actions.py +4 -2
- {exaai_agent-2.0.9.dist-info → exaai_agent-2.2.0.dist-info}/entry_points.txt +0 -0
- {exaai_agent-2.0.9.dist-info → exaai_agent-2.2.0.dist-info}/licenses/LICENSE +0 -0
exaaiagnt/interface/cli.py
CHANGED
|
@@ -30,39 +30,66 @@ BANNER = r"""
|
|
|
30
30
|
"""
|
|
31
31
|
|
|
32
32
|
|
|
33
|
+
from exaaiagnt.dashboard.server import start_dashboard
|
|
34
|
+
|
|
33
35
|
async def run_cli(args: Any) -> None: # noqa: PLR0915
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
console.print()
|
|
36
|
+
# Start the live dashboard
|
|
37
|
+
try:
|
|
38
|
+
start_dashboard()
|
|
39
|
+
console_temp = Console()
|
|
40
|
+
console_temp.print("[bold green]🚀 Live Dashboard available at: http://localhost:8000[/]")
|
|
41
|
+
except Exception as e:
|
|
42
|
+
import logging
|
|
43
|
+
logging.error(f"Failed to start dashboard: {e}")
|
|
43
44
|
|
|
44
|
-
#
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
target_table.add_column("Target", style="white")
|
|
48
|
-
|
|
49
|
-
for target_info in args.targets_info:
|
|
50
|
-
target_type = target_info.get("type", "URL")
|
|
51
|
-
target_table.add_row(target_type, target_info["original"])
|
|
45
|
+
# Detect if running in a real terminal or headless (pipe/background)
|
|
46
|
+
is_tty = sys.stdout.isatty()
|
|
47
|
+
console = Console(force_terminal=is_tty, no_color=not is_tty)
|
|
52
48
|
|
|
53
|
-
|
|
54
|
-
|
|
49
|
+
if is_tty:
|
|
50
|
+
# Clear screen and show banner only in interactive terminal
|
|
51
|
+
console.clear()
|
|
52
|
+
console.print()
|
|
53
|
+
console.print(BANNER, style="bold cyan", justify="center")
|
|
54
|
+
console.print("[bold purple]Advanced AI-Powered Cybersecurity Agent[/]", justify="center")
|
|
55
|
+
console.print("[dim]v2.1.2[/]", justify="center")
|
|
56
|
+
console.print()
|
|
57
|
+
else:
|
|
58
|
+
# Simple text output for headless/pipe mode
|
|
59
|
+
print("=" * 50)
|
|
60
|
+
print("ExaAiAgent - AI-Powered Security Scanner")
|
|
61
|
+
print("=" * 50)
|
|
62
|
+
|
|
63
|
+
if is_tty:
|
|
64
|
+
# Target info table (rich formatting)
|
|
65
|
+
target_table = Table(show_header=True, header_style="bold cyan", border_style="cyan")
|
|
66
|
+
target_table.add_column("Type", style="dim")
|
|
67
|
+
target_table.add_column("Target", style="white")
|
|
68
|
+
|
|
69
|
+
for target_info in args.targets_info:
|
|
70
|
+
target_type = target_info.get("type", "URL")
|
|
71
|
+
target_table.add_row(target_type, target_info["original"])
|
|
72
|
+
|
|
73
|
+
console.print(Panel(target_table, title="[bold cyan]🎯 Targets", border_style="cyan"))
|
|
74
|
+
console.print()
|
|
55
75
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
76
|
+
# Config info
|
|
77
|
+
config_text = Text()
|
|
78
|
+
config_text.append("📁 Results: ", style="dim")
|
|
79
|
+
config_text.append(f"exaai_runs/{args.run_name}\n", style="white")
|
|
80
|
+
if args.instruction:
|
|
81
|
+
config_text.append("📝 Instruction: ", style="dim")
|
|
82
|
+
config_text.append(f"{args.instruction[:100]}{'...' if len(args.instruction) > 100 else ''}", style="white")
|
|
83
|
+
|
|
84
|
+
console.print(Panel(config_text, title="[bold green]⚙️ Configuration", border_style="green"))
|
|
85
|
+
console.print()
|
|
86
|
+
else:
|
|
87
|
+
# Simple text output for headless mode
|
|
88
|
+
print(f"Targets: {[t['original'] for t in args.targets_info]}")
|
|
89
|
+
print(f"Results: exaai_runs/{args.run_name}")
|
|
90
|
+
if args.instruction:
|
|
91
|
+
print(f"Instruction: {args.instruction[:100]}")
|
|
92
|
+
print("-" * 50)
|
|
66
93
|
|
|
67
94
|
scan_config = {
|
|
68
95
|
"scan_id": args.run_name,
|
|
@@ -71,7 +98,12 @@ async def run_cli(args: Any) -> None: # noqa: PLR0915
|
|
|
71
98
|
"run_name": args.run_name,
|
|
72
99
|
}
|
|
73
100
|
|
|
74
|
-
|
|
101
|
+
# Handle prompt modules
|
|
102
|
+
prompt_modules = None
|
|
103
|
+
if getattr(args, "prompt_modules", None):
|
|
104
|
+
prompt_modules = [m.strip() for m in args.prompt_modules.split(",")]
|
|
105
|
+
|
|
106
|
+
llm_config = LLMConfig(prompt_modules=prompt_modules)
|
|
75
107
|
agent_config = {
|
|
76
108
|
"llm_config": llm_config,
|
|
77
109
|
"max_iterations": 300,
|
exaaiagnt/interface/main.py
CHANGED
|
@@ -242,7 +242,7 @@ async def warm_up_llm() -> None:
|
|
|
242
242
|
|
|
243
243
|
def get_version() -> str:
|
|
244
244
|
"""Get the current ExaAi version."""
|
|
245
|
-
return "2.1.
|
|
245
|
+
return "2.1.2"
|
|
246
246
|
|
|
247
247
|
|
|
248
248
|
def parse_arguments() -> argparse.Namespace:
|
|
@@ -276,6 +276,10 @@ Examples:
|
|
|
276
276
|
# Custom instructions
|
|
277
277
|
exaai -t example.com -i "Focus on authentication vulnerabilities"
|
|
278
278
|
exaai -t example.com --instruction ./instructions.txt
|
|
279
|
+
|
|
280
|
+
# Specific security modules
|
|
281
|
+
exaai -t example.com --prompt-modules kubernetes_security,cloud_security
|
|
282
|
+
exaai -t example.com --prompt-modules prompt_injection
|
|
279
283
|
""",
|
|
280
284
|
)
|
|
281
285
|
|
|
@@ -306,6 +310,12 @@ Examples:
|
|
|
306
310
|
"or test credentials (e.g., 'Use the following credentials: admin:password123'). "
|
|
307
311
|
"You can also provide a path to a file containing detailed instructions.",
|
|
308
312
|
)
|
|
313
|
+
parser.add_argument(
|
|
314
|
+
"--prompt-modules",
|
|
315
|
+
type=str,
|
|
316
|
+
help="Comma-separated list of prompt modules to load (e.g., 'kubernetes_security,prompt_injection'). "
|
|
317
|
+
"Overrides auto-detection.",
|
|
318
|
+
)
|
|
309
319
|
parser.add_argument(
|
|
310
320
|
"--run-name",
|
|
311
321
|
type=str,
|
exaaiagnt/interface/tui.py
CHANGED
|
@@ -45,7 +45,7 @@ def get_package_version() -> str:
|
|
|
45
45
|
return pkg_version("exaai-agent")
|
|
46
46
|
except PackageNotFoundError:
|
|
47
47
|
# Fallback version if package not installed
|
|
48
|
-
return "2.
|
|
48
|
+
return "2.1.2"
|
|
49
49
|
|
|
50
50
|
|
|
51
51
|
class ChatTextArea(TextArea): # type: ignore[misc]
|
|
@@ -80,7 +80,7 @@ class SplashScreen(Static): # type: ignore[misc]
|
|
|
80
80
|
NEON_ORANGE = "#ff8800"
|
|
81
81
|
SOFT_WHITE = "#e0e0e0"
|
|
82
82
|
|
|
83
|
-
# Enhanced ASCII Logo - ExaAi v2.
|
|
83
|
+
# Enhanced ASCII Logo - ExaAi v2.1.2
|
|
84
84
|
BANNER = r"""
|
|
85
85
|
███████╗██╗ ██╗ █████╗ █████╗ ██╗
|
|
86
86
|
██╔════╝╚██╗██╔╝██╔══██╗ ██╔══██╗██║
|
|
@@ -104,7 +104,7 @@ class SplashScreen(Static): # type: ignore[misc]
|
|
|
104
104
|
self._animation_step = 0
|
|
105
105
|
self._animation_timer: Timer | None = None
|
|
106
106
|
self._panel_static: Static | None = None
|
|
107
|
-
self._version = "2.1.
|
|
107
|
+
self._version = "2.1.2"
|
|
108
108
|
|
|
109
109
|
def compose(self) -> ComposeResult:
|
|
110
110
|
self._version = get_package_version()
|
|
@@ -181,14 +181,14 @@ class SplashScreen(Static): # type: ignore[misc]
|
|
|
181
181
|
return text
|
|
182
182
|
|
|
183
183
|
def _build_new_features_text(self) -> Text:
|
|
184
|
-
"""Build new features highlight for v2.0.
|
|
184
|
+
"""Build new features highlight for v2.1.0."""
|
|
185
185
|
text = Text("🔥 ", style=Style(color=self.NEON_ORANGE))
|
|
186
186
|
text.append("NEW: ", style=Style(color=self.NEON_ORANGE, bold=True))
|
|
187
|
-
text.append("
|
|
187
|
+
text.append("K8s Security", style=Style(color=self.NEON_PINK))
|
|
188
188
|
text.append(" • ", style=Style(color=self.SOFT_WHITE, dim=True))
|
|
189
|
-
text.append("
|
|
189
|
+
text.append("Prompt Injection", style=Style(color=self.NEON_CYAN))
|
|
190
190
|
text.append(" • ", style=Style(color=self.SOFT_WHITE, dim=True))
|
|
191
|
-
text.append("
|
|
191
|
+
text.append("Azure/GCP", style=Style(color=self.NEON_GREEN))
|
|
192
192
|
return text
|
|
193
193
|
|
|
194
194
|
|
|
@@ -391,7 +391,11 @@ class ExaaiTUIApp(App): # type: ignore[misc]
|
|
|
391
391
|
}
|
|
392
392
|
|
|
393
393
|
def _build_agent_config(self, args: argparse.Namespace) -> dict[str, Any]:
|
|
394
|
-
|
|
394
|
+
prompt_modules = None
|
|
395
|
+
if getattr(args, "prompt_modules", None):
|
|
396
|
+
prompt_modules = [m.strip() for m in args.prompt_modules.split(",")]
|
|
397
|
+
|
|
398
|
+
llm_config = LLMConfig(prompt_modules=prompt_modules)
|
|
395
399
|
|
|
396
400
|
config = {
|
|
397
401
|
"llm_config": llm_config,
|
exaaiagnt/llm/llm.py
CHANGED
|
@@ -424,19 +424,54 @@ class LLM:
|
|
|
424
424
|
else:
|
|
425
425
|
completion_args["reasoning_effort"] = "high"
|
|
426
426
|
|
|
427
|
-
# Use Adaptive Traffic Controller for intelligent rate limiting
|
|
427
|
+
# Use Adaptive Traffic Controller for intelligent rate limiting with retries
|
|
428
428
|
controller = get_traffic_controller()
|
|
429
429
|
agent_id = self.agent_id or "unknown_agent"
|
|
430
430
|
|
|
431
431
|
async def do_request():
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
432
|
+
try:
|
|
433
|
+
from litellm import completion
|
|
434
|
+
return await completion(**completion_args, stream=False)
|
|
435
|
+
except litellm.RateLimitError:
|
|
436
|
+
# Let tenacity (in controller) handle retry
|
|
437
|
+
raise
|
|
438
|
+
except Exception as e:
|
|
439
|
+
# Log other transient errors for potential retry
|
|
440
|
+
logger.warning(f"Transient LLM error: {e}")
|
|
441
|
+
raise
|
|
442
|
+
|
|
443
|
+
# Wrap in retry logic
|
|
444
|
+
from tenacity import (
|
|
445
|
+
retry,
|
|
446
|
+
stop_after_attempt,
|
|
447
|
+
wait_exponential,
|
|
448
|
+
retry_if_exception_type,
|
|
449
|
+
)
|
|
450
|
+
import litellm
|
|
451
|
+
|
|
452
|
+
@retry(
|
|
453
|
+
retry=retry_if_exception_type((
|
|
454
|
+
litellm.RateLimitError,
|
|
455
|
+
litellm.ServiceUnavailableError,
|
|
456
|
+
litellm.APIConnectionError,
|
|
457
|
+
litellm.Timeout
|
|
458
|
+
)),
|
|
459
|
+
wait=wait_exponential(multiplier=1, min=4, max=60),
|
|
460
|
+
stop=stop_after_attempt(5),
|
|
461
|
+
reraise=True
|
|
439
462
|
)
|
|
463
|
+
async def execute_with_retries():
|
|
464
|
+
return await controller.queue_request(
|
|
465
|
+
do_request,
|
|
466
|
+
agent_id=agent_id,
|
|
467
|
+
priority=RequestPriority.NORMAL
|
|
468
|
+
)
|
|
469
|
+
|
|
470
|
+
try:
|
|
471
|
+
response = await execute_with_retries()
|
|
472
|
+
except Exception:
|
|
473
|
+
self._total_stats.failed_requests += 1
|
|
474
|
+
raise
|
|
440
475
|
|
|
441
476
|
self._total_stats.requests += 1
|
|
442
477
|
self._last_request_stats = RequestStats(requests=1)
|
|
@@ -54,11 +54,13 @@ class AdaptiveLLMController:
|
|
|
54
54
|
"""
|
|
55
55
|
|
|
56
56
|
_instance: Optional["AdaptiveLLMController"] = None
|
|
57
|
+
_lock_cls = __import__("threading").Lock()
|
|
57
58
|
|
|
58
59
|
def __new__(cls) -> "AdaptiveLLMController":
|
|
59
|
-
|
|
60
|
-
cls._instance
|
|
61
|
-
|
|
60
|
+
with cls._lock_cls:
|
|
61
|
+
if cls._instance is None:
|
|
62
|
+
cls._instance = super().__new__(cls)
|
|
63
|
+
cls._instance._initialized = False
|
|
62
64
|
return cls._instance
|
|
63
65
|
|
|
64
66
|
def __init__(self):
|
exaaiagnt/prompts/README.md
CHANGED
|
@@ -43,12 +43,14 @@ The modules are dynamically injected into the agent's system prompt, allowing it
|
|
|
43
43
|
| `race_conditions` | Race condition and TOCTOU exploits |
|
|
44
44
|
| `path_traversal` | Directory traversal attacks |
|
|
45
45
|
|
|
46
|
-
### NEW: Advanced Modules
|
|
46
|
+
### NEW: Advanced Modules (v2.1)
|
|
47
47
|
|
|
48
48
|
| Module | Description |
|
|
49
49
|
|--------|-------------|
|
|
50
50
|
| `api_security` | REST, GraphQL, gRPC API security testing |
|
|
51
51
|
| `cloud_security` | AWS, Azure, GCP security assessment |
|
|
52
|
+
| `kubernetes_security` | **NEW!** K8s RBAC, Pod Security, Network Policy audit |
|
|
53
|
+
| `prompt_injection` | **NEW!** AI/LLM prompt injection & jailbreaking |
|
|
52
54
|
| `reconnaissance_osint` | Reconnaissance and OSINT techniques |
|
|
53
55
|
| `privilege_escalation` | Linux/Windows privilege escalation |
|
|
54
56
|
| `high_impact_bugs` | Bug bounty hunting for critical vulns |
|
exaaiagnt/prompts/auto_loader.py
CHANGED
|
@@ -210,6 +210,37 @@ MODULE_PATTERNS = {
|
|
|
210
210
|
],
|
|
211
211
|
"keywords": ["aws", "s3", "ec2", "lambda", "azure", "gcp", "cloud", "bucket", "metadata"],
|
|
212
212
|
},
|
|
213
|
+
|
|
214
|
+
# Kubernetes Security (NEW v2.1)
|
|
215
|
+
"kubernetes_security": {
|
|
216
|
+
"url_patterns": [
|
|
217
|
+
r"/api/v1/",
|
|
218
|
+
r"/apis/",
|
|
219
|
+
r":6443",
|
|
220
|
+
r":10250",
|
|
221
|
+
r":8443",
|
|
222
|
+
],
|
|
223
|
+
"keywords": ["kubernetes", "k8s", "kubectl", "pod", "deployment", "service",
|
|
224
|
+
"ingress", "helm", "kubelet", "etcd", "rbac", "namespace"],
|
|
225
|
+
},
|
|
226
|
+
|
|
227
|
+
# AI/LLM Prompt Injection (NEW v2.1)
|
|
228
|
+
"prompt_injection": {
|
|
229
|
+
"url_patterns": [
|
|
230
|
+
r"/chat",
|
|
231
|
+
r"/completions",
|
|
232
|
+
r"/generate",
|
|
233
|
+
r"/ask",
|
|
234
|
+
r"/ai",
|
|
235
|
+
r"/llm",
|
|
236
|
+
r"/v1/chat",
|
|
237
|
+
r"/v1/completions",
|
|
238
|
+
r"/assistant",
|
|
239
|
+
],
|
|
240
|
+
"keywords": ["openai", "anthropic", "llm", "gpt", "claude", "chatbot",
|
|
241
|
+
"ai assistant", "langchain", "llama", "gemini", "copilot",
|
|
242
|
+
"rag", "embedding", "vector", "prompt"],
|
|
243
|
+
},
|
|
213
244
|
}
|
|
214
245
|
|
|
215
246
|
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
<azure_security_guide>
|
|
2
|
+
<title>AZURE CLOUD SECURITY AUDIT</title>
|
|
3
|
+
|
|
4
|
+
<critical>Azure is the enterprise cloud. Misconfigurations in Azure AD, Blob Storage, and Managed Identities are primary attack vectors. Default settings often expose resources publicly. Entra ID (formerly Azure AD) is the crown jewel - compromise it, own everything.</critical>
|
|
5
|
+
|
|
6
|
+
<scope>
|
|
7
|
+
- Identity: Azure AD (Entra ID), Service Principals, Managed Identities
|
|
8
|
+
- Storage: Blob Storage, Azure Files, Data Lake
|
|
9
|
+
- Compute: VMs, App Service, Functions, AKS
|
|
10
|
+
- Network: NSGs, Azure Firewall, Private Link, VNet
|
|
11
|
+
- Secrets: Key Vault, App Configuration
|
|
12
|
+
</scope>
|
|
13
|
+
|
|
14
|
+
<methodology>
|
|
15
|
+
1. **Reconnaissance**: Enumerate subscriptions, resource groups, and public assets.
|
|
16
|
+
2. **Identity Audit**: Check Azure AD roles, conditional access policies, MFA enforcement.
|
|
17
|
+
3. **Storage Audit**: Scan for public blobs, SAS token misuse, anonymous access.
|
|
18
|
+
4. **Compute Audit**: Check VM extensions, managed identity permissions, IMDS exposure.
|
|
19
|
+
5. **Network Audit**: Verify NSG rules, private endpoints, exposed services.
|
|
20
|
+
6. **Secrets Audit**: Key Vault access policies, secret rotation, RBAC vs Access Policies.
|
|
21
|
+
</methodology>
|
|
22
|
+
|
|
23
|
+
<attack_vectors>
|
|
24
|
+
- **Public Blob Storage**: Anonymous read access to containers.
|
|
25
|
+
- **SAS Token Abuse**: Overly permissive or leaked SAS tokens.
|
|
26
|
+
- **IMDS v1**: Instance metadata service for credential theft (169.254.169.254).
|
|
27
|
+
- **Service Principal Secrets**: Exposed or non-rotating SP credentials.
|
|
28
|
+
- **Managed Identity Abuse**: Over-privileged managed identities on VMs/Functions.
|
|
29
|
+
- **Azure AD Misconfig**: Guest users with elevated roles, no conditional access.
|
|
30
|
+
- **Key Vault RBAC**: Users with Key Vault Contributor can read secrets.
|
|
31
|
+
- **ARM Template Secrets**: Secrets hardcoded in deployments.
|
|
32
|
+
</attack_vectors>
|
|
33
|
+
|
|
34
|
+
<critical_checks>
|
|
35
|
+
<storage>
|
|
36
|
+
# Check for public blob containers
|
|
37
|
+
az storage container list --account-name <storage> --query "[?properties.publicAccess!=null]"
|
|
38
|
+
|
|
39
|
+
# Check for anonymous access
|
|
40
|
+
az storage blob list --container-name <container> --account-name <storage> --auth-mode key
|
|
41
|
+
</storage>
|
|
42
|
+
|
|
43
|
+
<identity>
|
|
44
|
+
# List Service Principals with secrets
|
|
45
|
+
az ad sp list --all --query "[?passwordCredentials]"
|
|
46
|
+
|
|
47
|
+
# Check for high-privilege role assignments
|
|
48
|
+
az role assignment list --all --query "[?roleDefinitionName=='Owner' || roleDefinitionName=='Contributor']"
|
|
49
|
+
|
|
50
|
+
# Managed Identity check
|
|
51
|
+
az vm identity show --name <vm> --resource-group <rg>
|
|
52
|
+
</identity>
|
|
53
|
+
|
|
54
|
+
<network>
|
|
55
|
+
# Check for open NSG rules (any source)
|
|
56
|
+
az network nsg rule list --nsg-name <nsg> --resource-group <rg> --query "[?sourceAddressPrefix=='*' && access=='Allow']"
|
|
57
|
+
|
|
58
|
+
# Find public IPs
|
|
59
|
+
az network public-ip list --query "[?ipAddress!=null]"
|
|
60
|
+
</network>
|
|
61
|
+
|
|
62
|
+
<keyvault>
|
|
63
|
+
# Check Key Vault access policies
|
|
64
|
+
az keyvault show --name <vault> --query "properties.accessPolicies"
|
|
65
|
+
|
|
66
|
+
# Check for RBAC mode (preferred)
|
|
67
|
+
az keyvault show --name <vault> --query "properties.enableRbacAuthorization"
|
|
68
|
+
</keyvault>
|
|
69
|
+
</critical_checks>
|
|
70
|
+
|
|
71
|
+
<automation_script>
|
|
72
|
+
```python
|
|
73
|
+
import subprocess
|
|
74
|
+
import json
|
|
75
|
+
|
|
76
|
+
def run_az(cmd: list) -> dict:
|
|
77
|
+
"""Run Azure CLI command and return JSON."""
|
|
78
|
+
result = subprocess.run(["az"] + cmd + ["-o", "json"], capture_output=True, text=True)
|
|
79
|
+
return json.loads(result.stdout) if result.returncode == 0 else {}
|
|
80
|
+
|
|
81
|
+
def audit_azure():
|
|
82
|
+
findings = []
|
|
83
|
+
|
|
84
|
+
# Check public storage containers
|
|
85
|
+
accounts = run_az(["storage", "account", "list"])
|
|
86
|
+
for acc in accounts:
|
|
87
|
+
containers = run_az(["storage", "container", "list", "--account-name", acc["name"], "--auth-mode", "login"])
|
|
88
|
+
for container in containers:
|
|
89
|
+
if container.get("properties", {}).get("publicAccess"):
|
|
90
|
+
findings.append({
|
|
91
|
+
"severity": "CRITICAL",
|
|
92
|
+
"title": "Public Blob Container",
|
|
93
|
+
"resource": f"{acc['name']}/{container['name']}",
|
|
94
|
+
"remediation": "Set publicAccess to None"
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
# Check privileged role assignments
|
|
98
|
+
assignments = run_az(["role", "assignment", "list", "--all"])
|
|
99
|
+
for assignment in assignments:
|
|
100
|
+
if assignment.get("roleDefinitionName") in ["Owner", "User Access Administrator"]:
|
|
101
|
+
findings.append({
|
|
102
|
+
"severity": "HIGH",
|
|
103
|
+
"title": f"Privileged Role: {assignment['roleDefinitionName']}",
|
|
104
|
+
"resource": assignment.get("principalName"),
|
|
105
|
+
"remediation": "Review and apply least privilege"
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
return findings
|
|
109
|
+
|
|
110
|
+
if __name__ == "__main__":
|
|
111
|
+
for f in audit_azure():
|
|
112
|
+
print(f"[{f['severity']}] {f['title']}: {f['resource']}")
|
|
113
|
+
```
|
|
114
|
+
</automation_script>
|
|
115
|
+
|
|
116
|
+
<remediation_guide>
|
|
117
|
+
1. **Disable Public Blob Access**: Set `allowBlobPublicAccess: false` at storage account level.
|
|
118
|
+
2. **Use Managed Identities**: Avoid Service Principal secrets where possible.
|
|
119
|
+
3. **Enable MFA**: Enforce MFA for all users via Conditional Access.
|
|
120
|
+
4. **Use RBAC for Key Vault**: Switch from Access Policies to Azure RBAC.
|
|
121
|
+
5. **Private Endpoints**: Use Private Link for storage, databases, and Key Vault.
|
|
122
|
+
6. **Enable Defender for Cloud**: Turn on security recommendations and alerts.
|
|
123
|
+
7. **Rotate Secrets**: Automate rotation for Service Principal secrets and keys.
|
|
124
|
+
</remediation_guide>
|
|
125
|
+
|
|
126
|
+
</azure_security_guide>
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
<gcp_security_guide>
|
|
2
|
+
<title>GOOGLE CLOUD PLATFORM (GCP) SECURITY AUDIT</title>
|
|
3
|
+
|
|
4
|
+
<critical>GCP's IAM model is powerful but complex. Over-permissive service accounts, public Cloud Storage buckets, and metadata service abuse are the top attack vectors. Workload Identity Federation and VPC Service Controls are critical but often misconfigured.</critical>
|
|
5
|
+
|
|
6
|
+
<scope>
|
|
7
|
+
- Identity: IAM policies, Service Accounts, Workload Identity
|
|
8
|
+
- Storage: Cloud Storage (GCS), BigQuery, Firestore
|
|
9
|
+
- Compute: Compute Engine, Cloud Functions, Cloud Run, GKE
|
|
10
|
+
- Network: VPC, Firewall Rules, Private Google Access
|
|
11
|
+
- Secrets: Secret Manager, KMS
|
|
12
|
+
</scope>
|
|
13
|
+
|
|
14
|
+
<methodology>
|
|
15
|
+
1. **Reconnaissance**: Enumerate projects, service accounts, and public assets.
|
|
16
|
+
2. **IAM Audit**: Check for overly permissive roles (Editor, Owner) on service accounts.
|
|
17
|
+
3. **Storage Audit**: Scan for public buckets, allUsers/allAuthenticatedUsers ACLs.
|
|
18
|
+
4. **Compute Audit**: Check metadata server access, service account scopes, public IPs.
|
|
19
|
+
5. **Network Audit**: Verify firewall rules, VPC Service Controls, Private Google Access.
|
|
20
|
+
6. **Secrets Audit**: Secret Manager access, KMS key permissions.
|
|
21
|
+
</methodology>
|
|
22
|
+
|
|
23
|
+
<attack_vectors>
|
|
24
|
+
- **Public GCS Buckets**: `allUsers` read access on sensitive buckets.
|
|
25
|
+
- **Metadata Server Abuse**: `http://metadata.google.internal/computeMetadata/v1/` for token theft.
|
|
26
|
+
- **Service Account Key Leakage**: JSON keys committed to repos or logs.
|
|
27
|
+
- **Default Service Account**: Compute Engine default SA with Editor role.
|
|
28
|
+
- **Over-privileged IAM**: Service accounts with `roles/editor` or `roles/owner`.
|
|
29
|
+
- **Workload Identity Misconfiguration**: Allowing external identities to impersonate SAs.
|
|
30
|
+
- **Public Cloud Functions**: Functions with `allUsers` invoker permission.
|
|
31
|
+
- **BigQuery Public Datasets**: Datasets readable by `allAuthenticatedUsers`.
|
|
32
|
+
</attack_vectors>
|
|
33
|
+
|
|
34
|
+
<critical_checks>
|
|
35
|
+
<storage>
|
|
36
|
+
# Check for public buckets
|
|
37
|
+
gsutil iam get gs://<bucket> | grep -E "allUsers|allAuthenticatedUsers"
|
|
38
|
+
|
|
39
|
+
# List all buckets
|
|
40
|
+
gsutil ls
|
|
41
|
+
|
|
42
|
+
# Check bucket ACLs
|
|
43
|
+
gsutil acl get gs://<bucket>
|
|
44
|
+
</storage>
|
|
45
|
+
|
|
46
|
+
<iam>
|
|
47
|
+
# List all service accounts
|
|
48
|
+
gcloud iam service-accounts list
|
|
49
|
+
|
|
50
|
+
# Check service account keys (should be minimal)
|
|
51
|
+
gcloud iam service-accounts keys list --iam-account=<sa-email>
|
|
52
|
+
|
|
53
|
+
# Find over-privileged bindings
|
|
54
|
+
gcloud projects get-iam-policy <project> --format=json | jq '.bindings[] | select(.role | contains("owner") or contains("editor"))'
|
|
55
|
+
|
|
56
|
+
# Check for external members
|
|
57
|
+
gcloud projects get-iam-policy <project> --format=json | jq '.bindings[].members[] | select(contains("user:") | not)'
|
|
58
|
+
</iam>
|
|
59
|
+
|
|
60
|
+
<compute>
|
|
61
|
+
# Check default service account usage
|
|
62
|
+
gcloud compute instances list --format="table(name,serviceAccounts.email)"
|
|
63
|
+
|
|
64
|
+
# Metadata server access test (from compromised instance)
|
|
65
|
+
curl -H "Metadata-Flavor: Google" http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token
|
|
66
|
+
|
|
67
|
+
# Check for public IPs
|
|
68
|
+
gcloud compute instances list --format="table(name,networkInterfaces[].accessConfigs[].natIP)"
|
|
69
|
+
</compute>
|
|
70
|
+
|
|
71
|
+
<functions>
|
|
72
|
+
# Check for public invocation
|
|
73
|
+
gcloud functions describe <function> --format="value(invokerMembers)"
|
|
74
|
+
# If contains "allUsers" = PUBLIC
|
|
75
|
+
</functions>
|
|
76
|
+
|
|
77
|
+
<firewall>
|
|
78
|
+
# List all firewall rules allowing 0.0.0.0/0
|
|
79
|
+
gcloud compute firewall-rules list --filter="sourceRanges=0.0.0.0/0" --format="table(name,allowed,direction)"
|
|
80
|
+
</firewall>
|
|
81
|
+
</critical_checks>
|
|
82
|
+
|
|
83
|
+
<automation_script>
|
|
84
|
+
```python
|
|
85
|
+
import subprocess
|
|
86
|
+
import json
|
|
87
|
+
|
|
88
|
+
def run_gcloud(cmd: list) -> dict:
|
|
89
|
+
"""Run gcloud command and return JSON."""
|
|
90
|
+
result = subprocess.run(["gcloud"] + cmd + ["--format=json"], capture_output=True, text=True)
|
|
91
|
+
return json.loads(result.stdout) if result.returncode == 0 else []
|
|
92
|
+
|
|
93
|
+
def audit_gcp(project: str):
|
|
94
|
+
findings = []
|
|
95
|
+
|
|
96
|
+
# Check for service account keys (should be 0 or 1)
|
|
97
|
+
sas = run_gcloud(["iam", "service-accounts", "list", f"--project={project}"])
|
|
98
|
+
for sa in sas:
|
|
99
|
+
keys = run_gcloud(["iam", "service-accounts", "keys", "list",
|
|
100
|
+
f"--iam-account={sa['email']}", f"--project={project}"])
|
|
101
|
+
user_keys = [k for k in keys if k.get("keyType") == "USER_MANAGED"]
|
|
102
|
+
if len(user_keys) > 0:
|
|
103
|
+
findings.append({
|
|
104
|
+
"severity": "HIGH",
|
|
105
|
+
"title": "User-Managed SA Keys Exist",
|
|
106
|
+
"resource": sa["email"],
|
|
107
|
+
"details": f"{len(user_keys)} user-managed keys found",
|
|
108
|
+
"remediation": "Use Workload Identity or VM Service Accounts instead of keys"
|
|
109
|
+
})
|
|
110
|
+
|
|
111
|
+
# Check IAM for overly permissive roles
|
|
112
|
+
policy = run_gcloud(["projects", "get-iam-policy", project])
|
|
113
|
+
for binding in policy.get("bindings", []):
|
|
114
|
+
if any(role in binding.get("role", "") for role in ["owner", "editor"]):
|
|
115
|
+
for member in binding.get("members", []):
|
|
116
|
+
if member.startswith("serviceAccount:"):
|
|
117
|
+
findings.append({
|
|
118
|
+
"severity": "CRITICAL",
|
|
119
|
+
"title": f"Service Account with {binding['role']}",
|
|
120
|
+
"resource": member,
|
|
121
|
+
"remediation": "Replace with fine-grained roles"
|
|
122
|
+
})
|
|
123
|
+
|
|
124
|
+
# Check for public firewall rules
|
|
125
|
+
rules = run_gcloud(["compute", "firewall-rules", "list", f"--project={project}"])
|
|
126
|
+
for rule in rules:
|
|
127
|
+
if "0.0.0.0/0" in rule.get("sourceRanges", []):
|
|
128
|
+
if rule.get("allowed"):
|
|
129
|
+
findings.append({
|
|
130
|
+
"severity": "MEDIUM",
|
|
131
|
+
"title": "Firewall Rule Allows 0.0.0.0/0",
|
|
132
|
+
"resource": rule["name"],
|
|
133
|
+
"details": f"Allows: {rule['allowed']}",
|
|
134
|
+
"remediation": "Restrict source ranges to known IPs"
|
|
135
|
+
})
|
|
136
|
+
|
|
137
|
+
return findings
|
|
138
|
+
|
|
139
|
+
if __name__ == "__main__":
|
|
140
|
+
import sys
|
|
141
|
+
project = sys.argv[1] if len(sys.argv) > 1 else "your-project"
|
|
142
|
+
for f in audit_gcp(project):
|
|
143
|
+
print(f"[{f['severity']}] {f['title']}: {f['resource']}")
|
|
144
|
+
```
|
|
145
|
+
</automation_script>
|
|
146
|
+
|
|
147
|
+
<remediation_guide>
|
|
148
|
+
1. **Remove SA Keys**: Delete all user-managed service account keys. Use Workload Identity.
|
|
149
|
+
2. **Least Privilege IAM**: Replace Editor/Owner with specific roles.
|
|
150
|
+
3. **Private Buckets**: Remove allUsers/allAuthenticatedUsers from bucket IAM.
|
|
151
|
+
4. **VPC Service Controls**: Enable for sensitive projects.
|
|
152
|
+
5. **OS Login**: Use OS Login instead of SSH keys for VM access.
|
|
153
|
+
6. **Private Google Access**: Enable for subnets to access Google APIs privately.
|
|
154
|
+
7. **Security Command Center**: Enable for vulnerability and threat detection.
|
|
155
|
+
8. **Organization Policies**: Enforce constraints (no public IPs, no external sharing).
|
|
156
|
+
</remediation_guide>
|
|
157
|
+
|
|
158
|
+
</gcp_security_guide>
|