Log/alert content pollution via unescaped config strings in reload error

    • Type: Bug
    • Resolution: Unresolved
    • Priority: Trivial - P5
    • None
    • Affects Version/s: None
    • Component/s: None
    • Monguard
    • ALL
    • None
    • None
    • None
    • None
    • None
    • None
    • None

      Summary

      Someone who can already edit the proxy's config file on the box can put any text, of any length, into one monitoring alert.

      Impact

      A local principal who already owns the config file can place arbitrary JSON-escaped text of arbitrary length inside the attr.error field of a single Atlas-flagged log record; no log-line forgery, no HTTP header/body framing break, no cross-principal disclosure.
      Severity: low
      Exploitability: theoretical — Reaching the alert sink requires local write access to the service's own config file plus SIGHUP, at which point the attacker already controls the proxy outright; both emitters JSON-escape the payload so no record forgery or response splitting occurs.

      Location

      • monguard/src/config/error.rs:70 in impl Display for ConfigError
      • monguard/src/config/loader.rs:120 in impl Display for ReloadError
      • monguard/src/runtime/reload.rs:146 in ReloadService::handle_reload
      • monguard/src/http/validate.rs:149 in validate_config_blob
      • monguard/src/config/resolver.rs:890 in validate_listener_references

      Reproduction / trigger path

      Analyzed trigger path from static analysis — not an executed PoC. Confirm with a concrete repro before handing off.

      Attacker controls: Full content and length of any free-text YAML field that the resolver echoes into a ConfigError (e.g. listeners.port[N].upstream, TLS file paths). On the HTTP path the total request body is capped at 1 MiB; on the SIGHUP path the only bound is the on-disk config file size.

      1. (source) monguard/src/config/loader.rs:60 in reload_config — On SIGHUP, RawConfig::from_file_with_content reads the on-disk YAML config file.
      2. (hop) monguard/src/config/resolver.rs:890 in validate_listener_references — Raw YAML port.upstream string is cloned verbatim into ConfigError::UnresolvedReference.name.
      3. (hop) monguard/src/config/error.rs:70 in impl Display for ConfigError — Display writes {name}

        with plain {} formatting — no escaping or truncation.

      4. (hop) monguard/src/config/loader.rs:120 in impl Display for ReloadError — ValidationFailed arm appends each ConfigError via {e} into one multi-line message.
        # (sink) monguard/src/runtime/reload.rs:146 in ReloadService::handle_reload — tracing::error!(error = %e, shouldIngest=true, shouldAlert=true) emits the string through the logv2 StructuredLogLayer JSON encoder.

        h2. Root cause
        - `ConfigError` Display arms interpolate config-derived strings with bare `{}` and no length cap or control-char escaping (monguard/src/config/error.rs:48,52,56,60,70).
        - `validate_listener_references` clones the raw YAML `port.upstream` string straight into `UnresolvedReference.name` (monguard/src/config/resolver.rs:890); `resolve_required_file` clones the raw YAML path into `FileNotFound.path` (resolver.rs:683).
        - `ReloadError` Display concatenates every `ConfigError` via `{e}

        ` into one multi-line string (monguard/src/config/loader.rs:117-122).

      • `ReloadService::handle_reload` logs that string as `error = %e` with `shouldIngest=true, shouldAlert=true` (monguard/src/runtime/reload.rs:142-148); the surrounding tracing subscriber is the logv2 StructuredLogLayer JSON encoder (monguard/src/observability/tracing.rs:295-311,374,422), which escapes the value, and the HTTP path serializes via serde_json (monguard/src/http/validate.rs:306).

      Filed from Aegis finding e3cf02ade04d (scan scan-3c7821728fe9) · primitive: Attacker-chosen YAML string flows verbatim through error Display into a single JSON-encoded Atlas alert log record. · categories: Log Injection

            Assignee:
            Unassigned
            Reporter:
            Anand Paithankar
            Votes:
            0 Vote for this issue
            Watchers:
            1 Start watching this issue

              Created:
              Updated: