O campanie automatizată numită Gheist folosește tokenuri GitHub furate pentru a publica workflow-uri care copiază secretele unui depozit în jurnalele GitHub Actions, le descarcă și șterg apoi rulările. Analiza companiei de securitate cloud Wiz arată că metoda a funcționat în depozite private și în medii GitHub Enterprise, unde măsurile de protecție disponibile pentru depozitele publice nu se aplică.
Wiz urmărește activitatea din 15 septembrie și a recuperat peste 13.500 de ramuri create de pe mai mult de 850 de conturi compromise, de la proiecte personale până la depozite private ale unor organizații mari. Raportul nu precizează câte organizații au avut efectiv secretele expuse, iar la 24 octombrie campania era încă activă și își schimba instrumentele.
Un token cu drepturi de scriere deschide accesul la secrete
Instrumentul atacatorilor caută depozitele accesibile tokenului furat și creează în fiecare o ramură cu numele ci-sync- urmat de șase caractere aleatorii. Apoi adaugă un singur fișier, .github/workflows/sync-check.yml, într-un commit nesemnat, cu mesajul ci: sync config; numele proprietarului tokenului apare în commit, iar publicarea ramurii pornește workflow-ul.
Miza este legată de nivelul de acces: potrivit documentației GitHub, oricine are drept de scriere într-un depozit poate citi toate secretele configurate pentru acesta. Prin urmare, un token furat cu asemenea permisiuni îi poate oferi atacatorului o cale directă către valorile secrete pe care le poate accesa un job GitHub Actions, fără să fie nevoie de o vulnerabilitate în platformă.

Sursă foto: AIWorkflow-ul afișează mediul jobului și secretele disponibile, după ce le codifică în base32. GitHub încearcă să mascheze secretele în jurnale, dar documentația avertizează că protecția nu este garantată dacă valoarea este codificată sau modificată; Wiz spune că textul base32 nu mai corespunde valorii originale, astfel încât masca nu o ascunde.
Fișierul declară un bloc permissions gol, ceea ce limitează tokenul GITHUB_TOKEN generat pentru job, dar nu împiedică citirea secretelor. O variantă a instrumentului lansează câte un job pentru fiecare mediu de deployment al depozitului, astfel încât pot fi expuse și secretele specifice mediilor respective, care includ adesea credențiale de producție.
După rulare, instrumentul descarcă jurnalul prin API-ul GitHub și șterge execuția câteva secunde mai târziu. Datele ies prin infrastructura GitHub, nu printr-un server controlat de atacatori; de aceea, blocarea unei conexiuni externe suspecte nu este suficientă pentru a opri exfiltrarea.
De ce verificările GitHub nu au oprit atacurile din depozitele private
Începând din 28 iulie, GitHub poate pune în așteptare rulările pe care le identifică drept potențial malițioase, până când un colaborator cu drept de scriere le aprobă. Însă compania precizează că mecanismul „se aplică în prezent doar depozitelor publice de pe github.com”; GitHub Enterprise Server nu dispune de această măsură, iar depozitele private nu sunt acoperite de protecția descrisă.
Wiz afirmă că toate rulările Gheist verificate în depozite publice au rămas în așteptarea aprobării, indiferent de versiunea instrumentului, deci încărcăturile nu au rulat acolo. Protecția nu este însă o garanție generală: ea oprește doar execuțiile semnalate de GitHub, iar o campanie distinctă, GhostAction, a rulat în depozite publice până când GitHub a început să o blocheze, pe 9 octombrie, potrivit GitGuardian.
În depozitele private și pe GitHub Enterprise Server, jurnalele de audit pot arăta rulări pe ramuri ci-sync-* care se încheie și sunt șterse ulterior de agentul de utilizator gheist. În jurnalele AWS CloudTrail, Wiz a observat și workflow-uri legitime ale organizațiilor rulând pe aceleași ramuri și asumând roluri AWS, indiciu că Actions a executat cod fără aprobare.
În organizațiile enterprise, instrumentul a trecut în câteva minute prin tokenuri ale mai multor utilizatori și conturi de automatizare și a clonat depozite private înainte de a publica workflow-ul. La câteva ore distanță, credențialele cloud din aceeași organizație au fost folosite pentru a enumera AWS Secrets Manager; raportul Wiz nu stabilește de unde proveneau acele credențiale și nici cine le-a folosit.
În jurul datei de 19 octombrie, o versiune a început să adauge un al doilea job, destinat runnerelor găzduite de organizație, adică mașinilor proprii care execută joburi Actions. Codul pare să vizeze numai depozitele cu astfel de mașini înregistrate și să caute 31 de tipuri de tokenuri și credențiale, printre care chei private, variabile de proces, credențiale pentru roluri AWS de instanță, fișiere de autentificare ale runnerului și tokenul contului de serviciu Kubernetes.
Wiz descrie acest job drept defectuos și nu raportează nicio execuție reușită. Totuși, dacă o ramură Gheist a rulat pe un runner propriu, organizația trebuie să trateze mașina și rolul cloud al instanței ca fiind compromise, deoarece simpla ștergere a ramurii sau a rulării nu anulează accesul deja obținut și nici nu retrage secretele deja citite.
Campania se intersectează cu furturi de tokenuri și alte operațiuni
Sursele tokenurilor furate pe care Wiz a putut să le urmărească includ jurnale provenite din infostealere și tokenuri expuse în atacuri anterioare asupra lanțului de aprovizionare. Printre acestea se numără compromiterea din martie a scanerului de vulnerabilități Trivy, însă analiza nu atribuie toate tokenurile unei singure surse și nu stabilește identitatea operatorilor Gheist.
Conturile folosite nu au fost implicate doar în această campanie. Pe 6 octombrie, 391 de conturi, multe utilizate și de Gheist, au redenumit aproximativ 1.461 de depozite pentru a le prezenta drept software piratat și a răspândi malware; operațiunea a șters 610 dintre ramurile Gheist, fără ca asta să însemne că secretele citite anterior au fost protejate.
Unele victime par să fi fost infectate și cu PolinRider, malware pe care cercetătorii l-au asociat cu Coreea de Nord. Pe 19 și 20 octombrie, trei dintre ele au republicat commiturile Gheist, de data aceasta cu un loader adăugat în fișierul vite.config.js; separat, campania Megalodon, care a introdus workflow-uri de furt al secretelor în mii de depozite în mai, afectase deja 57 dintre conturile respective.
La 24 octombrie, aproximativ două treimi dintre conturile implicate nu mai erau disponibile pe GitHub. Wiz a raportat activitatea platformei și spune că dispariția lor reflectă probabil un răspuns la sesizare, dar nu oferă o confirmare că toate conturile sau depozitele afectate au fost identificate și remediate.
Ce trebuie să verifice organizațiile din România și UE
Pentru companiile și instituțiile din România și restul UE care folosesc GitHub Actions, incidentul arată că auditarea trebuie să includă depozitele private, mediile Enterprise Server și infrastructura cloud conectată prin OIDC. O organizație poate să nu colecteze jurnalele GitHub, dar să găsească în CloudTrail asumări de rol sau schimburi de tokenuri al căror subiect include o ramură ci-sync-.
Echipele de securitate ar trebui să caute ramuri cu forma ^ci-sync-[0-9a-z]{6}$, în care singura modificare este fișierul .github/workflows/sync-check.yml, și commitul ci: sync config. În jurnalele de audit, semnalele relevante sunt rulările pe aceste ramuri șterse la mai puțin de un minut după încheiere și activitatea asociată agentului de utilizator gheist.
Pe runner-ele proprii trebuie căutate fișierele .gdmp_pat, .gdmp_patg și .gdmp_sweep, precum și șirul gdmp-pivot în jurnalele joburilor. Indicatorii publicați de Wiz includ și textul toJSON(secrets) }}' | base32 -w 120, separatorul ===== gdmp-pivot:, precum și amprentele blob Git pentru variantele workflow-ului: f3da2b8b357dcda02fd455d314be2a95007516a6 pentru varianta simplă, d09535857a741bbb976bdc084a9a021713133291 pentru cea cu timeout, 1dc5af9cce950aad1eb1546ecce8565b117540c4 pentru cea cu separator și c665b778b2d822453293003e7ca0bbb2cb08e39d pentru varianta cu job destinat runnerelor proprii.
Variantele care enumeră mediile de deployment diferă de la un depozit la altul și nu au un hash fix. În plus, documentația OIDC GitHub arată că, în mod implicit, subiectul tokenului include numele ramurii doar dacă jobul nu folosește un mediu de deployment; căutarea în logurile cloud trebuie interpretată în acest context și nu trebuie limitată la un singur tip de înregistrare.
Dacă este găsită o ramură suspectă, Wiz recomandă rotirea tuturor secretelor de depozit, organizație și mediu pe care proiectul le-ar fi putut accesa și revocarea tokenurilor care au publicat commiturile. Proprietarii tokenurilor trebuie să-și verifice dispozitivele pentru infostealere; rulările suspendate nu trebuie aprobate, iar codul din ramurile ci-sync- nu trebuie executat.
Pentru a reduce riscul pe viitor, organizațiile ar trebui să ceară aprobarea unui reviewer pentru mediile care conțin secrete de producție și să limiteze politicile de încredere OIDC la ramuri protejate sau medii aprobate. GitHub precizează că un job nu poate citi secretele unui mediu până când reviewerul obligatoriu nu îl aprobă, ceea ce oferă o barieră suplimentară.
Wiz recomandă și înlocuirea tokenurilor personale cu aplicații GitHub sau tokenuri cu durată scurtă și permisiuni fine-grained. Runner-ele proprii ar trebui să fie mașini efemere, fără acces la socketul Docker și fără acces deschis la metadatele cloud; astfel, compromiterea unui job are mai puține șanse să devină o breșă persistentă în infrastructura organizației.



