Upload files to "redflare/modules"
This commit is contained in:
@@ -0,0 +1,50 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import re
|
||||
import time
|
||||
from urllib.parse import urljoin
|
||||
|
||||
from redflare.core.models import Finding, ModuleResult, Target
|
||||
from .base import Module, ModuleContext
|
||||
from .http import request
|
||||
|
||||
PATHS = ("/", "/login", "/admin", "/dashboard", "/panel", "/console", "/manage", "/manager",
|
||||
"/status", "/api/status", "/health", "/metrics", "/config", "/config.json", "/api/config",
|
||||
"/graphql", "/swagger-ui.html", "/openapi.json", "/actuator/env", "/debug/vars", "/.env")
|
||||
SENSITIVE = re.compile(r"(?:config|secret|credential|token|\.env|actuator|debug|metrics|console|admin|dashboard)", re.I)
|
||||
AUTH = re.compile(r"(?:type=[\"']?password|/(?:login|signin|auth)(?:/|\?|$)|sign\s*in|log\s*in)", re.I)
|
||||
|
||||
|
||||
class NativeNoAuthModule(Module):
|
||||
name = "authorization_surface"
|
||||
description = "Natively triage bounded in-scope web surfaces for missing authentication barriers"
|
||||
|
||||
def run(self, target: Target, context: ModuleContext) -> ModuleResult:
|
||||
started = time.monotonic(); result = ModuleResult(self.name, target.url); hits = []
|
||||
for path in PATHS:
|
||||
url = urljoin(target.url.rstrip("/") + "/", path.lstrip("/"))
|
||||
try:
|
||||
response = request(url, context.timeout, max_body=200_000, allowed_origin=(target.host, target.port))
|
||||
except Exception:
|
||||
continue
|
||||
body = response.body.decode("utf-8", errors="replace")
|
||||
authenticated = response.status in {401, 403} or "www-authenticate" in response.headers or bool(AUTH.search(body))
|
||||
context.surface_graph.add_endpoint(target.url, response.url, method="GET", source=self.name,
|
||||
content_type=response.headers.get("content-type"), status=response.status,
|
||||
authentication="required" if authenticated else "not-observed")
|
||||
if response.status == 200 and not authenticated and (path != "/" or SENSITIVE.search(body)) and SENSITIVE.search(path + " " + body[:5000]):
|
||||
evidence = {"url": response.url, "path": path, "status": response.status,
|
||||
"content_type": response.headers.get("content-type", ""), "authentication_barrier": "not observed"}
|
||||
hits.append(evidence)
|
||||
result.findings.append(Finding(context.run_id, target.url, self.name, "unauthenticated-service",
|
||||
f"Potential unauthenticated surface at {path}", "high" if SENSITIVE.search(path) else "medium", .85,
|
||||
"A bounded GET request reached a potentially sensitive interface without an observed authentication barrier.",
|
||||
evidence, ["authorization", "native", "noauth"],
|
||||
remediation="Require server-side authentication and authorization before returning sensitive interface content."))
|
||||
context.emit(target.url, self.name, "finding", f"Potential unauthenticated surface: {response.url}")
|
||||
if context.rate > 0: time.sleep(min(1.0 / context.rate, 1.0))
|
||||
result.observations = {"engine": "native-redflare", "paths_checked": len(PATHS), "candidate_surfaces": len(hits)}
|
||||
directory = context.artifact_dir / self.name / target.host; directory.mkdir(parents=True, exist_ok=True)
|
||||
artifact = directory / "findings.json"; artifact.write_text(json.dumps({"target": target.url, "findings": hits}, indent=2), encoding="utf-8")
|
||||
result.artifacts.append(str(artifact)); result.duration_seconds = round(time.monotonic() - started, 4); return result
|
||||
Reference in New Issue
Block a user