Entre el 23 de setembre i el 21 d'octubre, GitLab va publicar tres tandes de pedaços crítics que corregeixen quatre vulnerabilitats diferents. Les quatre les va trobar la mateixa persona. Les quatre són al mateix lloc: Onigmo, el motor d'expressions regulars que Ruby porta a dins i que GitLab empaqueta juntament amb el seu propi intèrpret.
Això té dues conseqüències pràctiques, i cap de les dues no cap a la frase «ja ho hem pedaçat». La primera és que cada pedaç va deixar la versió que acabaves d'instal·lar dins del rang afectat de l'avís següent, tres vegades seguides. La segona és que l'arranjament de l'últim no és una línia del codi de GitLab: és un pedaç a l'intèrpret, i això canvia on cal mirar per saber si t'arriba.
Quatre CVE, tres tandes, un motor
Tot el que segueix surt de les tres notes de pedaç publicades per GitLab. L'investigador hi apareix acreditat amb l'àlies joaxcar a les tres, sempre a través del programa de recompenses de l'empresa a HackerOne.
| Tanda | Què corregeix | A qui arriba |
|---|---|---|
| 23 set 19.4.1 / 19.3.3 / 19.2.7 |
CVE-2026-89078 (9,9) CVE-2026-93577 (9,9) Tots dos amb PR:L: cal compte. |
19.2, 19.3, 19.4 |
| 12 oct 19.4.2 / 19.3.5 / 19.2.8 |
CVE-2026-97229 (10,0) Ja PR:N. A KEV des del 29. |
19.2, 19.3, 19.4 |
| 21 oct 19.5.1 / 19.4.3 / 19.3.6 / 19.2.9 / 19.1.9 / 19.0.10 / 18.11.13 |
CVE-2026-110264 (10,0)PR:N. No és a KEV. |
«all versions from 7.0 before 18.11.13», més les sis branques 19.x per sota de la seva correcció. Una dècada de versions. |
La columna del mig és la que explica la història, i aquesta lectura és nostra. Al setembre les dues fallades exigien un compte: el vector portava PR:L, privilegis baixos però privilegis. A l'octubre, dues vegades, el mateix investigador va tornar al mateix compilador i aquell requisit va desaparèixer. El que es veu a la taula és algú recorrent sistemàticament una superfície que estava sense explorar, i trobant-hi cada vegada una entrada més barata.
La cinta de córrer de les versions
Això és aritmètica dels rangs oficials. Si vas actualitzar el 23 de setembre et vas quedar a la 19.4.1, i l'avís del 12 d'octubre declara afectades les «19.4 before 19.4.2». Si llavors vas actualitzar a la 19.4.2, l'avís del 21 declara afectades les «19.4 before 19.4.3». Tres tandes, i després de cadascuna la versió acabada d'instal·lar torna a aparèixer a la llista. El mateix amb les branques 19.3 i 19.2, amb els seus números respectius.
No hi ha res a retreure a qui va aplicar el pedaç de pressa: va fer el correcte les tres vegades. El problema és que «ja està pedaçat» té data de caducitat i ningú no l'escriu enlloc. Contra això no protegeix reaccionar més de pressa al titular següent, sinó tenir una cadència escrita per a aquella màquina concreta: una finestra periòdica, un responsable amb nom i una comprovació posterior que la finestra s'ha complert. És la part menys vistosa del manteniment informàtic, i és la que converteix un avís en una tasca programada.
Un camp de 16 bits
L'explicació més clara de l'última fallada no és a cap nota de premsa. És a la petició de canvi amb què GitLab va incorporar l'arranjament al seu propi empaquetatge, escrita per a qui l'havia de revisar: «Onigmo stores range-repeat and null-check IDs in a signed 16-bit bytecode operand but counts them in an int, so a regexp with more than 32,767 of either wraps the ID negative. At match time the negative ID indexes the repeat stack, an out-of-bounds write».
Traduït: Onigmo numera internament les repeticions d'un patró. Compta aquests nombres en un enter gran, però els desa en un camp de 16 bits amb signe, de manera que passant de 32.767 l'identificador es dona la volta i es queda en negatiu. Més tard aquell nombre negatiu s'utilitza com a índex per accedir a una pila, i allà s'escriu fora de lloc. L'arranjament rebutja el patró en compilar-lo, amb un error, en comptes d'acceptar-lo. Convé dir que ningú no escriu per accident una expressió regular amb 32.768 repeticions: cal escriure-la a posta i cal tenir on ficar-la.
On viu cada arranjament
Aquí hi ha una distinció que val la pena fer bé, perquè els dos casos no són iguals. El del 12 d'octubre el va tancar GitLab al seu costat: la seva pròpia descripció diu que el problema era «insufficient regular expression validation», i es va corregir validant abans de compilar. El del 21 és una altra cosa. El seu arranjament és un fitxer a config/patches/ruby/ dins d'omnibus-gitlab, un per cada Ruby empaquetat (3.3, 3.4 i 4.0), que aplica tal qual un commit del mateix Ruby.
La raó que GitLab carregui amb aquell pedaç la dona la mateixa petició de canvi: l'arranjament original era només a la branca principal de Ruby, la futura 4.1, i no a les branques 3.3, 3.4 ni 4.0 que fa servir tothom. Ruby va publicar les seves versions corregides dos dies després, el 23 d'octubre: 4.0.9, 3.4.12 i 3.3.13, que tanquen les fallades d'Onigmo darrere dels quatre CVE d'aquesta història. Hi ha un detall revelador a la mateixa petició: a les variants per a 3.3 i 3.4 el forat on entren els nous codis d'error ja estava ocupat per un altre pedaç de Ruby anterior, el de la mida del programa compilat, i a la de 4.0 no. És a dir, que aquest no era el primer.
La conseqüència operativa és curta. Si el teu GitLab és el paquet Omnibus o la imatge oficial de contenidor, l'intèrpret ve a dins i actualitzar GitLab et porta el pedaç; aquesta és la majoria de les instal·lacions i amb això n'hi ha prou. Si el teu GitLab es va instal·lar des de fonts contra el Ruby del sistema, actualitzar GitLab no toca aquell intèrpret, i la frontera passa a ser 4.0.9, 3.4.12 o 3.3.13. Si a més aquell Ruby l'empaqueta la teva distribució, el que decideix és la versió que la teva distribució serveix avui. És el cas minoritari, i és on l'inventari es queda curt: la fila diu «GitLab 19.5.1» i el component que falla és una capa més avall.
I la resta de les teves aplicacions en Ruby
Els quatre CVE porten el nom de GitLab perquè allà hi havia un programa de recompenses on reportar-los. El motor és de Ruby i és a sota de qualsevol aplicació Rails que tinguis en marxa. Convé acotar de seguida què significa això, perquè la versió alarmista és falsa: la condició perquè t'afecti és compilar una expressió regular a partir de text que vingui de fora, i la majoria de les aplicacions no ho fan, perquè els seus patrons estan escrits al codi i no els tria ningú des del navegador. GitLab sí que ho fa, per disseny, perquè la seva configuració de CI/CD deixa escriure expressions.
L'útil, aleshores, és una pregunta que no acostuma a ser a cap llista: alguna de les nostres aplicacions compila expressions regulars amb text que ve de fora? Un cercador amb comodins, un filtre de regles que configura el client, un validador que accepta un patró per formulari. El teu escàner de vulnerabilitats tampoc no la contesta, perquè compara noms i versions de paquet, i aquí el que falla és l'intèrpret que executa el paquet. És la tercera vegada que escrivim sobre aquesta forma concreta de punt cec: ja va passar amb sis avisos i un escàner que continua dient zero i, al mateix món de Ruby, amb la biblioteca que ningú no va triar.
El que això no és
No és la història d'un fabricant negligent. GitLab va corregir el seu propi intèrpret, va publicar la petició de canvi en obert amb el commit d'origen i el raonament sencer, i va tramitar els quatre reports pel seu programa de recompenses. Ens agradaria que més fabricants deixessin llegir això: la majoria publica una línia i un número de versió.
Tampoc no hi ha constància pública que l'últim s'estigui explotant. El que CISA va donar d'alta el 29 d'octubre, juntament amb una fallada de Cisco i sota la directiva BOD 26-04, és el del 12. Segons les xifres que la Shadowserver Foundation va publicar el 2 de novembre, de les unes 39.000 instàncies de GitLab que veu abocades a internet cada dia, més de 1.000 continuen sense el pedaç del 12 d'octubre i més d'11.300 sense el del 21. Aquella segona xifra és exposició, no incidents. I convé no explicar-la amb una sola causa: el pedaç del 21 és nou dies més jove i no té cap catàleg oficial al darrere empenyent, a més d'abastar moltíssimes més versions.
I el «sense autenticar» dels avisos d'octubre té lletra petita. Rapid7 va validar la cadena completa contra la imatge oficial de GitLab CE 19.4.1 i descriu així el que cal: «an attacker must first be able to commit a malicious CI/CD configuration file .gitlab-ci.yml, and a repository path containing UTF-8 characters to a branch in a public project». Això no canvia que calgui corregir-ho. Canvia quantes hores tens per fer-ho, i la resposta depèn de a qui deixi la teva instància crear projectes. Per cert, la 19.4.1 contra la qual Rapid7 ho va reproduir era la versió corregida de la tanda anterior.
El conflicte d'interès, per endavant: el nostre propi CI/CD corre sobre una instància de GitLab autoallotjada, amb desplegaments GitOps a Docker Swarm i Kubernetes. Aquesta cronologia l'hem recorreguda com qualsevol altre.
La fila que li falta al teu inventari
En comptes d'una llista de comprovacions, una taula de dues columnes: el que el teu inventari diu avui d'aquella màquina i el que hauria de poder contestar. Les tres primeres files es resolen amb una ordre. L'última no, i és la que decideix.
| El que diu avui | El que ha de poder contestar |
|---|---|
| «GitLab 19.4.2» | És a la llista del 21 d'octubre o es va quedar a la del 12? La frontera és 19.5.1 / 19.4.3 / 19.3.6 / 19.2.9 / 19.1.9 / 19.0.10 / 18.11.13. |
| (res) | L'intèrpret el porta el paquet de GitLab o és el del sistema? Un ruby -v a la màquina, i la frontera 4.0.9 / 3.4.12 / 3.3.13. |
| «Servidor de desenvolupament» | Què pot signar aquella màquina? Claus de desplegament, tokens de registre d'executors, variables de CI, accés al registre de contenidors. |
| (ningú) | Qui té nom i cognoms per a aquella màquina, i quan és la seva pròxima finestra? |
Quan la resposta a «d'on va sortir aquest paquet» és «el va muntar algú que ja no hi és», el que toca primer és reconstruir la procedència i després decidir si aquella màquina continua sent artesanal o passa a un empaquetatge amb suport. Aquesta decisió no la venem amb una llicència a sota, perquè no en revenem cap, i força vegades acaba en «deixa-ho com està i posa-li una finestra»; és la feina de consultoria. Que la finestra existeixi cada mes i que algú comprovi després que s'ha complert és manteniment.
Cinc setmanes, quatre vulnerabilitats i un camp de 16 bits dins d'un intèrpret que ningú no va triar a consciència. La puntuació del CVE no distingeix entre l'equip a qui això va costar una tarda i l'equip a qui va costar una matinada. El que els distingeix és si algú tenia escrit, abans del 23 de setembre, de qui era aquella màquina i quin dia li tocava.
Fonts. Les tres tandes surten de les notes de pedaç del mateix fabricant, consultades el 3 de novembre de 2026: la del 23 de setembre (19.4.1), amb CVE-2026-89078 i CVE-2026-93577, tots dos 9,9 i amb vector PR:L; la del 12 d'octubre (19.4.2), amb CVE-2026-97229, 10,0, vector CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H; i la del 21 d'octubre (19.5.1), amb CVE-2026-110264, 10,0 i el mateix vector. D'allà procedeixen també les versions, els rangs afectats, les descripcions citades i el crèdit a joaxcar. L'alta al catàleg KEV, el CVE de Cisco del mateix dia i la directiva BOD 26-04 són de l'avís de CISA del 29 d'octubre de 2026. La precondició d'explotació i la validació contra la imatge 19.4.1 són de l'anàlisi de Rapid7. L'explicació del desbordament, el fitxer de pedaç i el motiu d'aplicar-lo a 3.3, 3.4 i 4.0 són de la petició de canvi 9921 d'omnibus-gitlab. Les xifres d'instàncies les atribueix a la Shadowserver Foundation Techzine, el 2 de novembre de 2026, i són un mesurament de tercers, no un recompte nostre. Les cites tècniques es deixen en el seu anglès original perquè es puguin cercar literalment. La lectura de la caiguda de PR:L a PR:N com a superfície en exploració és nostra i va marcada com a tal al text. Ja vam tractar CVE-2026-97229 el 14 d'octubre des d'un altre angle, el de la reconstrucció de la instància, a l'article que li vam dedicar el 14 d'octubre. La fotografia de la imatge social és «Cable closet bh», de domini públic, via Wikimedia Commons.
Qui té nom i cognoms per al vostre GitLab?
Ho mirem amb tu: quina versió corre de debò, d'on va sortir el paquet, què pot signar aquella màquina i quina finestra mensual té escrita. Sense vendre llicències de res.
Parlar amb everyWAN