Skip to content
KitploitKITPLOIT
StrumentiBlog
Invia
StrumentiBlog
Invia

Strumenti di Hacking, PenTest e Cybersecurity per il tuo Arsenale di Sicurezza!

Kitploit è una directory di strumenti di hacking, cybersecurity e pentesting. Scopri gli ultimi aggiornamenti dei progetti per trovare vulnerabilità, analizzare sistemi, automatizzare i test e rafforzare la tua sicurezza.

··Feed·Contatto·Privacy·© 2026 Kitploit

Directory degli strumenti

Categorie

Vedi tutte le categorie
Loading categories
wix-kickstartx-challenge-writeup — Write-up dettagliato di una challenge CTF: reverse engineering di una SPA React, parsing di immagini SVG, creazione di un solver automatizzato con greedy matching e raggiungimento di un punteggio perfetto. | Kitploit
Strumenti/GitHubGitHub/d0rb/wix-kickstartx-challenge-writeup
Reverse EngineeringCTFApprendimento e Formazione
GitHubd0rb/wix-kickstartx-challenge-writeup

wix-kickstartx-challenge-writeup

Write-up dettagliato di una challenge CTF: reverse engineering di una SPA React, parsing di immagini SVG, creazione di un solver automatizzato con greedy matching e raggiungimento di un punteggio perfetto.

Vedi Repository

Più Popolari

Vedi tutti →

Scopri gli strumenti più utilizzati dalla nostra community.

Esplora tutti gli strumenti

Sfoglia la nostra collezione di strumenti

Vedi tutti gli strumenti →
Condividi
3 mesi faNon ancora revisionato

Wix KickstartX Challenge 2026 — Writeup Completo

Risultato: 200/200 (100%) — Token: WKX-9245FA70-200 Data: 24 marzo 2026


1. Scoperta e Ricognizione

L'Annuncio della Sfida

Un post su LinkedIn annunciava una sfida speciale per Wix KickstartX — un programma per sviluppatori junior. Le regole:

  • 30 minuti, un solo tentativo
  • I 10 punteggi più alti avanzano direttamente alla fase d'esame
  • Due link forniti: wix-kickstartx-challenge-2026.base44.app/ e wixkickstart.com

Trovare il Punto d'Ingresso della Sfida

Il sito principale wixkickstart.com è un sito ospitato su Wix (renderizzato lato server dal motore Thunderbolt di Wix). Una curl standard restituisce solo lo shell JavaScript — nessun contenuto reale. Abbiamo usato Chromium headless per renderizzarlo:

root@kitploit:~
chromium --headless --disable-gpu --no-sandbox --virtual-time-budget=10000 \
  --dump-dom "https://wixkickstart.com" > /tmp/wix_rendered.html

Dal DOM renderizzato abbiamo estratto un iframe incorporato:

root@kitploit:~

Questa si è rivelata essere un'animazione di particelle p5.js (semplice effetto visivo della hero section), non la sfida stessa. La vera sfida vive nell'app Base44.


2. Reverse Engineering dell'App della Sfida

Piattaforma: Base44

La sfida gira su Base44 — una piattaforma app low-code. L'app è una applicazione React a pagina singola con tutta la logica in un unico bundle JS:

root@kitploit:~
https://wix-kickstartx-challenge-2026.base44.app/assets/index-oTG160r9.js

Dimensione: 410.809 byte (React minificato + logica app + SDK Base44)

Estrazione della Superficie API

Cercando nel bundle JS, abbiamo mappato l'intera API:

Entità: GameSession, Participant

Pattern URL API:

root@kitploit:~
POST /api/apps/{appId}/functions/{functionName}

App ID: 69aea07cbcb9a3dd1039a58d

Costanti Chiave dal Bundle

root@kitploit:~
const or = 1800;   // Limite di tempo: 1800 secondi (30 minuti)
const rm = 200;    // Elementi totali: 200 coppie immagine-descrizione

Scoperta dell'Invio JSON

Un risultato fondamentale — l'app ha una modalità di invio JSON in blocco. Dal sorgente minificato:

root@kitploit:~
function Ik({onSubmit:r, onClose:n}) {
    // ...
    h = JSON.parse(s)  // Analizza l'input JSON
    // Validazione: deve essere un oggetto come { "IMG-001": "DESC-042", ... }
    r(h)  // Invia tutte le corrispondenze in una volta
}

Testo segnaposto nella modale:

root@kitploit:~
{
  "IMG-001": "DESC-042",
  "IMG-002": "DESC-017",
  ...
}

Questo significa che non dobbiamo cliccare 200 volte nell'interfaccia — possiamo inviare una mappatura JSON di tutte le 200 corrispondenze in modo programmatico.

Flusso di Autenticazione

L'SDK Base44 usa autenticazione JWT:

root@kitploit:~
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

Il payload JWT contiene:

root@kitploit:~
{
  "sub": "[email protected]",
  "exp": 1782162460,
  "iat": 1774386460
}

Header richiesti per tutte le chiamate API:

root@kitploit:~
Content-Type: application/json
Authorization: Bearer {jwt_token}
X-App-Id: 69aea07cbcb9a3dd1039a58d
Base44-Functions-Version: prod
X-Origin-URL: https://wix-kickstartx-challenge-2026.base44.app/

3. Capire i Dati del Gioco

Risposta di startGame

root@kitploit:~
POST /api/apps/69aea07cbcb9a3dd1039a58d/functions/startGame
Status: 200
Transferred: 62.87 kB compresso (2.20 MB decompresso)
Content-Encoding: br (Brotli)

Struttura della risposta:

root@kitploit:~
{
  "status": "active",
  "sessionId": "69c2fd218e5b26f307c941c9",
  "startedAt": "2026-03-24T21:07:45.101Z",
  "imageOrder": ["IMG-154", "IMG-102", ...],   // 200 elementi (ordine di visualizzazione)
  "descOrder": ["DESC-125", "DESC-109", ...],  // 200 elementi (ordine di visualizzazione)
  "imagesData": { "IMG-001": "data:image/svg+xml;base64,...", ... },  // 200 SVG
  "descriptionsData": { "DESC-105": "milky field, overlaid with...", ... }  // 200 testi
}

Formato Immagine: SVG Inline (codificato in base64)

Ogni immagine è un SVG 200x200 contenente:

  1. Un gradiente di sfondo (a tema chiaro o scuro)
  2. Un pattern di overlay opzionale (linee, punti, anelli, ecc.)
  3. Forme geometriche con colori, dimensioni, opacità, rotazione e posizione specifici

Esempio di SVG decodificato (IMG-001):

root@kitploit:~
<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200" viewBox="0 0 200 200">
  <defs>
    <linearGradient id="bg" ...>
      <stop offset="0%" stop-color="#f5f0eb"/>
      <stop offset="100%" stop-color="#e8e0d5"/>
    </linearGradient>
  </defs>
  <rect width="200" height="200" fill="url(#bg)"/>
  <!-- Linee/punti/anelli di overlay opzionali qui -->
  <polygon points="..." fill="#E2725B" transform="translate(128.6,185.7) rotate(90)"
           style="opacity:0.5;filter:drop-shadow(...)"/>
  <!-- Altre forme... -->
</svg>

Formato Descrizione: Linguaggio Naturale Strutturato

Ogni descrizione segue uno schema rigoroso:

root@kitploit:~
{campo} field[, overlaid with {overlay}]. {N} elementi totali:
{dimensione} {opacità} {colore} {forma} ({rotazione}, at {posizione}) ·
{dimensione} {opacità} {colore} {forma} ({rotazione}, at {posizione}) · ...

Esempio:

root@kitploit:~
milky field, overlaid with tilted cross-lines. 10 elements total:
minuscule nearly solid dim gray pike (steeply angled, at center inner-left) ·
substantial solid azure pike (diagonal, at top center) · ...

4. Costruzione del Solver Automatico

Passo 1: Mappare Tutte le Costanti

Dovevamo mappare le proprietà SVG al vocabolario delle descrizioni. Questo ha richiesto l'analisi di tutte le 200 immagini e tutte le 200 descrizioni per trovare le corrispondenze esatte.

Mappatura degli Sfondi (12 tipi)

Contando le occorrenze su entrambi i lati, abbiamo stabilito una corrispondenza 1:1:

Metodo: Contare i colori di inizio gradiente unici in tutti i 200 SVG, contare la prima parola unica in tutte le 200 descrizioni, abbinare in base al conteggio.

Mappatura degli Overlay (7 tipi)

Gli overlay SVG sono implementati in modo diverso a seconda del tipo:

Intuizione chiave: Gli elementi del pattern di sfondo usano opacity come attributo XML (es. opacity="0.06"), mentre le forme in primo piano usano opacity dentro l'attributo style (es. style="opacity:0.7"). Questa distinzione è stata fondamentale per separare i pattern di sfondo dalle forme reali.

Mappatura dei Colori (28 colori)

Tutti i 200 SVG usano esattamente 28 colori hex di riempimento unici. Tutte le 200 descrizioni usano esattamente 28 nomi di colore unici.

Mappatura dei Tipi di Forma (10 tipi)

Mappatura delle Dimensioni

Basata sul raggio massimo dei vertici della forma dall'origine:

Mappatura dell'Opacità

Valore opacità SVGNome Descrizione
1.0solid
0.85nearly solid
0.7semi-transparent

Mappatura della Rotazione

Griglia delle Posizioni

Le forme sono posizionate su una griglia 7x7 alle coordinate: [14.3, 42.9, 71.4, 100.0, 128.6, 157.1, 185.7] sia per X che per Y.

Queste corrispondono ai nomi delle colonne: far-left, left, inner-left, center, inner-right, right, far-right E ai nomi delle righe: top, upper, upper-mid, center, lower-mid, lower, bottom

Passo 2: Analizzare Tutti gli SVG

Per ciascuna delle 200 immagini SVG:

  1. Decodificare base64 in XML SVG grezzo
  2. Estrarre lo sfondo — leggere il primo colore <stop> da <linearGradient id="bg">
  3. Classificare l'overlay — cercare <line>, <circle fill="none" stroke>, punti/forme a bassa opacità
  4. Estrarre le forme — trovare tutti gli elementi <polygon>, <circle>, <path>, <rect> che:
    • NON sono il rect di sfondo (salta width="200")
    • NON sono il rect della vignetta (salta fill="url(#vig)")
    • NON sono elementi del pattern di overlay (salta opacity="0.0x" come attributo)
    • HANNO un style="opacity:..." (tutte le forme in primo piano ce l'hanno)

Passo 3: Analizzare Tutte le Descrizioni

Per ciascuno dei 200 testi descrittivi:

  1. Sfondo: prima parola (es. "milky", "pitch", "tenebrous")
  2. Overlay: testo dopo "overlaid with" nella prima frase
  3. Conteggio forme: regex (\d+) elements total
  4. Forme individuali: pattern regex:
    root@kitploit:~
    (minuscule|modest|mid-sized|substantial|massive)
    (solid|nearly solid|semi-transparent|faint)
    (nome colore)
    (tipo di forma)
    ((rotazione), at (posizione))
    

Passo 4: Abbinamento Basato sul Punteggio

L'algoritmo di abbinamento usa un approccio greedy basato su punteggi:

root@kitploit:~
def score_match(img, desc):
    # Vincoli rigidi — devono corrispondere esattamente
    if img['num'] != desc['num']:      return -10000  # Conteggio forme
    if img['bg'] != desc['bg']:        return -10000  # Tipo di sfondo
    if img['overlay'] != desc['overlay']: return -10000  # Tipo di overlay

    score = 100  # Punteggio base per aver soddisfatto i vincoli rigidi

    # Punteggio soft — sovrapposizione colori
    for color in img_colors:
        if color in desc_colors:
            score += 10

    # Punteggio soft — sovrapposizione tipi
    for type in img_types:
        if type in desc_types:
            score += 8

    # Abbinamento dettagli per singola forma (greedy stile ungherese)
    for each image_shape:
        find best matching desc_shape by:
            +20 if color matches
            +15 if type matches
            +10 if size matches
            +8  if opacity matches
            +5  if rotation matches
        score += best_match_score

    return score

Poi assegnare greedy prima le coppie con punteggio più alto:

root@kitploit:~
all_scores.sort(reverse=True)
for score, img_id, desc_id in all_scores:
    if img_id not in matched and desc_id not in used:
        matches[img_id] = desc_id

5. Esecuzione e Invio

Chiamate API Effettuate

1. Avvia Gioco

root@kitploit:~
POST /api/apps/69aea07cbcb9a3dd1039a58d/functions/startGame
Body: {"email": "[email protected]"}
Response: 200 OK (2.2 MB — tutti i dati di gioco)

2. Valuta Gioco

root@kitploit:~
POST /api/apps/69aea07cbcb9a3dd1039a58d/functions/scoreGame
Body: {
  "matches": {"IMG-032": "DESC-182", "IMG-198": "DESC-134", ...},
  "sessionId": "69c2fd218e5b26f307c941c9"
}
Response: 200 OK
{"correctCount": 200, "scoreSeal": "3d1ac8a7e7be4856"}

3. Genera Token

root@kitploit:~
POST /api/apps/69aea07cbcb9a3dd1039a58d/functions/generateToken
Body: {"sessionId": "69c2fd218e5b26f307c941c9"}
Response: 200 OK
{"token": "WKX-9245FA70-200", "score": 200}

Cronologia

PassoAzione
21:07:45 UTCGioco avviato (chiamata startGame)
21:07–21:15Sviluppo dello script solver (parsing + abbinamento)
~21:15scoreGame inviato —

Tempo totale di risoluzione: ~8 minuti di una finestra di 30 minuti.


6. Riepilogo Stack Tecnico


7. Intuizioni Chiave

Perché Ha Funzionato

  1. L'endpoint di invio JSON è stato il fattore critico — senza di esso, avremmo dovuto usare automazione del browser per cliccare 400 volte (seleziona immagine + seleziona descrizione x 200).

  2. La mappatura basata sul conteggio è stata la svolta per colori, sfondi e overlay. Invece di indovinare a cosa corrisponda l'hex #cd7f32 in inglese, abbiamo contato che appare 65 volte negli SVG e "brass" appare 65 volte nelle descrizioni — conteggio unico = abbinamento garantito.

  3. I vincoli rigidi eliminano rapidamente i candidati. Ogni immagine ha una combinazione unica di (tipo_sfondo, tipo_overlay, numero_forme). Con 12 sfondi x 7 overlay x conteggi di forme variabili, la maggior parte delle immagini ha solo una manciata di possibili corrispondenze di descrizione, non 200.

  4. SVG è dati strutturati. A differenza delle immagini raster (PNG/JPG), gli SVG sono XML — ogni forma, colore, posizione e rotazione è esplicitamente codificato come testo. Nessuna visione artificiale necessaria.

Cosa Ha Reso la Sfida Difficile

  1. Il rilevamento degli overlay era complicato. I pattern di sfondo usavano 5 diverse tecniche SVG: elementi <line>, <circle> solo con stroke, <circle> con attributo a bassa opacità, <polygon> con attributo a bassa opacità e assenza di tutti questi. La distinzione chiave era opacity come attributo XML (sfondo) vs. dentro style (forme in primo piano).

  2. Il vocabolario dei colori non era ovvio. Nomi come "oxblood" (#800020), "brass" (#cd7f32) e "deep sapphire" (#0f52ba) richiedevano l'approccio del conteggio — non si può indovinare in modo affidabile solo dai valori hex.

  3. La classificazione delle forme dai percorsi SVG richiedeva la comprensione dei comandi dei path SVG:

    • M (moveto), L (lineto), A (arc), (closepath)
Scarica lo strumento
FunzioneScopo
startGameCrea una sessione di gioco, restituisce 200 immagini + 200 descrizioni
scoreGameAccetta {matches: {}, sessionId: ""}, restituisce {correctCount: N}
generateTokenPrende sessionId, restituisce il token di completamento
getLeaderboardRestituisce i punteggi migliori
saveNicknameSalva il nome visualizzato per la classifica
Colore Inizio Gradiente SVGConteggioParola DescrizioneConteggio
#0a162828pitch28
#eef2f720frosted20
#0d0d0d19tenebrous19
#fef9f018pearlescent18
#1e0a2e18nocturnal18
#0a1a0a17midnight17
#1a0a0a15inky15
#f5f0eb14milky14
#f7f3ee14ethereal14
#f0f0f014radiant14
#f0f7f413glowing13
#1a1a2e10somber10
Pattern SVGMetodo di RilevamentoConteggioNome DescrizioneConteggio
Elementi <line> orizzontali (dy=0)Controlla x1,y1,x2,y235striped overlay35
Elementi <line> verticali (dx=0)Controlla x1,y1,x2,y234lattice pattern34
<circle> con opacity="0.06" (punti)Conta i cerchi a bassa opacità30stippled layer30
Nessun elemento di overlayNessuna linea, nessuna forma di sfondo28(nessuno)28
Elementi <line> diagonaliControlla la direzione della pendenza27tilted cross-lines27
Piccoli <polygon>/<path> con opacity="0.06"Forme con attributo a bassa opacità25arrow-band texture25
<circle> con fill="none" stroke="#888"Cerchi solo con stroke21ringed pattern21
HexNome DescrizioneHexNome Descrizione
#708090blue-gray#cd7f32brass
#f5f5f5near white#dc143cfiery red
#00bcd4electric cyan#2196f3azure
#ff6b6bsalmon pink#228b22rich green
#98ff98pale green#b0b0b0platinum
#0f52badeep sapphire#800020oxblood
#ff8c00deep orange#4b0082dark purple
#e2725bterra rosa#ffbf00marigold
#b7410erusset#ff69b4candy pink
#ffd700bright gold#c0c0c0tin
#6b8e23moss#008080deep teal
#40e0d0pale teal#0047abroyal blue
#e34234burnt sienna#a0522ddeep red
#4a4a4adim gray#36454fdark gray
Elemento SVGLogica di RilevamentoNome Descrizione
<polygon> con 3 verticiConta le coppie di punti separati da spaziopike
<polygon> con 4 verticitilted square
<polygon> con 5 verticiquint form
<polygon> con 6 verticibee cell
<polygon> con 10 verticiasterisk
<rect> (non di sfondo)Ha width/height, non 200x200tilted square
<circle>Nome del tagdisc
<path> con fill-rule="evenodd"Due percorsi ad arco concentricidonut
<path> con singolo arco + ZPercorso a semicerchiohalf-disc
<path> con 6+ comandi LForma a croce/piùcrosshair
<path> con 3-5 comandi LForma simile a una frecciapointer
Intervallo di RaggioNome Descrizione
0–12minuscule
13–16modest
17–20mid-sized
21–25substantial
26+massive
0.5
faint
Valore rotate() SVGNome Descrizione
0°upright
1–20°slightly tilted
21–55°diagonal
56–75°steeply angled
76–105°sideways
  • Per ogni forma, estrarre:
    • Colore: dall'attributo fill, mappato tramite COLOR_MAP
    • Tipo: dal tag dell'elemento + numero di vertici / comandi del percorso
    • Dimensione: dalle coordinate dei vertici o dal raggio
    • Opacità: da style="opacity:X"
    • Rotazione: da transform="... rotate(X)"
    • Posizione: da transform="translate(X,Y) ..."
  • 200/200 corretto
    ~21:15generateToken chiamato — WKX-9245FA70-200
    ComponenteTecnologia
    Piattaforma della sfidaBase44 (low-code app builder)
    FrontendReact SPA (unico bundle JS, ~410KB)
    BackendPython/uvicorn dietro Cloudflare
    CDN/ProxyCloudflare (HTTP/3, compressione Brotli)
    AutenticazioneJWT (HS256), salvato in localStorage
    Formato datiSVG (base64 inline), API JSON
    Real-timeWebSocket (socket.io) per aggiornamenti sessione in tempo reale
    SolverPython 3 (parsing con regex, nessuna libreria esterna)
    Z
  • Un donut = due percorsi ad arco concentrici con fill-rule="evenodd"
  • Un half-disc = singolo percorso ad arco
  • Un crosshair = percorso con 6+ segmenti di linea (forma a croce/più)
  • Un pointer = percorso con 3-5 segmenti di linea (forma a freccia)