
Lab CVE-2020-13277: vulnerabilità logica di Gitlab - accesso non autorizzato ai repository privati da parte di qualsiasi utente
Lab CVE-2020-13277: vulnerabilità logica di GitLab - accesso non autorizzato di qualsiasi utente a repository privati
CVE-2020-13277
├── README.md ............... [此 README 说明]
├── imgs .................... [辅助 README 说明的图片]
├── gitlab .................. [Gitlab 容器的挂载目录]
│ ├── Dockerfile .......... [Gitlab 的 Docker 构建文件]
│ ├── config .............. [Gitlab 配置挂载目录]
│ ├── data ................ [Gitlab 数据挂载目录]
│ ├── logs ................ [Gitlab 日志挂载目录]
│ ├── keys ................ [Gitlab 破解 License 存储目录]
│ └── runner .............. [Runner 容器的挂载目录]
├── license ................. [破解 License 的容器构建目录]
│ ├── Dockerfile .......... [License 的 Docker 构建文件]
│ └── license.rb .......... [生成破解 License 的 Ruby 脚本]
├── docker-compose.yml ...... [Docker 的构建配置]
├── keygen.ps1 .............. [Windows: 一键生成破解 License]
├── keygen.sh ............... [Linux: 一键生成破解 License]
├── run.ps1 ................. [Windows: 一键运行 Gitlab 靶场]
├── run.sh .................. [Linux: 一键运行 Gitlab 靶场]
├── register.ps1 ............ [Windows: 一键注册 Runner]
├── register.sh ............. [Linux: 一键注册 Runner]
├── stop.ps1 ................ [Windows: 一键停止 Gitlab 靶场]
└── stop.sh ................. [Linux: 一键停止 Gitlab 靶场]
Il cuore di questa vulnerabilità è principalmente lo sfruttamento della funzione di backup e sincronizzazione a specchio dei repository, Mirror Repository.
La direzione di sincronizzazione del Mirror si divide in due tipi:
Questa vulnerabilità sfrutta il Mirror Repository in direzione Pull
Da notare che GitLab è disponibile in due versioni: CE (Community Edition gratuita) e EE (Enterprise Edition a pagamento), e GitLab ha dichiarato ufficialmente che questa vulnerabilità colpisce sia CE che EE nelle seguenti versioni:
>=10.6, <12.9.10>=12.10, <12.10.11>=13.0, <13.0.6Ma ciò non significa che tutte queste versioni delle Gitlab Docker Image possano essere usate per allestire il lab, perché:
In altre parole, per allestire il lab con Docker si può scegliere solo la versione GitLab-EE e crackarla (chi ha molti soldi può anche scegliere di acquistare una License) per attivare la funzionalità Mirror Repository - Pull.
Ma anche dopo aver crackato GitLab-EE, sia con , che , quando l'URL di Mirror Repository - Pull contiene un percorso locale, viene restituito l'errore .
./keygen.sh o ./keygen.ps1./run.sh o ./run.ps1Quando in precedenza è stata generata la coppia di chiavi per il crack, la chiave pubblica è già stata scritta nel backend del container GitLab; ora è necessario caricare la chiave privata tramite il frontend su GitLab per completare il crack:
./gitlab/keys/; copiare il contenuto di .gitlab-license (la chiave privata)Enter license key, incollare la chiave privata e fare clic sul pulsante Upload license per completare il crackA questo punto la funzionalità Mirror Repository - Pull è attivata

Outbound requests, spuntare Allow requests to the local network from hooks and services e salvareA questo punto Mirror Repository - Pull supporta il pull di Repository locali

./register.sh $TOKEN o ./register.ps1 $TOKENA questo punto tutti i Repository possono usare questo Runner per eseguire script CI (Pipeline Jobs)

Per il processo di verifica si può fare riferimento alla Issue ufficiale, ma il processo di verifica seguente prevede alcune piccole modifiche ai passaggi in base a questo lab
Con l'utente root aprire la pagina http://127.0.0.1/admin/users e creare 3 account:
victim: l'account della vittimaattacker1: account dell'attaccante 1attacker2: account dell'attaccante 2Durante la creazione dell'account non è possibile impostare una password iniziale; GitLab per impostazione predefinita invia la password iniziale all'Email configurata. Per comodità, si può inserire un Email qualsiasi, creare l'account e poi modificarlo subito: a quel punto è possibile impostare con root la password iniziale dell'account, senza passare dall'Email

victimNew Project:
targetPrivateREADME.md e impostarne il contenuto a piacere, ad esempio mykey is abcxyzOvviamente
targetè il repository privato divictim, e il nostro obiettivo è sfruttare la vulnerabilità per ottenere il contenuto di questo repository

attacker1New Project:
pocPublic.gitlab-ci.yml con il seguente contenuto:image: "ruby:2.6"
rspec:
script:
- git clone http://gitlab-ci-token:[email protected]/victim/target.git
- cd target
- ls -lah .
- cat README.md
【Scopo】Nei passaggi successivi, con alcune tecniche si farà sì che victim, senza saperlo, esegua questo script CI con i propri permessi.
Poiché il lab attuale non ha certificati configurati, si può usare solo il protocollo
http; inoltre172.168.30.2è l'IP assegnato dadocker-compose.ymlal container GitLab: poiché questo script CI verrà eseguito tramite il Runner, e nel lab attuale il Runner e GitLab non sono nello stesso container, è necessario usare l'indirizzo IP assegnato da Docker

attacker2New Group:
testPublictest creare un repository completamente vuoto New Project:
pocPublic
Configurare il repository mirror Settings => Repository => Pull from a remote repository:
Mirror repository: spuntareGit repository URL: inserire http://GITLAB/attacker1/pocPassword: (lasciare vuoto, il livello di visibilità del repository da cui si fa Pull è Public, non serve la password)Trigger pipelines for mirror updates: spuntare (attiva l'esecuzione dello script CI durante la sincronizzazione dello specchio)Una volta configurato correttamente, ogni 30 minuti viene controllato se il repository sorgente è cambiato; in caso di modifiche viene forzata la sincronizzazione.
Il
Git repository URLpunta al repository poc creato in precedenza; il motivo per cui non si usa 127.0.0.1 è che il Pull vieta di scaricare repository locali, ma è possibile aggirare il blocco usando il DNS:GITLABè l'hostname assegnato dadocker-compose.ymlal container GitLab ed è impostato per impostazione predefinita in/etc/hosts; sebbene l'host non possa risolvereGITLAB, all'interno del container equivale ad accedere ahttp://127.0.0.1/attacker1/poc

attacker2testvictim al gruppo:
Add new member to test: victimPermissions: OwnerExpiration date: (lasciare vuoto, cioè senza scadenza)=> Setting => Account => Delete account per eliminare l'account corrente (attacker2)【Scopo】Nel gruppo test ci sono solo due Owner: victim e attacker2; quando l'utente attacker2 viene eliminato, la proprietà del gruppo test e di tutti i repository in esso contenuti viene trasferita forzatamente a victim. Poiché nel gruppo test c'è un repository test/poc sincronizzato via mirror da attacker1/poc, ciò equivale a cedere forzatamente il repository test/poc all'utente victim.


Prima, riepiloghiamo la situazione attuale:
test/poc per la vittima victimtest/poc viene sincronizzato via mirror dal repository attacker1/poc (ogni 30 minuti viene controllato se ci sono modifiche da sincronizzare)attacker1/poc è controllato dall'attaccante, inclusi gli script CI .gitlab-ci.ymltest/poc ha impostato Trigger pipelines for mirror updates, lo script CI viene eseguito a ogni sincronizzazioneOwner del repository test/poc è victim, lo script CI viene eseguito con i permessi di victim
Ricapitolando il contenuto dello script CI .gitlab-ci.yml configurato in precedenza, questo poc, nel Runner, usa i permessi di victim per accedere alla struttura delle directory e al file README.md del suo repository Private target:
image: "ruby:2.6"
rspec:
script:
- git clone http://gitlab-ci-token:[email protected]/victim/target.git
- cd target
- ls -lah .
- cat README.md
Successivamente, l'attaccante deve solo modificare arbitrariamente qualche contenuto insignificante nel repository attacker1/poc; nel caso peggiore basta attendere 30 minuti e lo script CI configurato verrà eseguito con i permessi di victim.
Sebbene l'attaccante non abbia accesso diretto ai risultati dell'esecuzione dei Pipeline Jobs, gli basta modificare la destinazione di output dello script, ad esempio inviando il contenuto del repository a una Email o a un server FTP specifici, per rubare il codice del repository.

10.x12.x13.xImport url is blocked: Requests to localhost are not allowedSebbene dopo aver impostato Allow requests to the local network from hooks and services in Admin area => Settings => Network => Outbound requests sia possibile configurare URL locali, durante la sincronizzazione dello specchio si ottiene l'errore 2:Fetching remote upstream failed: fatal: unable to access http://127.0.0.1/xxxx/: The requested URL returned error: 301. In altre parole, è utilizzabile solo il Pull del Remote Repository.
Fortunatamente, sebbene 12.x e 13.x abbiano metodi di rilevamento degli URL locali molto severi, la versione 10.x offre una soluzione: quando si configura l'URL di Pull, basta usare il nome del servizio DNS configurato localmente per aggirare il blocco.
In conclusione, l'unica opzione possibile è costruire il lab con l'immagine Docker gitlab-ee:10.6.0-ee.0.
In realtà, dalle descrizioni precedenti si capisce anche che le condizioni per sfruttare questa vulnerabilità sono piuttosto severe; in pratica, i "poveri" difficilmente possono essere colpiti da questa vulnerabilità