Kai Ole Hartwig
20 Min. Lesezeit
Von

GitLab CVE-2026-110264: Eine Org-Datei lässt den Regex-Compiler von Ruby überlaufen

GitLab zeigt Dateien im Org-Format nicht als Rohtext, sondern rendert sie mit dem Gem org-ruby zu HTML, und das seit Version 7.0 aus dem Jahr 2014. Dabei baut org-ruby aus selbst definierten TODO-Schlüsselwörtern der Datei einen regulären Ausdruck, ohne das Wort dafür zu escapen. Enthält ein solches Schlüsselwort genug Wiederholungen, zählt die Regex-Engine Onigmo ihre internen Repeat-IDs über die Grenze eines vorzeichenbehafteten 16-Bit-Werts hinaus und schreibt beim Matching außerhalb des vorgesehenen Speichers. CVE-2026-110264 macht daraus Codeausführung als Nutzer git, ausgelöst durch den Aufruf einer öffentlichen Seite. Der Fix steckt deshalb nicht im GitLab-Code, sondern im Ruby, das GitLab mitliefert.

TL;DR — 90 Sekunden

Von der Org-Datei zum Speicherfehler

Org ist das Textformat des Emacs-Modus Org, und GitLab behandelt es ähnlich wie Markdown oder AsciiDoc. Öffnet jemand eine Datei mit der Endung .org im Repository-Browser, reicht Workhorse die Anfrage an einen Puma-Worker weiter, in dem die Rails-Anwendung läuft. Dort landet sie über Gitlab::OtherMarkup und das Gem gitlab-markup bei org-ruby, das die Datei in HTML übersetzt. Laut den Detection-Regeln, die GitLab am 20. Oktober veröffentlicht hat, arbeitet GitLab dabei mit org-ruby 0.9.12 und einem Zeitbudget für das Rendern. Das Gemfile von GitLab 7.0 aus dem Juni 2014 führt org-ruby erstmals auf, und genau ab dieser Version gilt die Lücke.

Org-Dateien dürfen eigene Statuswörter für Aufgaben festlegen. Eine Zeile wie #+TODO:, #+SEQ_TODO: oder #+TYP_TODO: ersetzt die üblichen Wörter TODO und DONE durch eigene Begriffe, und der Parser muss sie danach in jeder Überschrift wiedererkennen. org-ruby erledigt das mit regulären Ausdrücken. Beim Aufbau der gemeinsamen Alternation escaped es die Schlüsselwörter noch korrekt, an einer zweiten Stelle setzt es das rohe Wort aber direkt in Regexp.new ein. Was in der Datei steht, wird damit zum Muster, und das Muster wird zum Programm.

Warum das mehr ist als ein Schönheitsfehler, zeigt der Blick in die Regex-Engine. Ruby übersetzt jeden regulären Ausdruck mit Onigmo in einen Bytecode, den ein in C geschriebener Interpreter danach gegen den Text ausführt. Jede Wiederholung mit Bereichsangabe bekommt dabei eine eigene Repeat-ID, über die der Interpreter beim Matching seinen Zähler im Speicher findet. Laut dem Omnibus-Merge-Request zum Fix speichert Onigmo diese IDs als vorzeichenbehafteten 16-Bit-Operanden im Bytecode, zählt sie beim Kompilieren aber in einem gewöhnlichen int. Wird die Grenze von 32.767 überschritten, kompiliert das Muster ohne Fehler, der Operand ist aber negativ. Beim Matching greift der Interpreter dann mit diesem negativen Index auf Speicher vor seinem Zählerfeld zu und schreibt dort hinein.

Der Fehler steckt damit in zwei Schichten zugleich. org-ruby gibt dem Inhalt einer fremden Datei die Macht, einen regulären Ausdruck zu formen, und Onigmo verlässt sich darauf, dass kein Muster so groß wird, dass seine eigenen Zähler überlaufen. Laut GitLab lässt sich der Out-of-Bounds-Write mit gezielter Vorbereitung des Heaps über den Request-Body in einen Aufruf von system() verwandeln. Das Zeitbudget für das Rendern ändert daran nichts, denn es begrenzt nur, wie lange gerendert wird, und nicht, was dabei im Speicher passiert.

Der Fix im mitgelieferten Ruby

Im GitLab-Code selbst findet sich kein öffentlicher Fix, org-ruby bleibt im aktuellen Gemfile auf ~> 0.9.12 gepinnt. Repariert wird stattdessen die Engine: Der Omnibus-Merge-Request !9921, gemergt am 14. Oktober, portiert den Ruby-Pull-Request 16801 mit dem Titel „Reject overflowed repeat/null-check IDs in huge regexps“. Danach prüft der Compiler, ob Repeat- und Null-Check-IDs noch in ihren Operanden Platz haben, und lehnt ein zu großes Muster mit einem RegexpError ab, etwa mit der Meldung „too many range repeat“. Ein Programm mit negativem Index erreicht den Interpreter damit gar nicht erst. Upstream steht dieser Fix laut Merge-Request bisher nur im Entwicklungszweig, aus dem Ruby 4.1 wird, und nicht in den Zweigen 3.3, 3.4 und 4.0. GitLab patcht seine Ruby-Builds deshalb selbst, im Linux-Paket wie in den Cloud-Native-Images.

Für die älteren Linien fallen die Backports größer aus. Der Backport-Merge-Request !9927 für 19.1 bündelt drei Ruby-Patches: diesen Repeat-ID-Fix, einen Fix für einen Double Free bei Bereichswiederholungen und ein Limit für die Programmgröße. Die beiden letzten hatte GitLab am 12. Oktober schon mit den Releases zur Schwesterlücke CVE-2026-97229 in den CI-Regeln ausgeliefert, und der Release-Post führt den Double-Free-Patch auch für 19.0.10 und 18.11.13 auf. Dass GitLab die Engine repariert und nicht den Aufrufer, lese ich als bewusste Entscheidung: GitLab sichert die gemeinsame Senke ab, weil sich nicht jeder Weg zuverlässig finden lässt, auf dem Nutzereingaben zu regulären Ausdrücken werden. Die Kehrseite ist, dass der unescapte Aufruf in org-ruby bestehen bleibt und der Schutz allein an der Prüfung im Compiler hängt.

Vom öffentlichen Projekt zur Shell als git

Der CVSS-Vektor beschreibt einen Angriff ohne Anmeldung und ohne Mitwirkung eines Nutzers. Das stimmt für das Auslösen, verdient aber einen genaueren Blick. Laut GitLabs Detection-Regel braucht der Angreifer nur ein öffentliches Projekt, das die präparierte Datei enthalten kann. Die Datei muss also zuerst irgendwo liegen, und danach genügt ein anonymer Aufruf der Seite, die sie rendert. Am einfachsten gelingt das auf Instanzen mit freier Registrierung, denn dort legt sich der Angreifer ein Konto und ein eigenes öffentliches Projekt an.

Ein zweiter Weg führt über die Vorschau im Editor. Sie wählt den Renderer nach dem Parameter file_path, den der Client mitschickt, und kann so den Org-Renderer auch für eine Datei erzwingen, die gar nicht auf .org endet. Ob dieser Weg auch ohne Anmeldung funktioniert, geht aus GitLabs Angaben nicht hervor, das Beispiel-Log in der Detection-Regel zeigt einen angemeldeten Nutzer.

Der Angriff selbst ist laut GitLab kein sauberer Einzeltreffer, sondern ein lautes Durchprobieren mit vielen abstürzenden Puma-Workern, bis die Speicherbelegung passt. Gelingt er, läuft der Code mit den Rechten des Nutzers git, unter dem im Linux-Paket auch Puma und Sidekiq arbeiten. Wie groß der Schaden wird, hängt davon ab, was dieser Nutzer lesen kann. Die Datei /etc/gitlab/gitlab-secrets.json gehört root und hat den Modus 0600, sie ist für git also nicht direkt lesbar. Die daraus erzeugte secrets.yml unter /var/opt/gitlab/gitlab-rails/etc/ liegt laut Omnibus-Rezept aber mit 0644 auf der Platte und enthält unter anderem db_key_base, secret_key_base, otp_key_base und openid_connect_signing_key. Die database.yml mit den Zugangsdaten zur Datenbank kann git über die Gruppe lesen. Damit liegen verschlüsselte Spalten und ihr Schlüssel in einer Hand. GitLab selbst nennt als Folge den Zugriff auf alle CI/CD-Variablen, Runner-Tokens und Access-Tokens sowie Lese- und Schreibrechte auf jedes Repository.

Akut betroffen ist damit jede self-managed Instanz, die aus dem Internet erreichbar ist und öffentliche Projekte zulässt, mit offener Registrierung als schlimmstem Fall. Instanzen ohne öffentliche Projekte und ohne Selbstregistrierung sind für Fremde deutlich schwerer zu erreichen. Für jeden, der dort schon ein Konto mit Schreibrecht hat, bleibt die Lücke trotzdem eine Codeausführung auf dem Server. Trügerisch sicher fühlen dürften sich viele, die am 12. Oktober wegen CVE-2026-97229 auf 19.4.2, 19.3.5 oder 19.2.8 gegangen sind, denn diese Versionen enthalten den neuen Fix nicht. GitLab.com ist laut Release-Post bereits gepatcht, und Mandanten von GitLab Dedicated auf 19.3.5-internal0, 19.4.2-internal0 oder neuer müssen nichts tun.

CVEBetroffenFixVoraussetzungCVSS
CVE-2026-110264GitLab CE/EE ab 7.0 vor 18.11.13, 19.0 vor 19.0.10, 19.1 vor 19.1.9, 19.2 vor 19.2.9, 19.3 vor 19.3.6, 19.4 vor 19.4.3, 19.5 vor 19.5.119.5.1 / 19.4.3 / 19.3.6, Backports 19.2.9 / 19.1.9 / 19.0.10 / 18.11.13Öffentliches Projekt mit präparierter .org-Datei, Auslösen ohne Anmeldung10.0 (kritisch)
CVE-2026-97229, Schwesterlücke19.2 vor 19.2.8, 19.3 vor 19.3.5, 19.4 vor 19.4.219.4.2 / 19.3.5 / 19.2.8Regulärer Ausdruck in den rules einer CI-Konfiguration10.0 (kritisch)
CVE-2026-15723, gleiches Release10.1 bis vor 19.3.6, 19.4 vor 19.4.3, 19.5 vor 19.5.119.3.6 / 19.4.3 / 19.5.1, nicht in den BackportsAngemeldet, OmniAuth-SAML und LDAP-Auto-Linking aktiv8.2 (hoch)

Der Release-Post enthält insgesamt zwölf Sicherheitsfixes, die übrigen elf stecken aber nur in 19.3.6, 19.4.3 und 19.5.1. Eine Kette aus zwei Lücken beschreibt keine Quelle, ich halte sie aber für denkbar: Auf einer Instanz ohne freie Registrierung könnte jemand mit einem beliebigen Konto über CVE-2026-15723 ein LDAP-Konto übernehmen und so das Schreibrecht gewinnen, das ihm für das Einschleusen der Datei fehlt. Wer nur auf einen der Backports geht, lässt diese Lücke offen.

Einen bestätigten öffentlichen Exploit nennen weder GitLab noch die Fachpresse, und GitLab meldet keine Hinweise auf Ausnutzung. Für die Schwesterlücke CVE-2026-97229 hat Rapid7 nach eigener Aussage einen funktionierenden Exploit gebaut, der Code als git ausführt. Dass sich diese Fehlerfamilie verwerten lässt, ist damit belegt. Im CISA-KEV-Katalog steht CVE-2026-110264 laut The Hacker News nicht (Stand 22. Oktober), und die EPSS-Schnittstelle von FIRST lieferte am 22. Oktober noch keinen Wert. Gemeldet hat die Lücke joaxcar über HackerOne, laut The Hacker News seine vierte RCE über eine Regex-Engine innerhalb eines Monats.

GitLab 19.5.1 und der Weg über die Required Stops

Vollständig behoben ist alles in 19.5.1, 19.4.3 und 19.3.6. Für ältere Linien hat GitLab außerhalb des normalen Support-Fensters die Backports 19.2.9, 19.1.9, 19.0.10 und 18.11.13 gebaut, die laut den Versionsangaben im Release-Post nur CVE-2026-110264 schließen. Workarounds über gitlab.rb oder die Admin-Einstellungen nennt der Release-Post nicht, und eine Einstellung, die nur das Org-Rendering abschaltet, gibt es nicht. Wer noch auf 18.10 oder älter läuft, bekommt keinen gepatchten Release für seine Linie und muss über die Required Stops gehen, die die Upgrade-Doku vorgibt, mindestens bis 18.11.13. Die Stops liegen derzeit bei 18.2, 18.5, 18.8, 18.11, 19.2, 19.5, 19.8 und 19.11, und das Upgrade Path Tool von GitLab rechnet den Weg für deine Ausgangsversion aus.

Einige Stolperfallen kündigt der Release-Post an. 19.3.6 und 19.4.3 bringen reguläre Datenbank-Migrationen mit, 19.4.3 zusätzlich eine Post-Deploy-Migration. Eine Single-Node-Instanz hat dadurch eine kurze Downtime, Multi-Node-Setups können nach dem Zero-Downtime-Verfahren aktualisieren. Lehnt Gitaly einen Archiv-Download (GetArchive) wegen Überlast ab, antwortet Workhorse ab 19.3.6, 19.4.3 und 19.5.1 mit HTTP 429, was Monitoring und Clients auffallen kann. Außerdem aktualisiert das Release gitlab-markup auf 2.2.0 und rdoc auf 8.1.0. Für Helm-Installationen gehört zu 19.5.1 die Chart-Version 10.5.0.

Installationen aus dem Quellcode verdienen besondere Aufmerksamkeit. Weil der Fix im mitgelieferten Ruby sitzt und nicht im Rails-Code, dürfte ein Update von GitLab allein nicht schützen, wenn du ein eigenes Ruby der Linien 3.3, 3.4 oder 4.0 verwendest. Der Release-Post äußert sich dazu nicht. Meine Einschätzung stützt sich auf den Omnibus-Merge-Request !9921, der den Fix upstream nur im Entwicklungszweig von Ruby verortet, und ich würde sie vor einem Rollout mit dem Plausibilitätscheck unten absichern. Im Linux-Paket kannst du nach dem Update plausibel prüfen, ob der Patch angekommen ist. Aus den Fehlermeldungen, die der Omnibus-Merge-Request nennt, leite ich ab: Die mitgelieferte libruby unter /opt/gitlab/embedded sollte die neue Fehlermeldung „too many range repeat“ enthalten. Das ist ein Plausibilitätscheck und keine offizielle Prüfanleitung.

Härtung über den Patch hinaus

Vor den einzelnen Maßnahmen lohnt die Frage, was gegen diesen Mechanismus überhaupt wirken kann. Der Fehler sitzt in einem Parser, den GitLab für jeden Besucher ausführt, der eine Datei ansieht, und er wird in genau dem Prozess ausgelöst, der ohnehin alle Schlüssel der Instanz kennt. Wirksam ist deshalb dreierlei: weniger Menschen, die Inhalte ablegen und Seiten aufrufen können, weniger Wege aus dem Prozess heraus und weniger, was ein kompromittierter Prozess wert ist. Was nur den Container gegen Ausbrüche härtet, schützt das, was GitLab selbst lesen muss, nicht.

Keine öffentlichen Projekte

Der unauthentifizierte Pfad braucht eine öffentlich gerenderte Datei, und GitLab nennt das Zurückfahren öffentlicher Sichtbarkeit selbst als Überbrückung bis zum Patch. Unter Admin > Settings > General > Visibility and access controls legst du mit „Restricted visibility levels“ fest, dass nur Administratoren öffentliche Gruppen, Projekte und Snippets anlegen dürfen. Die Doku nennt zwei Grenzen: Die Einschränkung gilt nicht für Projekte in persönlichen Namespaces, und ein Level, das als Standard gesetzt ist, lässt sich nicht einschränken. Dass bestehende öffentliche Projekte dadurch umgestellt werden, sagt die Doku nicht, du musst sie also selbst finden und prüfen. Für eine Firmeninstanz ist „public“ ohnehin selten nötig, und „internal“ reicht für das meiste, was Teams untereinander teilen.

Geschlossene Registrierung

Ohne eigenes Konto kommt ein Fremder nicht an ein Projekt, in das er schreiben darf. Unter Admin > Settings > General > New user account restrictions schaltest du „Allow new user accounts“ ab oder verlangst mit „Require admin approval for new user accounts“ zumindest die Freigabe durch einen Admin, auf Wunsch kombiniert mit einer Allowlist für E-Mail-Domains. Laut Doku darf ab Werk jeder ein Konto anlegen, der die Instanz erreicht, und nur neue Instanzen verlangen standardmäßig die Admin-Freigabe. Ältere Installationen, an deren Einstellungen nie jemand etwas geändert hat, solltest du deshalb gezielt prüfen. Gegen bestehende Konten hilft die Maßnahme nicht, sie begrenzt den Kreis möglicher Angreifer aber auf Menschen, die du kennst.

Keine offene Erreichbarkeit aus dem Internet

Am stärksten wirkt, was einen unbekannten Angreifer gar nicht erst bis zur Instanz kommen lässt. GitLab empfiehlt in seiner Doku zur Reaktion auf Sicherheitsvorfälle, ein- und ausgehenden Netzzugriff auf autorisierte Nutzer und Server zu beschränken. Für eine Instanz, die nur das eigene Team nutzt, heißt das VPN, ein Zero-Trust-Zugang oder zumindest eine IP-Allowlist vor dem Load Balancer. Öffentliche Open-Source-Instanzen können das naturgemäß nicht umsetzen. Für die meisten Firmeninstanzen ist es aber die Maßnahme, die auch gegen die nächste Lücke dieser Familie hilft, und dass eine nächste kommt, ist nach vier Funden in einem Monat wahrscheinlich.

Egress-Sperre für die Webservice-Pods

Nach einer Codeausführung will ein Angreifer meist eine Verbindung nach außen aufbauen, um Werkzeuge nachzuladen oder Daten abzuziehen. Das Helm-Chart bietet für den Webservice dafür die Werte networkpolicy.enabled, networkpolicy.egress.enabled und networkpolicy.egress.rules, die ab Werk auf false beziehungsweise leer stehen. Ist Egress aktiv, blockiert die Policy jede ausgehende Verbindung, für die keine Regel existiert. Beim Beispiel in der Chart-Doku lohnt ein zweiter Blick, denn es gibt neben Gitaly auf Port 8075, KAS, Datenbank, Redis und DNS auch 0.0.0.0/0 (ohne 10.0.0.0/8) auf Port 443 frei, und genau diese Regel öffnet einer Reverse Shell wieder die Tür. Die Doku begründet sie mit externem Object Storage. Ersetze sie deshalb durch die konkreten Ziele, die deine Instanz wirklich braucht, etwa die Endpunkte deines Object Storage.

Die Grenze der Maßnahme ist deutlich. Datenbank, Redis und Gitaly müssen erreichbar bleiben, und genau dort liegen die wertvollen Daten. Die Sperre verhindert also nicht den Zugriff, sie erschwert den Abtransport und das Nachladen. Im Linux-Paket erreichst du dasselbe mit einer Host-Firewall für ausgehende Verbindungen des Nutzers git, was ich als Übertragung des Prinzips ableite und nicht aus der GitLab-Doku habe.

Secrets außerhalb der Reichweite von Rails

Der härteste Teil dieses Falls ist der Schlüssel db_key_base. Mit ihm verschlüsselt GitLab sensible Spalten in der Datenbank, darunter CI/CD-Variablen und 2FA-Daten, und laut Backup-Doku gehört die Datei mit diesem Schlüssel deshalb nie an denselben Ort wie die verschlüsselten Daten. Ein kompromittierter Rails-Prozess hat aber beides. Dazu kommt, dass sich db_key_base offiziell nicht rotieren lässt, das zugehörige Issue 25332 ist bis heute offen. Ist dieser Schlüssel einmal abgeflossen, kannst du Variablen und Tokens austauschen, aber nicht den Schlüssel, mit dem sich alte Backups weiter entschlüsseln lassen. Deshalb lohnt sich hier aus meiner Sicht Vorsorge mehr als Nachsorge. Wirklich wertvolle Zugangsdaten für Pipelines gehören in einen externen Secrets-Manager, aus dem Jobs sie zur Laufzeit holen, statt als CI/CD-Variable in der GitLab-Datenbank zu liegen.

Auch das hat eine Grenze, die du kennen solltest. Jobs melden sich bei einem externen Secrets-Manager in der Regel mit einem ID-Token an, das GitLab signiert, und der dafür genutzte openid_connect_signing_key steht ebenfalls in secrets.yml. Was ich daraus ableite: Ein Angreifer mit diesem Schlüssel könnte sich eigene ID-Tokens ausstellen. Rollen im Secrets-Manager sollten deshalb eng an Claims wie Projekt, Branch und geschützte Refs gebunden sein und nur kurzlebige, schmale Rechte vergeben. Das verhindert den Missbrauch nicht, macht ihn aber teurer und im Audit-Log des Secrets-Managers sichtbar.

Rate Limits für anonyme Anfragen

GitLab beschreibt den Angriff als Durchprobieren mit vielen Fehlversuchen. Unter Admin > Settings > Network > User and IP rate limits lassen sich Limits für nicht angemeldete Web- und API-Anfragen einschalten, die laut Doku ab Werk aus sind und nach dem Aktivieren mit 3600 Anfragen pro 3600 Sekunden starten. Ein Limit verlangsamt das Durchprobieren, verhindert es aber nicht, und ein Angreifer mit vielen IP-Adressen umgeht es leicht. Ich sehe darin eine Ergänzung und keinen Schutz.

Wirkungslose Maßnahmen

Das Helm-Chart setzt für den Webservice bereits vernünftige Werte: runAsNonRoot: true, allowPrivilegeEscalation: false, capabilities.drop: ["ALL"] und ein seccomp-Profil RuntimeDefault. Diese Einstellungen erschweren einen Ausbruch aus dem Container und eine Rechteausweitung, gegen diesen Fall sind sie aber weitgehend wirkungslos. Der Code läuft im selben Prozess, der die Rails-Secrets und die Datenbank-Zugangsdaten für seine Arbeit legitim braucht, und genau diese Daten sind das Ziel. Dasselbe gilt für ein schreibgeschütztes Root-Dateisystem, das die Chart-Doku für den Webservice ohnehin nicht erwähnt.

Auch eine WAF-Regel, die Pfade mit .org sperrt, ist keine Mitigation. Die Editor-Vorschau wählt den Renderer nach einem Parameter und nicht nach dem Pfad, und weitere Ansichten rendern Dateien ebenfalls. Die Einstellung „Filtering outbound requests“ wirkt nur auf Anfragen der Anwendung selbst, etwa für Webhooks, und nicht auf Prozesse, die ein Angreifer per system() startet. Mehr zu Egress-Regeln, Mandantentrennung und Netzwerksegmentierung findest du in der Serie TYPO3 auf Kubernetes unter IT-Grundschutz.

Erkennung und Prüfung

Ob du betroffen bist, verrät dir zuerst die Version, die du im Admin-Bereich oder unter /help deiner Instanz findest. Alles vor den genannten Fix-Versionen ist verwundbar, und 19.4.2, 19.3.5 und 19.2.8 sind es ausdrücklich ebenfalls. Danach lohnt der Blick auf die Angriffsfläche: Gibt es öffentliche Projekte, ist die Registrierung offen, und ist die Instanz aus dem Internet erreichbar?

Ob es jemand versucht hat, kannst du mit den Detection-Regeln prüfen, die GitLab am 20. Oktober veröffentlicht hat. Die wichtigste sucht in exceptions_json.log nach einer Häufung von Timeout::Error aus OtherMarkup beim Rendern von .org-Dateien im Projects::BlobController#show und schlägt bei fünf solchen Ereignissen pro Client in 30 Minuten an. Anonyme Angreifer erscheinen dort als ip/<Adresse>. Eine zweite Regel sucht in production_json.log nach Aufrufen von Projects::BlobController#preview, deren file_path auf .org endet, obwohl die URL keine Org-Datei nennt, und jeder Treffer ist hier verdächtig. Abstürzende Worker hinterlassen in puma_stderr.log einen Eintrag mit [BUG] und Frames aus org-ruby oder Onigmo, und Workhorse protokolliert dazu eine 502, während der Request in den Rails-Logs fehlt. Korreliere deshalb über Host und Zeit und nicht über die Correlation-ID. GitLab weist darauf hin, dass puma_stderr.log und das Workhorse-Log oft gar nicht zentral gesammelt werden, und das solltest du ändern.

In den Repositorys selbst kannst du nach Org-Dateien mit übergroßen Zeilen #+TODO:, #+SEQ_TODO: oder #+TYP_TODO: suchen. GitLab nennt dafür den Ausdruck ^\#\+(TODO|SEQ_TODO|TYP_TODO):.{500,}. Für die Zeit nach einem möglichen Erfolg empfiehlt GitLab, nach neuen Dateien des Nutzers git in /tmp, unerwarteten Kindprozessen von Puma, ausgehenden Verbindungen sowie geänderten Crontabs oder SSH-Schlüsseln dieses Nutzers zu suchen. Konkrete IoCs wie IP-Adressen, Hashes oder Domains nennt GitLab bisher nicht.

Bei einem Verdacht nimmst du die Instanz vom Netz und sicherst die Logs so, dass sie sich nicht mehr ändern lassen. GitLabs Doku zu Sicherheitsvorfällen empfiehlt, den Host aus einem bekannt sauberen Backup oder ganz neu aufzubauen. Danach rotierst du alle CI/CD-Variablen, Runner-Tokens und Access-Tokens und prüfst die Admin-Konten sowie, falls deine Edition es bietet, das Credentials Inventory. Die Schlüssel aus gitlab-secrets.json gehören laut GitLabs Detection ebenfalls ausgetauscht, für db_key_base gibt es dafür aber keinen offiziellen Weg.

Betreiberempfehlung

Akut handeln solltest du, wenn deine self-managed Instanz aus dem Internet erreichbar ist und öffentliche Projekte zulässt, ganz besonders mit offener Registrierung. Aktualisiere dann sofort auf 19.5.1, 19.4.3 oder 19.3.6 und schränke bis dahin Sichtbarkeit und Registrierung ein. Prüfe danach die Logs der letzten Wochen mit GitLabs Detection-Regeln. Hast du am 12. Oktober aktualisiert, gilt das genauso, denn der damalige Patch deckt diese Lücke nicht ab.

Beobachten genügt hier eigentlich nie ganz, am ehesten noch bei einer Instanz, die nur über VPN erreichbar ist, keine öffentlichen Projekte hat und nur bekannte Konten kennt. Auch dann gehört das Update in die nächsten Tage, weil die Lücke für jeden angemeldeten Nutzer eine Codeausführung bleibt und du die übrigen elf Fixes des Releases nur mit 19.3.6 oder neuer bekommst.

Unabhängig vom Einzelfall gilt, was ich bei jeder Lücke sage: Patches und Updates gehören automatisiert. Bei GitLab heißt das, dass Renovate oder Dependabot neue Versionen des Linux-Pakets, des Docker-Images oder des Helm-Charts als Merge Request vorschlagen und die Pipeline das Upgrade gegen eine Staging-Instanz mit realistischen Daten testet. Dann ist ein Critical Patch Release wie dieser eine Sache von Stunden statt Wochen, auch wenn es der dritte innerhalb eines Monats ist. Eine Karenzzeit für Routine-Updates schützt vor kompromittierten Releases, Sicherheitsupdates nimmst du davon aus. Die Migrationen in 19.3.6 und 19.4.3 oder Nebenwirkungen des rdoc-Updates fallen dann im Test auf und nicht in Produktion. Wer dagegen von Hand aktualisiert, verpasst in der Urlaubszeit schnell ein paar Releases und steht dann vor einem Upgrade über mehrere Required Stops, für das niemand ein Wartungsfenster hat. Genau so entstehen die Instanzen auf 18.10 und älter, für die es jetzt keinen Patch mehr gibt.

Häufige Fragen zu CVE-2026-110264

Ich habe am 12. Oktober aktualisiert. Bin ich geschützt?+

Nein. 19.4.2, 19.3.5 und 19.2.8 schließen nur die CI-Lücke CVE-2026-97229. Für CVE-2026-110264 brauchst du 19.5.1, 19.4.3, 19.3.6 oder einen der Backports.

Meine Instanz hat keine öffentlichen Projekte. Muss ich trotzdem patchen?+

Ja. Fremde kommen dann schwerer an die Lücke, aber jeder angemeldete Nutzer mit Schreibrecht kann sie weiter ausnutzen. Patche zeitnah, auch wenn der Druck geringer ist.

Reicht es, .org-Dateien am Reverse Proxy zu sperren?+

Nein. Die Editor-Vorschau wählt den Renderer über einen Parameter und nicht über den Pfad. Eine Sperre auf Pfade lässt sich deshalb umgehen und ersetzt das Update nicht.

Welche Secrets muss ich nach einem Verdacht austauschen?+

Alle CI/CD-Variablen, Runner-Tokens und Access-Tokens sowie die Schlüssel aus gitlab-secrets.json. Für db_key_base gibt es keinen offiziellen Rotationsweg. Plane deshalb auch den Neuaufbau der Instanz ein.

Fazit

CVE-2026-110264 ist nicht der erste schwere Fehler in Rubys Regex-Engine, den GitLab in diesem Herbst patchen musste, und das Muster ist jedes Mal dasselbe. Irgendwo wird aus Nutzereingaben ein regulärer Ausdruck, und ein regulärer Ausdruck ist für eine C-Engine ein Programm, das sie ausführt. In diesem Fall saß die Quelle seit 2014 in einem Gem für ein Nischenformat, das kaum jemand auf dem Schirm hatte. Die übertragbare Lehre hat zwei Teile. Jede Stelle, an der Eingaben zu Mustern, Vorlagen oder Abfragen werden, ist eine Angriffsfläche für Codeausführung, auch wenn sie nach einer Formatierungsfunktion aussieht. Und weil sich solche Stellen nie vollständig finden lassen, zählt am Ende, was ein kompromittierter Prozess erreichen kann. Ein Schlüssel wie db_key_base, der sich nicht austauschen lässt, macht aus einem Einbruch einen dauerhaften Schaden. Deshalb lohnt es sich, wertvolle Geheimnisse gar nicht erst dort abzulegen, wo der Webserver sie lesen kann.

Quellen

Ich prüfe deine self-managed GitLab-Instanz auf Erreichbarkeit, Sichtbarkeit und Secret-Ablage, setze Egress-Regeln um und automatisiere den Rollout von GitLab-Patches.

GitLab-Audits für Linux-Paket und Helm, Upgrade-Pfade über die Required Stops, NetworkPolicies für Webservice-Pods und Incident-Response mit Secret-Rotation.

Plattform-Betrieb statt Beratung auf Papier: Ich prüfe, patche und härte deine Infrastruktur laufend.

Über den Autor