DanteCTF 2021

Writeups per la prima edizione della DanteCTF

Categories index
Web - Crypto - Misc - Binary - Hardware

Sorgenti

Sorgenti, assets e script di supporto per le seguenti challenges sono disponibili su GitHub: https://github.com/born2scan/dantectf-21

Web

Beatrice

Descrivere Inferno, Purgatorio e Paradiso è sicuramente un compito arduo. Chissà a quali fonti di ispirazione si potrebbe attingere… Posto che siano tutte valide.

Autore: ionno

La descrizione della challenge evidenzia la parola fonti. Aprendo la prima pagina, vediamo subito una galleria con tre immagini. La seconda immagine non viene caricata correttamente. Usando Ctrl+U possiamo vedere le sorgenti della pagina web.

<!DOCTYPE html>
<html>
<head>
    <title>Inferno</title>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" href="https://www.w3schools.com/w3css/4/w3.css">
    <link rel="stylesheet" href="main.css">
</head>
<body>

<h2 class="w3-center"><u>Cerca in profondità</u></h2>

<div class="w3-content w3-display-container">
    <img class="mySlides" src="images/img1.jpg" style="width:100%">
    <img class="mySlides" src="images/DANTE{0r_53_7u_qU3l_v1RG1l10_3_" style="width:100%"> <!-- <== Part 1  -->
    <img class="mySlides" src="images/img3.jpg" style="width:100%">

    <button class="w3-button w3-black w3-display-left" onclick="plusDivs(-1)">&#10094;</button>
    <button class="w3-button w3-black w3-display-right" onclick="plusDivs(1)">&#10095;</button>
</div>

<script src="main.js"></script>
</body>
</html>

Dal codice si nota subito che l’url della seconda immagine non è valido (linea 16) e più precisamente che si tratta della prima parte della flag. Controllando anche i file sorgenti main.css e main.js avremo rispettivamente anche la seconda e la terza parte della flag:

html {
    height: 100vh;
    background: #707070;
}
.mySlides {
    height: 640px;
    display:none;
}
body {
    margin-top: 3rem;
}
.w3-content {
    border: 2px solid #c9c9c9;
}

/* Part 2: qu3LL4_f0N73_Ch3_5p4ND1_d1_ */
var slideIndex = 1;
showDivs(slideIndex);

function plusDivs(n) {
  showDivs(slideIndex += n);
}

function showDivs(n) {
  var i;
  var x = document.getElementsByClassName("mySlides");
  if (n > x.length) {slideIndex = 1}
  if (n < 1) {slideIndex = x.length}
  for (i = 0; i < x.length; i++) {
    x[i].style.display = "none";  
  }
  x[slideIndex-1].style.display = "block";  
}
// Part 3: P4Rl4R_51_L4rG0_F1uM3}

🏁 DANTE{0r_53_7u_qU3l_v1RG1l10_3_qu3LL4_f0N73_Ch3_5p4ND1_d1_P4Rl4R_51_L4rG0_F1uM3}

Guido Guinizzelli

Drizza la testa, drizza, e vedi a cui s’aperse a li occhi d’i Teban la terra;

Autore: ionno

Aprendo la pagina web vediamo un canto preso dalla Divina Commedia. Scorrendo (o leggendo) i versi, verso la metà del canto possiamo notare un link bizzarro “Get the flag”. Premendoci sopra veniamo reindirizzati ad una pagina di errore contenente il messaggio Try hEarder. Alludendo la parola hEarder a header, andiamo a controllare quali header HTTP vengono restituiti dal server quando premiamo il link nella pagina iniziale. Per far ciò possiamo usare gli strumenti per sviluppatori del browser. In particolare, usando il tasto F12 possiamo aprire la scheda “Network” per vedere come vengono fatte le richieste HTTP (spuntando “Preserve log” per non perdere le richieste intermedie).

Guido Guinizzelli (tab network)

Dai log del browser si nota subito che prima di essere reindirizzati sulla pagina di errore nope.html, il browser esegue una richiesta a getflag.php ed il server risponde con uno status code 302 (redirect) ma non prima di fornirci anche un header FLAG contenente la flag.

🏁 DANTE{s0n_GuId0_GuiniZz3lli_e_g1a_m1_pUrg0per_b3N_dol3rM1_pRima_ch_A_l0_sTr3m0}

Virgilio

Chissà quali ricette moderne sono state influenzate dai Lebkuchen di Norimberga, preparati sin dal medioevo.

Autore: ionno

Dalla descrizione della challenge possiamo dedurre che i cookies (Lebkuchen) del browser siano in qualche modo coinvolti. La prima pagina non è altro che una form di login. Non avendo a disposizione delle credenziali di accesso, non rimane altro che controllare le sorgenti della pagina (CTRL+U).

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Paradiso</title>
    <link rel="stylesheet" href="main.css">
</head>
<body>
<div class="container">
    <div class="card">
        <form action="login.php" method="POST">
            <div class="label">Username</div>
            <input type="text" name="username" id="username" class="form-control"/>
            <div class="label">Password</div>
            <input type="password" name="password" id="password" class="form-control"/>
            <input type="submit" value="Login"/>
                    </form>
    </div>
</div>
<!--TODO remove user guest:guest in production -->
</body>
</html>

Nel codice HTML abbiamo un commento bizzarro che ci fornisce delle possibili credenziali di accesso. Usando guest:guest rispettivamente come username e password possiamo accedere al sistema.

Virgilio (accesso normale)

Dal messaggio di benvenuto capiamo che non possiamo ottenere la flag eseguendo l’accesso come guest. Ricaricando la pagina (F5) vediamo che l’applicazione non ci rimanda alla pagina di login. Ciò significa che in qualche modo il server riesce ad identificarci. Controlliamo se l’applicazione usa dei cookies di autenticazione. Aprendo gli strumenti sviluppatori (F12) -> Application -> Cookies osserviamo il seguente cookie:

Virgilio (pagina cookies)

In particolare il valore Z3Vlc3Q%3D viene decodificato dal base64 come guest. Dovendo identificarci come admin proviamo a modificare il cookie inserendo YWRtaW4= (admin in base64) come valore del cookie. Ricaricando la pagina otteniamo la flag.

🏁 DANTE{b347R1c3_L0D4_d1_D10_v3R4}


Crypto

Cangrande della scala

Ragiono, ragiono, ma Ohibò! Tutto questo Ancora non mi porta a nessuna rivelaZIONE.

Vb ivqv cvh' sbytb'e iviv r ivapragv
sne qv abv prageb r qv fr' sne pbeban,
cvh' qbypv va ibpr pur va ivfgn yhpragv:
pbfv' pvatre yn svtyvn qv Yngban
irqrz gnyibygn, dhnaqb y'nrer r' certab,
fì pur evgratn vy svy pur sn yn mban.
Ar yn pbegr qry pvryb, baq' vb evirtab,
QNAGR{fv_gebina_zbygr_tvbvr_pner_r_oryyr}
gnagb pur aba fv cbffba gene qry ertab;
r 'y pnagb qv dhrv yhzv ren qv dhryyr;
puv aba f'vzcraan fì pur yn' fh' ibyv,
qny zhgb nfcrggv dhvaqv yr abiryyr.

Autore: beryxz

Come suggeriscono le lettere in maiuscolo nella descrizione, il testo ha subito una ROTAZIONE.

Effettuando un ROT13 del testo, in mezzo ai versi troviamo la flag.

🏁 DANTE{si_trovan_molte_gioie_care_e_belle}

Catone

Che freddo in cima al Purgatorio… Forse dovrei sbrigarmi a fare questi ultimi 122-91+33 scalini dalla base larga prima che mi comincino a battere i denti.

LS4tLiAuLi4uIC4gLi4tLS4tIC4tLS4gLiAuLS4gLS4uIC4gLi0uIC4uLS0uLSAtIC4gLS0gLi0tLiAtLS0gLi4tLS4tIC4tIC4uLS0uLSAtLi0uIC4uLi4gLi4gLi4tLS4tIC4tLS4gLi4gLi4tIC4uLS0uLSAuLi4gLi0gLi4tLS4tIC4tLS4gLi4gLi4tIC4uLS0uLSAuLi4gLi0tLiAuLiAuLSAtLi0uIC4=

Autore: beryxz

Il testo della challenge è inizialmente codificato in Base64. Questo veniva anche suggerito dalla descrizione.

Decodificato il testo, otteniamo una serie di punti, linee e spazi tipici della codifica Morse.

Effettuando quest’ulteriore decodifica, si ottiene il messaggio da poi inserire all’interno del formato: DANTE{ ... }.

La codifica del simbolo _, o ..--.-, non è propria dello standard Morse. Per questo motivo, al posto del carattere _ era accettato anche il carattere #

🏁 DANTE{che_perder_tempo_a_chi_piu_sa_piu_spiace}

Paolo e Francesca

La matematica si è evoluta molto dal 1300 ad oggi, ma per fortuna è ancora necessario scriverla chiaramente.

Autore: beryxz

#TODO: switch to getSecurePrime
from Crypto.Util.number import getPrime

FLAG = b"[REDACTED]"

# thanks to Rivest, Shamir and Adleman
m = int.from_bytes(FLAG,'big')
p = getPrime(333)
q = 13555951496777604631139270237917994748488574297776756734164338349749523288010729072351577528529826741
n = p*q
e = 65537
d = pow(e,-1,(p-1)*(q-1))
c = pow(m,e,n)

print("n =",n)
print("e =",e)
print("c =",c)
n = 175785929752996462178434258345343575846309727654642778549721777710133155101066155524234234832005665285890177335737047830024845479693160488020016421557387725264753427132622500860510496841993610209824541
e = 65537
c = 56436323842770213708488806649213159341609690590668828518007196230614202020971388121766028690294024159615280729637193915329424396896043182709647550033626899827367761977429109799023489815880544463491163

In quest’ultima challenge per la serie Crypto, abbiamo un messaggio crittografato con l’algoritmo RSA (textbook). Questo viene anche suggerito alla linea 6, dove vengono elencati gli autori dell’algoritmo.

Il problema si trova alla linea 9 dove viene usato un numero statico al posto di un numero random.

Questo ci permette di trovare l’altro primo generatore $p = n / q$

Possiamo quindi calcolare la chiave privata d come mostrato alla linea 12 e decrittare il messaggio.

m = pow(c, d, n).to_bytes(64, 'big')

🏁 DANTE{l4sc1at3_0gn3_sp3r4nz4_v01_ch_1ntr4t3}


Misc

Orlando

Non era lunga ancor la nostra via di qua dal sonno, quand’ io vidi un foco ch’emisperio di tenebre vincia.

Autore: synack

Connettendosi alla porta indicata tramite netcat o strumenti analoghi venivano proposte casualmente delle terzine da completare:

Su' mi levai, e tutti eran gia' pieni
de l'alto di' i ________ del sacro monte,
e andavam col sol novo a le reni.

-> giron

Una volta completatene tre veniva restituita la flag.

🏁 DANTE{3cc0_l4_f13r4_c0n_l4_c0d4_46uzz4}

Giotto

Guelfi e Ghibellini si sono combattuti per quasi due secoli - che le loro discussioni nascondessero una bandiera segreta dopo tutto?

Autore: synack

Veniva fornito un lungo file contenente solamente le parole GUELFI e GHIBELLINI, una per riga.

Sostituendo GUELFI con 0 e GHIBELLINI con 1 si otteneva la codifica binaria dei caratteri ASCII che compongono la flag. Ogni carattere era rappresentato da 8 bit, aggiungendo zeri a sinistra fino a raggiungere la lunghezza necessaria.

Ecco un link diretto ad una ricetta CyberChef di esempio.

🏁 DANTE{c0m3_qu31_ch3_v4_d1_n0tt3}

Oderisi

C’è chi sostiene che la Divina Commedia contenga dei messaggi nascosti, ma non è detto che il testo originale sia il luogo giusto dove cercarli.

Autore: synack

La flag era nascosta nel canale alfa (trasparenza) dell’immagine fornita, analizzabile con stegsolve o altri strumenti come StegOnline.

Oderisi (soluzione)

🏁 DANTE{4pr1_l4_m3n73_4_qu3l_ch3_10_71_p4l350}

Farinata

Cosa c’è di meglio di un’allegra melodia per rallegrar d’un cibernetico avventuriero le gloriose avventure? Ma attenzione ai fantasmi in agguato…

Autore: synack

Il file audio fornito conteneva delle frequenze evidentemente estranee al brano, e l’analisi dello spettro mostrava la flag:

Farinata (spettrogramma)

🏁 DANTE{s0tt0_d4ll4_lun4}


Binary

Cacciaguida

Per nostra fortuna il testo della Divina Commedia è chiaramente leggibile. E se delle importanti parole si potessero nascondere anche altrove?

Autore: beryxz

Viene fornito un binario che sembra mostrare solo una parte della flag.

Utilizzando l’utility strings, o una corrispettiva anche trovabile online, possiamo vedere tutte le stringhe leggibili all’interno del binario.

Filtrando per la parte iniziale della flag che ci viene data 53m..., possiamo vedere che dal binario non è stata rimossa la flag per intero.

strings cacciaguida | grep "53m"

🏁 DANTE{53mpr3_14_c0nfu510n_d3_13_p3r50n3_pr1nc1p10_fu_d31_m41_d3_14_c1774d3}

Casella

Dante Alighieri ha senza dubbio lasciato una profonda impronta nella nostra cultura - anzi, quale era il sinonimo giusto…? Traccia?

Autore: beryxz

Questa volta ci viene chiesta una parola segreta per procedere. Sfortunatamente l’utility strings non ci è sufficiente, in quanto la flag è inizialmente offuscata tramite delle operazioni XOR.

Senza avviare debugger o decompiler vari, è comunque possibile utilizzare le utility di debug come ltrace ed strace per rispettivamente visualizzare le chiamate effettuate alle librerie ed al sistema.

Utilizzando ltrace possiamo vedere come il nostro input passi dalla funzione strncmp, la quale controlla l’uguaglianza del nostro input con giust’appunto la flag. Flag che internamente è stata de-offuscata durante l’esecuzione.

ltrace -s 128 ./casella

🏁 DANTE{Ahi_quanto_son_diverse_quelle_foci_da_l_infernali}

Ciacco

Le anime dannate vengono poste innanzi ad un infinito numero di ostacoli insormontabili, ma per fortuna qui ce ne sono solo 4 e ben definiti.

Autore: beryxz

Per quest’ultima sfida della serie Binary era necessario un debugger come gdb o un decompilatore come Ghidra. Qui di seguito useremo ghidra.

Ciacco (main)

Nella funzione main, viene presa come input una stringa che viene poi passata alla funzione check_input (riga 17) la quale si presuma effettuerà dei controlli per verificarne la validità.

Possiamo anche vedere una chiamata alla funzione print_flag (riga 20), la quale però ci mostra una flag redatta. Infatti la flag è salvata solo sul server e viene mostrata inserendo l’input corretto una volta collegati al server indicato nella descrizione.

Ciacco (check_input)

La funzione check_input si suddivide in 4 controlli separati.

  • Il primo controlla che la lunghezza della stringa sia 21
  • Il secondo, terzo e quarto controllano in ordine sparso che tutti i caratteri siano uguali ad alcuni ben definiti.

Segnandosi tutti i caratteri e la loro posizione, otteniamo la parola D1vin4_daN7e_c0MM3d1a. Inserendo questa parola sul server, vengono effettivamente passati tutti i controlli ed otteniamo la flag.

🏁 DANTE{v01_c1774d1n1_m1_ch14m4573_c14cc0_p32_14_d4nn054_c01p4_d3114_9014_c0m3_7u_v3d1_4114_p109914_m1_f14cc0}


Hardware

Puccio

Circa 500 anni dopo la prima pubblicazione del poema dantesco venne inventato un tanto basilare quanto utile codice.

Autore: synack

Venivano forniti dei binari da caricare su un Arduino, un ESP32, un ESP8266 o un Raspberry Pico (RP2040).

Una volta programmata la scheda o il simulatore veniva ciclicamente attivato un pin di output (D4 per Arduino ed ESP8266, GPIO27 per ESP32, GP15 per RP2040) che scandiva la flag in codice Morse.

Per facilitarne l’estrazione, i caratteri del codice (.,_) venivano contemporaneamente stampati a 9600 baud anche sulla porta seriale predefinita della scheda e quindi, nella maggior parte dei casi, leggibili direttamente da un monitor seriale USB.

La flag era quindi decodificabile dal Morse: VECCHIAFAMA...- . -.-. -.-. .... .. .- ..-. .- -- .-

🏁 DANTE{VECCHIAFAMA}