You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: content/it/ctf/pascalCTF2026.md
+93-12Lines changed: 93 additions & 12 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -38,20 +38,20 @@ editPost:
38
38
# Pascal CTF 2026
39
39

40
40
41
-
La PascalCTF 2026 è stata una competizione di Capture The Flag (CTF) organizzata dal nostro team [Paolo](https://pascalctf.github.io/). La competizione si è svolta dal `31 gennaio 2026` al `1 febbraio 2026` e ha visto la partecipazione di numerosi team provenienti da tutto il mondo.
41
+
La PascalCTF 2026 è stata una competizione di Capture The Flag (CTF) organizzata dal nostro team [Paolo](https://pascalctf.github.io/). Si è svolta dal `31 gennaio 2026` al `1 febbraio 2026` e ha visto la partecipazione di numerosi team provenienti da tutto il mondo.
42
42
43
-
Buona parte delle challenge è stata creata per l'edizione **Beginner** tenutasi nello stesso mese presso l'[ITT Pascal](https://www.ispascalcomandini.it/pagine/pascal-ctf). Le challenge erano suddivise in diverse categorie, tra cui *Web Security*, *Cryptography*, *Binary Exploitation*, *Reverse Engineering*, *Miscellaneous* e *Intelligenza Artificiale*. Ogni categoria presentava sfide di difficoltà variabile, adatte sia a principianti che a esperti.
43
+
Gran parte delle challenge è stata realizzata per l'edizione **Beginner**, ospitata nello stesso mese presso l'[ITT Pascal](https://www.ispascalcomandini.it/pagine/pascal-ctf). Le sfide erano suddivise in diverse categorie, tra cui *Web Security*, *Cryptography*, *Binary Exploitation*, *Reverse Engineering*, *Miscellaneous* e *Intelligenza Artificiale*, con difficoltà variabile e adatte sia a principianti sia a esperti.
44
44
45
45

46
46
47
-
Ci teniamo inoltre a ringraziare tutti i **partecipanti** per il loro entusiasmo e la loro dedizione, nonché i nostri [**sponsors**](/it/sponsors#2026) per il loro supporto.
47
+
Desideriamo inoltre ringraziare tutti i **partecipanti** per l'entusiasmo e la dedizione dimostrati, così come i nostri [**sponsors**](/it/sponsors#2026) per il loro supporto.
Come si può intuire dal nome, questa challenge era basata su un semplice script JSFuck il quale poteva essere "deoffuscato" mediante un semplice`toString()`:
54
+
Come suggerisce il nome, questa challenge era basata su un semplice script JSFuck, che poteva essere "deoffuscato" con un banale`toString()`:
55
55
56
56
```javascript
57
57
let f = [][(![]+[])[+!+[]]+(!![]+[]) ... ];
@@ -71,13 +71,15 @@ Una volta deoffuscato, lo script risultava essere il seguente:
71
71
}
72
72
```
73
73
74
-
Ciò nonostante il metodo più veloce per risolvere una challenge del genere era semplicemente utilizzare un qualsiasi LLM disponibile (ad esempio ChatGPT) e chiedergli di deoffuscare il codice, ottenendo così la flag in pochi secondi.
74
+
Nonostante ciò, il modo più veloce per risolvere una challenge del genere era semplicemente usare un qualsiasi LLM disponibile, ad esempio ChatGPT, chiedendogli di deoffuscare il codice e ottenendo così la flag in pochi secondi.
Osservando il modo in cui veniva inizializzato un utente, si notava che nessun giocatore disponeva ovviamente di abbastanza denaro per acquistare la flag in `RealZa`...
95
+
```javascript
96
+
app.post('/login', (req, res) => {
97
+
const { username, password } =req.body;
98
+
if (username && password) {
99
+
req.session.user=true;
100
+
req.session.balance=100;
101
+
req.session.inventory= {};
102
+
req.session.cart= {};
103
+
returnres.json({ success:true });
104
+
} else {
105
+
res.json({ success:false });
106
+
}
107
+
});
108
+
```
92
109
93
-
110
+
Analizzando gli altri endpoint, però, saltava subito all'occhio `/add-cart`, che permetteva di aggiungere un prodotto al carrello salvato nella sessione tramite un oggetto `product`.
Prestando attenzione si può notare che viene controllato solo il nome del prodotto, e quindi di fatto sanitizzato, mentre la quantità non viene mai verificata per accertarsi che sia un `Number` e rimane quindi completamente non sanitizzata.
119
137
138
+
Per capire il vero exploit bisogna però guardare anche l'endpoint `/checkout`, che usa la sessione inizializzata da `/login` e modificata da `/add-cart` per calcolare il totale dei prodotti e verificare che sia inferiore al saldo disponibile.
120
139
140
+
Solo nel caso in cui il saldo disponibile sia sufficiente, i prodotti vengono aggiunti all'inventario dell'utente e quindi mostrati:
La domanda spontanea è però: *come faccio ad avere abbastanza soldi per comprare la flag?*
177
+
178
+
Questo, però, è il punto di vista sbagliato per risolvere la challenge: grazie ai dettagli notati in `/add-cart`, possiamo infatti impostare la quantità di un prodotto su un qualunque oggetto (`"stringa"`, `0`, `{}`, `[]`).
179
+
180
+
A questo punto vogliamo bypassare due controlli:
181
+
1. L'`if` presente in `/add-cart` il quale controlla che `quantity < 1`
182
+
2. L'`if` presente in `/checkout` il quale controlla che `total > req.session.balance`
183
+
184
+
Per superare il primo `if` si possono usare tutti e tre i casi tra **stringhe**, **array** (con almeno un oggetto) e un **oggetto** vuoto (`{}`).
185
+
186
+
Nel secondo `if`, invece, il metodo intended per rendere sufficiente il bilancio era usare una **stringa** oppure un **oggetto**, così che la moltiplicazione `prices[product] * cart[product]` producesse **`NaN`**.
187
+
188
+
Per definizione, `NaN < x` e `NaN > x` restituiscono entrambe `false` e, in questo caso specifico, poiché il sito controlla se l'utente ha superato il proprio bilancio, il fatto che questa verifica ritorni `false` consente di acquistare qualsiasi cosa!
Il codice di questo sito era davvero minimale ed era sostanzialmente il seguente:
194
+
```python
195
+
from flask import Flask, jsonify, request, render_template
196
+
197
+
app = Flask(__name__)
198
+
199
+
@app.route("/")
200
+
defindex():
201
+
return render_template("index.html")
202
+
203
+
@app.route("/pages/<int:index>")
204
+
defpage(index):
205
+
return render_template("pages.html", index=index)
206
+
207
+
@app.route("/api/get_json", methods=["POST"])
208
+
defget_json():
209
+
index = request.json.get("index")
210
+
ifnot index:
211
+
return jsonify({"error": "Index is required"}), 400
212
+
213
+
path =f"static/{index}"
214
+
try:
215
+
withopen(path, "r") asfile:
216
+
data =file.read()
217
+
return data, 200
218
+
exceptFileNotFoundError:
219
+
return jsonify({"error": "File not found"}), 404
220
+
exceptExceptionas e:
221
+
return jsonify({"error": str(e)}), 500
222
+
223
+
if__name__=="__main__":
224
+
app.run(host="0.0.0.0", port=5000, debug=False)
225
+
```
226
+
227
+
Un po' come nella challenge precedente, l'endpoint interessante era `/api/get_json`, che accettava un JSON e restituiva il contenuto del file `static/{index}`.
228
+
229
+
Guardando la cartella *static* si trovano 7 file con indici da **1** a **7**, ma il loro contenuto non è particolarmente interessante.
230
+
231
+
La flag, infatti, si trova in `flag.txt` e non in `static/flag.txt`.
232
+
233
+
L'aspetto rilevante per risolvere la challenge è che questa normalissima path traversal non implementa nemmeno un controllo sull'indice, che può assumere qualsiasi valore e non solo un intero.
234
+
235
+
Di conseguenza, il sito risponde senza problemi a un normale `{"index": "../flag.txt"}`, restituendo direttamente la flag.
Questa challenge come si può intuire dal nome si basava interamente sulla risoluzione di un normalissimo sistema di equazioni lineari le cui incognite altro non erano che i singoli byte della flag. Il sistema veniva generato in maniera casuale, ma con la garanzia che avesse una soluzione unica, e veniva fornito al giocatore sotto forma di stringa:
270
+
Come suggerisce il nome, questa challenge si basava interamente sulla risoluzione di un normale sistema di equazioni lineari, in cui le incognite erano i singoli byte della flag. Il sistema veniva generato in modo casuale, ma con la garanzia di avere una soluzione unica, e veniva fornito al giocatore sotto forma di stringa:
190
271
191
272
```python
192
273
defgenerate_system(values):
@@ -202,7 +283,7 @@ def generate_system(values):
202
283
print(streq)
203
284
```
204
285
205
-
Per risolvere il sistema, era sufficiente utilizzare un qualsiasi software di algebra computazionale (ad esempio Wolfram Alpha) o scrivere una semplice solve mediante l'utilizzo di librerie come [`z3`](https://ericpony.github.io/z3py-tutorial/guide-examples.htm).
286
+
Per risolvere il sistema era sufficiente usare un qualsiasi software di algebra computazionale, ad esempio Wolfram Alpha, oppure scrivere un semplice solver con librerie come [`z3`](https://ericpony.github.io/z3py-tutorial/guide-examples.htm).
Nonostante le diverse problematiche incontrate durante l'organizzazione e lo svolgimento della competizione, siamo estremamente soddisfatti del risultato finale e della partecipazione che abbiamo ricevuto. Vorremmo però anche scusarci con tutti i partecipanti per eventuali disagi o problemi tecnici che si sono verificati durante la competizione, e assicurarvi che faremo del nostro meglio per evitare che si ripetano in futuro.
327
+
Nonostante le diverse problematiche incontrate durante l'organizzazione e lo svolgimento della competizione, siamo estremamente soddisfatti del risultato finale e della partecipazione ricevuta. Vorremmo però anche scusarci con tutti i partecipanti per eventuali disagi o problemi tecnici verificatisi durante la competizione, e assicurarvi che faremo del nostro meglio per evitare che si ripetano in futuro.
247
328
248
-
Ci teniamo inoltre a riflettere sull'utilizzo degli **LLM** e dell'**AI** in generale all'interno delle competizioni CTF. Se da un lato queste tecnologie possono essere strumenti estremamente potenti per risolvere le challenge, dall'altro rischiano di rendere alcune sfide troppo *semplici* o addirittura *banali*. Per questo motivo, stiamo valutando l'idea di introdurre delle **limitazioni** all'utilizzo di questi strumenti nelle future edizioni della PascalCTF, al fine di mantenere un certo livello di difficoltà e di stimolare la creatività e l'ingegno dei partecipanti.
329
+
Vogliamo inoltre riflettere sull'utilizzo degli **LLM** e dell'**AI** in generale all'interno delle competizioni CTF. Se da un lato queste tecnologie possono essere strumenti estremamente potenti per risolvere le challenge, dall'altro rischiano di rendere alcune sfide troppo *semplici* o addirittura *banali*. Per questo motivo, stiamo valutando l'idea di introdurre delle **limitazioni** all'utilizzo di questi strumenti nelle future edizioni della PascalCTF, così da mantenere un certo livello di difficoltà e stimolare la creatività e l'ingegno dei partecipanti.
249
330
250
331
Il nostro scopo rimarrà infatti, oggi come in futuro, quello di offrire una competizione *divertente*, *educativa* e *stimolante* per tutti i partecipanti, indipendentemente dal loro livello di esperienza o dalle tecnologie che decidono di utilizzare.
0 commit comments