취약점 데이터베이스GHSA-x3wv-qq4q-hj6r

GHSA-x3wv-qq4q-hj6r: 
취약점 분석 및 완화

Summary

oapi-codegen generates Go code from an OpenAPI specification. A malicious specification can inject arbitrary top-level Go (e.g. a func init()) into the generated types package by abusing a whole-document external $ref (a $ref whose target has no # fragment, e.g. ./evil.json). The injected code executes whenever the generated package is built or imported on the developer's or CI machine.

Root Cause

ValidateSpec (the sole spec gate — kin-openapi's own Validate() is never called) deliberately stops its walk at every $ref: walkSchemaRef early-returns whenever ref.Ref != "", on the documented assumption that "the contents of external documents are never copied into the generated output." For a whole-document reference this assumption is false:

  • IsGoTypeReference("./evil.json") = ref != "" && !IsWholeDocumentReference(ref) = true && !true = false (no # fragment, utils.go).
  • So GenerateGoSchema does NOT take the type-reference early-return (schema.go), and instead inlines the external object as a Go struct, emitting each external property name verbatim into a backtick-delimited struct tag (GenFieldsFromProperties, fmt.Sprintf(\%s:"%s"`, ...)then wrapped in backticks). The external subtree is therefore never routed throughcheckText`, which would otherwise reject backticks and newlines.

Impact

Arbitrary Go code injection at build time (CWE-94). An attacker who controls an OpenAPI spec (or an external document it references) injects top-level declarations / func init() into the generated code, achieving code execution on the build/CI host. Same trust boundary and impact class as the project's existing enum/path/field code-injection advisories.

Attack Chain

  1. Entry: main api.yaml declares components.schemas.Container.properties.evil: { $ref: ./evil.json }. Run oapi-codegen -generate types api.yaml (default flags, local file).
    • Guard: ValidateSpec(spec) (codegen.go) -> walkSchema(Container) -> walkSchemaRef(evil).
    • Bypass proof: evil.SchemaRef.Ref == "./evil.json" (non-empty), so walkSchemaRef early-returns on ref.Ref != "" (validate_spec.go) BEFORE walkSchema(ref.Value) — the external property names are never seen by checkText. Empirically: loaded doc shows evil.Ref="./evil.json" with .Value != nil.
  2. Inline-vs-reference decision: GenerateGoSchema(ctx, evil, ...) (schema.go).
    • Guard: if IsGoTypeReference(sref.Ref) early-return to a Go type name.
    • Bypass proof: IsGoTypeReference("./evil.json") = false (./evil.json has no #; confirmed by kin-openapi's own utils_test.go). Generation falls through to inline struct emission using sref.Value.
  3. Sink: GenFieldsFromProperties sets JsonFieldName: pName verbatim, feeds it through text/template (no escaping), assembles tags[i] = fmt.Sprintf(\%s:"%s"`, k, fieldTags[k])` and wraps the whole field in backticks.
    • Guard: the struct tag is a backtick raw-string literal, so a " in the name does NOT break out — the true breakout char is a backtick (closes the raw string), after which a newline + func init(){...} becomes top-level Go.
    • Bypass proof: an external property name containing a backtick and a newline survives the loader verbatim (empirically confirmed). checkText would reject both, but it never ran on this subtree (step 1). Post-gen imports.Process is a formatter, not a sanitizer; SkipFmt/format-error return the raw goCode anyway.
  4. Impact: arbitrary Go (func init(), extra top-level decls) injected into the generated types package; executes when the package is built/imported.

Bypass Evidence (executed offline against kin-openapi loader)

api.yaml:  components.schemas.Container.properties.evil: {$ref: ./evil.json}
evil.json: object with a property name containing a backtick, a newline, and `func init(){}`
-> LOAD OK
-> evil.Ref="./evil.json"   resolved(.Value!=nil)=true
-> external property name contains backtick=true newline=true

The backtick+newline-bearing name reaches the struct-tag sink unescaped because checkText is skipped by the $ref early-return.

Proof of Concept


# evil.json (local file next to api.yaml)
{"type":"object","properties":{
  "a` json:\"a\"`\nfunc init(){exec.Command(\"touch\",\"/tmp/PWNED\").Run()}\ntype _x struct{ A string `json:\"a":
     {"type":"string"}}}

# api.yaml
components: {schemas: {Container: {type: object, properties: {evil: {$ref: ./evil.json}}}}}

# Container must be referenced by a path/response, or run with skip-prune.
oapi-codegen -generate types -o gen.go api.yaml   # default flags

# gen.go now contains an injected top-level func init(){...}; `go build ./...` runs it.

Affected Versions

Up to and including v2.8.1 (verified present on the v2.8.1 release tag). Default configuration; no flag required for a local-file whole-document ref. A remote whole-document ref ($ref: https://host/evil.json) additionally requires --allow-remote-refs-from.

Suggested Fix

When walkSchemaRef encounters a whole-document reference (ref with no # fragment) whose .Value is populated and will be inlined, do NOT early-return — recurse into ref.Value so checkText validates the inlined property names. Alternatively, run checkText on every property name actually emitted into a struct tag, regardless of its source document.

Reported by zx (Jace) — GitHub: @manus-use


소스: NVD

무료 취약점 평가

클라우드 보안 포세를 벤치마킹하세요

9개 보안 영역에서 클라우드 보안 관행을 평가하여 위험 수준을 벤치마킹하고 방어의 허점을 파악하세요.

요청 평가

추가 Wiz 리소스

맞춤형 데모를 받아보세요

맞춤형 데모 신청하기

"내가 본 최고의 사용자 경험은 클라우드 워크로드에 대한 완전한 가시성을 제공합니다."
데이비드 에슬릭CISO
"Wiz는 클라우드 환경에서 무슨 일이 일어나고 있는지 볼 수 있는 단일 창을 제공합니다."
아담 플레처최고 보안 책임자(CSO)
"우리는 Wiz가 무언가를 중요한 것으로 식별하면 실제로 중요하다는 것을 알고 있습니다."
그렉 포니아토프스키위협 및 취약성 관리 책임자