Skip to content
KitploitKITPLOIT
HerramientasExploitsBlog
Log in
Enviar
HerramientasExploitsBlog
Enviar

¡Herramientas de Hacking, PenTest y Ciberseguridad para tu Arsenal de Seguridad!

Kitploit es un directorio de herramientas de hacking, ciberseguridad y pentesting. Descubre las últimas actualizaciones de proyectos para encontrar vulnerabilidades, analizar sistemas, automatizar pruebas y fortalecer tu seguridad.

··Feeds·Contacto·Privacidad·© 2026 Kitploit

Directorio de Herramientas

Categorías

Ver todas las categorías
Loading categories
wix-kickstartx-challenge-writeup — Write-up detallado de un desafío CTF: ingeniería inversa de una SPA de React, parseo de imágenes SVG, creación de un solucionador automático con emparejamiento voraz (greedy) y logro de una puntuación perfecta. | Kitploit
Herramientas/GitHubGitHub/d0rb/wix-kickstartx-challenge-writeup
Ingeniería InversaCTFAprendizaje y Educación
GitHubd0rb/wix-kickstartx-challenge-writeup

wix-kickstartx-challenge-writeup

Write-up detallado de un desafío CTF: ingeniería inversa de una SPA de React, parseo de imágenes SVG, creación de un solucionador automático con emparejamiento voraz (greedy) y logro de una puntuación perfecta.

Ver Repositorio

Más Populares

Ver todos →

Descubre las herramientas más usadas por nuestra comunidad.

Explora todas las herramientas

Explora nuestra colección de herramientas

Ver todas las herramientas →
Compartir
9hace 4 mesesAún no revisado

Wix KickstartX Challenge 2026 — Writeup completo

Resultado: 200/200 (100%) — Token: WKX-9245FA70-200 Fecha: 24 de marzo de 2026


1. Descubrimiento y reconocimiento

El anuncio del desafío

Una publicación de LinkedIn anunció un desafío especial para Wix KickstartX, un programa para desarrolladores júnior. Las reglas:

  • 30 minutos, un solo intento
  • Los 10 puntajes más altos avanzan directamente a la etapa de examen
  • Dos enlaces proporcionados: wix-kickstartx-challenge-2026.base44.app/ y wixkickstart.com

Encontrar el punto de entrada al desafío

El sitio principal wixkickstart.com es un sitio alojado en Wix (renderizado del lado del servidor por el motor Thunderbolt de Wix). Un curl estándar solo devuelve el shell de JavaScript, sin contenido real. Usamos Chromium sin interfaz gráfica para renderizarlo:

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

Del DOM renderizado extrajimos un iframe incrustado:

Resultó ser una animación de partículas p5.js (decoración de la sección hero), no el desafío en sí. El desafío real está en la app de Base44.


2. Ingeniería inversa de la app del desafío

Plataforma: Base44

El desafío se ejecuta en Base44, una plataforma de aplicaciones low-code. La app es una aplicación React de una sola página con toda la lógica en un único bundle de JS:

https://wix-kickstartx-challenge-2026.base44.app/assets/index-oTG160r9.js

Tamaño: 410 809 bytes (React minificado + lógica de la app + SDK de Base44)

Extraer la superficie de la API

Haciendo grep al bundle de JS, mapeamos toda la API:

FunciónPropósito
startGameCrea una sesión de juego, devuelve 200 imágenes + 200 descripciones
scoreGameAcepta {matches: {}, sessionId: ""}, devuelve {correctCount: N}
generateTokenToma sessionId, devuelve el token de finalización
getLeaderboardDevuelve los puntajes más altos
saveNicknameGuarda el nombre para mostrar en la tabla de clasificación

Entidades: GameSession, Participant

Patrón de URL de la API:

POST /api/apps/{appId}/functions/{functionName}

ID de la app: 69aea07cbcb9a3dd1039a58d

Constantes clave del bundle

const or = 1800;   // Límite de tiempo: 1800 segundos (30 minutos)
const rm = 200;    // Total de elementos: 200 pares imagen-descripción

Descubrimiento del envío JSON

Un hallazgo crítico: la app tiene un modo de envío masivo JSON. Del código fuente minificado:

function Ik({onSubmit:r, onClose:n}) {
    // ...
    h = JSON.parse(s)  // Parsear entrada JSON
    // Validación: debe ser un objeto como { "IMG-001": "DESC-042", ... }
    r(h)  // Enviar todas las coincidencias de una vez
}

Texto de marcador de posición en el modal:

{
  "IMG-001": "DESC-042",
  "IMG-002": "DESC-017",
  ...
}

Esto significa que no necesitamos hacer clic 200 veces en la interfaz: podemos enviar un mapeo JSON de las 200 coincidencias mediante programación.

Flujo de autenticación

El SDK de Base44 usa autenticación JWT:

Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

El payload del JWT contiene:

{
  "sub": "[email protected]",
  "exp": 1782162460,
  "iat": 1774386460
}

Cabeceras requeridas para todas las llamadas a la API:

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. Comprender los datos del juego

Respuesta de startGame

POST /api/apps/69aea07cbcb9a3dd1039a58d/functions/startGame
Status: 200
Transferred: 62.87 kB compressed (2.20 MB decompressed)
Content-Encoding: br (Brotli)

Estructura de la respuesta:

{
  "status": "active",
  "sessionId": "69c2fd218e5b26f307c941c9",
  "startedAt": "2026-03-24T21:07:45.101Z",
  "imageOrder": ["IMG-154", "IMG-102", ...],   // 200 elementos (orden de visualización)
  "descOrder": ["DESC-125", "DESC-109", ...],  // 200 elementos (orden de visualización)
  "imagesData": { "IMG-001": "data:image/svg+xml;base64,...", ... },  // 200 SVGs
  "descriptionsData": { "DESC-105": "milky field, overlaid with...", ... }  // 200 textos
}

Formato de imagen: SVG en línea (codificado en base64)

Cada imagen es un SVG de 200x200 que contiene:

  1. Un gradiente de fondo (tema claro u oscuro)
  2. Un patrón de superposición opcional (líneas, puntos, anillos, etc.)
  3. Formas geométricas con colores, tamaños, opacidades, rotaciones y posiciones específicos

Ejemplo de SVG decodificado (IMG-001):

<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)"/>
  <!-- Optional overlay lines/dots/rings here -->
  <polygon points="..." fill="#E2725B" transform="translate(128.6,185.7) rotate(90)"
           style="opacity:0.5;filter:drop-shadow(...)"/>
  <!-- More shapes... -->
</svg>

Formato de descripción: lenguaje natural estructurado

Cada descripción sigue un patrón estricto:

{background} field[, overlaid with {overlay}]. {N} elements total:
{size} {opacity} {color} {shape} ({rotation}, at {position}) ·
{size} {opacity} {color} {shape} ({rotation}, at {position}) · ...

Ejemplo:

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. Construcción del solucionador automatizado

Paso 1: Mapear todas las constantes

Necesitábamos mapear las propiedades SVG al vocabulario de las descripciones. Esto requirió analizar las 200 imágenes y las 200 descripciones para encontrar las correspondencias exactas.

Mapeo de fondos (12 tipos)

Contando las ocurrencias en ambos lados, establecimos un mapeo 1:1:

Color inicial del gradiente SVGConteoPalabra de descripciónConteo
#0a162828pitch28
#eef2f720frosted20
#0d0d0d19tenebrous19
#fef9f018pearlescent18
#1e0a2e18nocturnal18
#0a1a0a17midnight17
#1a0a0a15inky15
#f5f0eb14milky14
#f7f3ee14ethereal14
#f0f0f014radiant14
#f0f7f413glowing13
#1a1a2e10somber10

Método: contar los colores únicos de inicio de gradiente en los 200 SVG, contar la primera palabra única en las 200 descripciones y emparejar por conteo.

Mapeo de superposiciones (7 tipos)

Las superposiciones SVG se implementan de forma diferente según el tipo:

Patrón SVGMétodo de detecciónConteoNombre en la descripciónConteo
Elementos <line> horizontales (dy=0)Comprobar x1,y1,x2,y235striped overlay35
Elementos <line> verticales (dx=0)Comprobar x1,y1,x2,y234lattice pattern34
<circle> con opacity="0.06" (puntos)Contar círculos de baja opacidad30stippled layer30
Sin elementos de superposiciónSin líneas, sin formas de fondo28(ninguno)28
Elementos <line> diagonalesComprobar la dirección de la pendiente27tilted cross-lines27
<polygon>/<path> pequeños con opacity="0.06"Formas con atributo de baja opacidad25arrow-band texture25
<circle> con fill="none" stroke="#888"Solo círculos con trazo21ringed pattern21
Descargar herramienta