Skip to content
KitploitKITPLOIT
ToolsExploitsBlog
Log in
Submit
ToolsExploitsBlog
Submit

Hacking, PenTest, and Cybersecurity Tools for Your Security Arsenal!

Kitploit is a directory of hacking, cybersecurity, and pentesting tools. Discover the latest project updates to find vulnerabilities, analyze systems, automate testing, and strengthen your security.

FeedsContactPrivacy© 2026 Kitploit

Tool Directory

Categories

View all categories
Loading categories
Gitlab-CVE-2026-19478 — Dockerized exploit lab and script for CVE-2026-19478, a critical unauthenticated GitLab GraphQL code injection enabling arbitrary Ruby method calls, project deletion, and data exfiltration. | Kitploit
Tools/GitHubGitHub/punitdarji/gitlab-cve-2026-19478
Vulnerability AnalysisExploitationWeb Application ExploitationAPI Security TestingPenetration TestingLearning & EducationLabs & Practice
GitHub
punitdarji/gitlab-cve-2026-19478

Gitlab-CVE-2026-19478

Dockerized exploit lab and script for CVE-2026-19478, a critical unauthenticated GitLab GraphQL code injection enabling arbitrary Ruby method calls, project deletion, and data exfiltration.

View Repository
411 month agoNot yet reviewed

Most Popular

View all →

Discover the most used tools by our community.

Explore all tools

Browse our collection of tools

View all tools →
Share

CVE-2026-19478 — GitLab GraphQL @gl_introduced Directive Injection

Unauthenticated Remote Code Injection via GraphQL Directive in GitLab CE/EE — Delete Any Public Project With a Single HTTP Request

A hands-on penetration testing lab that reproduces CVE-2026-19478, a critical (CVSS 9.4) vulnerability in GitLab's GraphQL API. The @gl_introduced directive allows unauthenticated attackers to execute arbitrary Ruby methods on server-side objects — including project deletion, data exfiltration, and ownership transfer — with zero authentication.

This lab runs a real vulnerable GitLab CE 19.2.0 instance in Docker for realistic exploitation practice.

Table of Contents

  • Vulnerability Summary
  • How the Exploit Works
  • Attack Flow Diagram
  • Lab Setup
  • Exploitation Guide
  • Exploit Script Usage
  • Detection and Indicators of Compromise
  • Remediation
  • References
  • Disclaimer
  • Connect With Us

Vulnerability Summary

FieldValue
CVE IDCVE-2026-19478
CVSS Score9.4 (Critical)
ProductGitLab Community Edition (CE) / Enterprise Edition (EE)
Vulnerability TypeCode Injection / Arbitrary Method Execution (CWE-94)
Attack VectorNetwork (Remote)
AuthenticationNone required
User InteractionNone
Attack ComplexityLow
Affected Versions18.2 – 18.11.10, 19.0 – 19.0.7, 19.1 – 19.1.5, 19.2 – 19.2.3
Patched Versions18.11.11, 19.0.8, 19.1.6, 19.2.4
Discovered Byhiimguardian (via HackerOne)
Patch DateAugust 17, 2026

Impact

An unauthenticated remote attacker can:

  • Delete any public project permanently
  • Exfiltrate internal data, admin tokens, and secrets
  • Modify project visibility, ownership, and settings
  • Execute arbitrary Ruby methods on the server-side Project model
  • Archive or transfer projects without authorization

How the Exploit Works

The @gl_introduced Directive

GitLab uses a custom GraphQL directive @gl_introduced(version: "X.Y") to support rolling deployments. When a newer GitLab version adds a field to the GraphQL API, older instances handle queries referencing those new fields gracefully by returning null instead of erroring.

The Vulnerable Code Path

File: lib/gitlab/graphql/version_filter/future_field_fallback.rb (Lines 14-36)

Step-by-step breakdown:

  1. FutureFieldFilter scans incoming GraphQL queries. When a field has @gl_introduced(version) with a version newer than the current server, it strips the field and sets context[:contain_future_fields] = true.

  2. IntroducedTracer restores the original query document at execution time, putting the stripped fields back into the AST.

  3. FutureFieldFallback#get_field intercepts every field lookup during execution. It checks three conditions:

    • Is contain_future_fields flag set? ✅
    • Is the field absent from the schema? ✅
    • Does the name NOT start with __? ✅
    • Is the field name safe? ❌ No check exists!
  4. When all three checks pass, it synthesizes a new GraphQL::Schema::Field with no resolver class.

  5. In graphql-ruby, a field without a resolver resolves by calling object.public_send(field_name) on the underlying Ruby object — converting the attacker's field name into an arbitrary method call on the Project ActiveRecord model.

The Fix (19.2.4+)

GitLab's patch replaces implicit method dispatch with an explicit NilResolver that returns nil unconditionally, preserving rolling-deploy compatibility while eliminating arbitrary method execution:

# BEFORE (vulnerable) — no resolver → method dispatch
GraphQL::Schema::Field.new(name: field_name, type: String, owner: type)
# → object.public_send(field_name) ← ARBITRARY METHOD CALL

# AFTER (patched) — explicit NilResolver
GraphQL::Schema::Field.new(name: field_name, type: String, owner: type,
  resolver_class: NilResolver)  # ← always returns nil

Attack Flow Diagram

                    ATTACKER (unauthenticated)
                              │
                              │  POST /api/graphql
                              │  { project(fullPath: "victim/repo") {
                              │      name
                              │      destroy @gl_introduced(version: "99.0")
                              │  }}
                              │
                              ▼
               ┌──────────────────────────────┐
               │     GitLab GraphQL API        │
               │     (no auth required)        │
               └──────────────┬───────────────┘
                              │
                              ▼
               ┌──────────────────────────────┐
               │   1. FutureFieldFilter        │
               │   "destroy" has @gl_introduced│
               │   version 99.0 > 19.2.0      │
               │   → Strip field              │
               │   → Set contain_future_fields │
               └──────────────┬───────────────┘
                              │
                              ▼
               ┌──────────────────────────────┐
               │   2. IntroducedTracer         │
               │   → Restore original query   │
               │   "destroy" is back in AST   │
               └──────────────┬───────────────┘
                              │
                              ▼
               ┌──────────────────────────────┐
               │   3. FutureFieldFallback      │
               │   "destroy" not in schema? ✓  │
               │   Flag set? ✓                 │
               │   Not __introspection? ✓      │
               │   → Synthesize field          │
               │   → NO RESOLVER attached      │
               └──────────────┬───────────────┘
                              │
                              ▼
               ┌──────────────────────────────┐
               │   4. graphql-ruby resolution  │
               │   No resolver found →         │
               │   object.public_send(:destroy)│
               │                               │
               │   Project.find("victim/repo") │
               │          .destroy()           │
               │                               │
               │   ██ PROJECT DELETED ██        │
               └──────────────────────────────┘

Lab Setup

Prerequisites

  • Docker and Docker Compose installed
  • Minimum 4 GB RAM available for Docker (GitLab is resource-heavy)
  • Python 3 (for the exploit script)
  • Web browser or curl / httpie for API testing

Quick Start — Real GitLab CE 19.2.0 (Vulnerable)

# Clone or navigate to the lab directory
cd CVE-2026-19478

# Pull and start the vulnerable GitLab instance
docker compose up -d

# Wait for GitLab to fully start (3-5 minutes on first boot)
# Monitor startup progress:
docker logs -f gitlab-vulnerable

# Once you see "gitlab Reconfigured!" in logs, set up test projects:
bash setup-lab.sh

Access Points

ServiceURLCredentials
GitLab Web UIhttp://localhostroot / P@ssw0rd123!
GraphQL APIhttp://localhost/api/graphqlNone required
GraphQL Explorerhttp://localhost/-/graphql-explorerLogin required
SSHlocalhost:2222—

Lightweight Alternative (Simulated)

For machines with limited resources or for faster startup:

docker compose -f docker-compose.simulated.yml up --build -d
# Access at http://localhost:5000

Stop / Full Reset

# Stop the lab
docker compose down

# Full reset (removes all data volumes)
docker compose down -v

Exploitation Guide

Download Tool