취약점 데이터베이스GHSA-2vcr-56fq-97rw

GHSA-2vcr-56fq-97rw: 
취약점 분석 및 완화

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 a generated union's ValueByDiscriminator() switch by using a discriminated oneOf/anyOf whose $ref branch has no explicit mapping entry. The implicit mapping key is derived as the raw schema name and emitted unescaped into a Go case "..." label. The injected code executes whenever the generated package is built or imported. Reachable from a single local OpenAPI file with default flags.

Root Cause

ValidateSpec validates a discriminator's explicit mapping keys with checkText (rejects ", backtick, control chars) — but it does so by iterating s.Discriminator.Mapping, which kin-openapi leaves empty when the mapping is implicit. Component schema names are validated only by checkNoControl, which permits double-quotes and backticks (only control chars are rejected). When a discriminated union has a $ref branch with no explicit mapping entry, generateUnion derives the mapping key implicitly as RefPathToObjName(element.Ref) — the raw last /-segment of the ref, returned verbatim (utils.go) — storing it into Discriminator.Mapping. DiscriminatorCases returns it unchanged as DiscriminatorCase.Value, which is emitted raw inside case "{{.Value}}": (templates/union.tmpl). No strconv.Quote/toGoString/checkText sits between the map and the sink. On v2.8.1 the union code was refactored: the old v2/v3 template split is gone and generateUnion is now called unconditionally for every oneOf/anyOf, with a single union.tmpl switch-case path — so this is reachable without any schema-merging-behavior flag.

Impact

Arbitrary Go code injection at build time (CWE-94). An attacker controlling an OpenAPI spec injects top-level declarations / func init() into generated union code, achieving code execution on the build/CI host. Same trust boundary and impact class as the project's existing code-injection advisories; broader reach than the external-$ref variant (needs only one local file).

Attack Chain

  1. Entry: single local spec defines a component schema whose name contains a double quote, e.g. components.schemas.'Dog"+someExec()+"', and a Pet: oneOf: [$ref .../Cat, $ref .../Dog"+someExec()+"] with discriminator: {propertyName: petType} and no mapping. Run oapi-codegen -generate types spec.yaml (default, no flags).
    • Guard: ValidateSpec -> walkComponents validates the schema name; walkSchema validates explicit discriminator mapping keys.
    • Bypass proof: the schema name is checked only by checkNoControl (validate_spec.go), which permits " and backtick. There is no explicit mapping, so the checkText loop over s.Discriminator.Mapping never runs on this key — the map is empty in the spec.
  2. Loader survival: kin-openapi keeps the "-bearing name and resolves the ref.
    • Bypass proof (empirical): schema name Dog"+someExec()+" survives as the components map key verbatim; the local $ref to it resolves (.Value != nil) with .Ref preserved including the "; Discriminator.Mapping is nil/empty at load. kin runs no key-grammar check at load, and oapi-codegen never calls kin's Validate().
  3. Implicit key derivation: generateUnion (called unconditionally on v2.8.1).
    • Guard: an explicit mapping key, if present, would be used.
    • Bypass proof: with no mapping, outSchema.Discriminator.Mapping[RefPathToObjName(element.Ref)] = elementSchema.GoType runs (schema.go). RefPathToObjName("#/components/schemas/Dog\"+someExec()+\"") returns the last /-segment Dog"+someExec()+" verbatim. No checkText/quote-escape on this path.
  4. Sink: DiscriminatorCases returns {Value: "Dog\"+someExec()+\"", ...} unchanged; emitted at case "{{.Value}}": (templates/union.tmpl).
    • Guard: sink quoting.
    • Bypass proof: .Value is interpolated raw between " and ":. The result is case "Dog"+someExec()+"": — the embedded " closes the Go string literal and the remainder is emitted as live Go. A fuller name closes the case/switch/func and injects a top-level func init(). checkText never touched this key.
  5. Impact: arbitrary Go injected into the generated union helper; executes on build/import.

Bypass Evidence (executed offline against kin-openapi loader)

-> LOAD OK
-> schema name key: Dog"+someExec()+"
-> branch[1] Ref="#/components/schemas/Dog\"+someExec()+\""  resolved=true
-> Discriminator.Mapping empty at load (implicit mapping)

The quote-bearing name survives as the components key and is reachable via a resolving $ref, while checkText never sees the implicit key.

Proof of Concept


# spec.yaml — single local file, default flags
components:
  schemas:
    Cat: {type: object, properties: {petType: {type: string}}}
    'Dog";}; func init(){ exec.Command("touch","/tmp/PWNED").Run() }; func _(){ switch x {case "x':
          {type: object, properties: {petType: {type: string}}}
    Pet:
      oneOf: [{$ref: '#/components/schemas/Cat'},
              {$ref: '#/components/schemas/Dog";}; func init(){ exec.Command("touch","/tmp/PWNED").Run() }; func _(){ switch x {case "x'}]
      discriminator: {propertyName: petType}   # NO mapping
oapi-codegen -generate types -o gen.go spec.yaml

# gen.go's ValueByDiscriminator() switch now carries an injected top-level func init().

Affected Versions

Up to and including v2.8.1 (verified present on the v2.8.1 release tag post-union-refactor). Default configuration, default schema-merging, single local file, no external refs, no flags.

Suggested Fix

Escape/validate the implicit discriminator key before it reaches the template: apply strconv.Quote/toGoString to DiscriminatorCase.Value at the case sink, OR run checkText on RefPathToObjName(element.Ref) when deriving an implicit mapping key in generateUnion, OR validate component schema names with checkText (not just checkNoControl) since they flow into generated code positions beyond identifiers and comments.

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


소스: NVD

무료 취약점 평가

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

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

요청 평가

추가 Wiz 리소스

맞춤형 데모를 받아보세요

맞춤형 데모 신청하기

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