Skip to content
KitploitKITPLOIT
ToolsBlog
Log in
Submit
ToolsBlog
Submit

Hacking, PenTest, and Cybersecurity Tools for Your Security Arsenal!

Kitploit is a directory of hacking, cybersecurity, and pentesting tools. Discover the latest project updates to find vulnerabilities, analyze systems, automate testing, and strengthen your security.

FeedsContactPrivacy© 2026 Kitploit

Tool Directory

Categories

View all categories
Loading categories
wix-kickstartx-challenge-writeup — Detailed write-up of a CTF challenge: reverse engineering a React SPA, parsing SVG images, building an automated solver with greedy matching, and achieving a perfect score. | Kitploit
Tools/GitHubGitHub/d0rb/wix-kickstartx-challenge-writeup
Reverse EngineeringCTFLearning & Education
GitHubd0rb/wix-kickstartx-challenge-writeup

wix-kickstartx-challenge-writeup

Detailed write-up of a CTF challenge: reverse engineering a React SPA, parsing SVG images, building an automated solver with greedy matching, and achieving a perfect score.

View Repository

Most Popular

View all →

Discover the most used tools by our community.

Explore all tools

Browse our collection of tools

View all tools →
Share
134 months agoNot yet reviewed

Wix KickstartX Challenge 2026 — Full Writeup

Result: 200/200 (100%) — Token: WKX-9245FA70-200 Date: March 24, 2026


1. Discovery & Reconnaissance

The Challenge Announcement

A LinkedIn post announced a special challenge for Wix KickstartX — a junior developer program. The rules:

  • 30 minutes, one attempt only
  • Top 10 highest scores advance directly to the exam stage
  • Two links provided: wix-kickstartx-challenge-2026.base44.app/ and wixkickstart.com

Finding the Challenge Entry Point

The main site wixkickstart.com is a Wix-hosted site (server-side rendered by Wix's Thunderbolt engine). Standard curl only returns the JavaScript shell — no actual content. We used headless Chromium to render it:

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

From the rendered DOM we extracted an embedded iframe:

This turned out to be a p5.js particle animation (hero section eye candy), not the challenge itself. The actual challenge lives at the Base44 app.


2. Reverse Engineering the Challenge App

Platform: Base44

The challenge runs on Base44 — a low-code app platform. The app is a single-page React application with all logic in one JS bundle:

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

Size: 410,809 bytes (minified React + app logic + Base44 SDK)

Extracting the API Surface

By grepping the JS bundle, we mapped the entire API:

FunctionPurpose
startGameCreates a game session, returns 200 images + 200 descriptions
scoreGameAccepts {matches: {}, sessionId: ""}, returns {correctCount: N}
generateTokenTakes sessionId, returns completion token
getLeaderboardReturns top scores
saveNicknameSaves display name for leaderboard

Entities: GameSession, Participant

API URL pattern:

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

App ID: 69aea07cbcb9a3dd1039a58d

Key Constants from the Bundle

const or = 1800;   // Time limit: 1800 seconds (30 minutes)
const rm = 200;    // Total items: 200 image-description pairs

JSON Submission Discovery

A critical finding — the app has a JSON bulk submission mode. From the minified source:

function Ik({onSubmit:r, onClose:n}) {
    // ...
    h = JSON.parse(s)  // Parse JSON input
    // Validation: must be object like { "IMG-001": "DESC-042", ... }
    r(h)  // Submit all matches at once
}

Placeholder text in the modal:

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

This means we don't need to click 200 times in the UI — we can submit a JSON mapping of all 200 matches programmatically.

Authentication Flow

The Base44 SDK uses JWT authentication:

Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

The JWT payload contains:

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

Required headers for all API calls:

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. Understanding the Game Data

startGame Response

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

Response structure:

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

Image Format: Inline SVG (base64-encoded)

Each image is a 200x200 SVG containing:

  1. A background gradient (light or dark themed)
  2. An optional overlay pattern (lines, dots, rings, etc.)
  3. Geometric shapes with specific colors, sizes, opacity, rotation, and position

Example decoded SVG (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>

Description Format: Structured Natural Language

Each description follows a strict pattern:

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

Example:

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. Building the Automated Solver

Step 1: Map All Constants

We needed to map SVG properties to description vocabulary. This required analyzing all 200 images and all 200 descriptions to find exact correspondences.

Background Mapping (12 types)

By counting occurrences on both sides, we established a 1:1 mapping:

SVG Gradient Start ColorCountDescription WordCount
#0a162828pitch28
#eef2f720frosted20
#0d0d0d19tenebrous19
#fef9f018pearlescent18
#1e0a2e18nocturnal18
#0a1a0a17midnight17
#1a0a0a15inky15
#f5f0eb14milky14
#f7f3ee14ethereal14
#f0f0f014radiant14
#f0f7f413glowing13
#1a1a2e10somber10

Method: Count unique gradient start colors in all 200 SVGs, count unique first-word in all 200 descriptions, match by count.

Overlay Mapping (7 types)

SVG overlays are implemented differently depending on type:

SVG PatternDetection MethodCountDescription NameCount
<line> elements, horizontal (dy=0)Check x1,y1,x2,y235striped overlay35
<line> elements, vertical (dx=0)Check x1,y1,x2,y234lattice pattern34
<circle> with opacity="0.06" (dots)Count low-opacity circles30stippled layer30
No overlay elements at allNo lines, no bg shapes28(none)28
<line> elements, diagonalCheck slope direction27tilted cross-lines27
Small <polygon>/<path> with opacity="0.06"Low-opacity attr shapes25arrow-band texture25
<circle> with fill="none" stroke="#888"Stroke-only circles21ringed pattern21

Key insight: Background pattern elements use opacity as an XML attribute (e.g., opacity="0.06"), while foreground shapes use opacity inside the style attribute (e.g., style="opacity:0.7"). This distinction was critical for separating background patterns from actual shapes.

Color Mapping (28 colors)

All 200 SVGs use exactly 28 unique hex fill colors. All 200 descriptions use exactly 28 unique color names.

Download Tool