Skip to content
KitploitKITPLOIT
ИнструментыБлог
Отправить
ИнструментыБлог
Отправить

Инструменты для хакинга, пентеста и кибербезопасности — ваш арсенал защиты!

Kitploit — это каталог инструментов для хакинга, кибербезопасности и пентестинга. Находите последние обновления проектов для поиска уязвимостей, анализа систем, автоматизации тестирования и усиления вашей безопасности.

··Ленты·Контакты·Конфиденциальность·© 2026 Kitploit

Каталог инструментов

Категории

Все категории
Loading categories
wix-kickstartx-challenge-writeup — Подробный разбор CTF-задания: реверс-инжиниринг React SPA, парсинг SVG-изображений, создание автоматического солвера с жадным сопоставлением и достижение идеального результата. | Kitploit
Инструменты/GitHubGitHub/d0rb/wix-kickstartx-challenge-writeup
Обратная инженерияCTFОбучение и Образование
GitHubd0rb/wix-kickstartx-challenge-writeup

wix-kickstartx-challenge-writeup

Подробный разбор CTF-задания: реверс-инжиниринг React SPA, парсинг SVG-изображений, создание автоматического солвера с жадным сопоставлением и достижение идеального результата.

Репозиторий

Популярное

Смотреть все →

Откройте для себя самые используемые инструменты нашего сообщества.

Изучить все инструменты

Просмотрите нашу коллекцию инструментов

Смотреть все инструменты →
Поделиться
3 месяцев назадЕщё не проверено

Wix KickstartX Challenge 2026 — Полный разбор

Результат: 200/200 (100%) — Токен: WKX-9245FA70-200 Дата: 24 марта 2026 года


1. Обнаружение и разведка

Анонс челленджа

В посте LinkedIn был анонсирован специальный челлендж для Wix KickstartX — программы для начинающих разработчиков. Правила:

  • 30 минут, только одна попытка
  • 10 лучших результатов проходят сразу на этап экзамена
  • Предоставлены две ссылки: wix-kickstartx-challenge-2026.base44.app/ и wixkickstart.com

Поиск точки входа в челлендж

Основной сайт wixkickstart.com размещён на Wix (серверный рендеринг движком Thunderbolt от Wix). Обычный curl возвращает только JavaScript-оболочку — без фактического содержимого. Чтобы отрендерить его, мы использовали headless Chromium:

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

Из отрендеренного DOM мы извлекли встроенный iframe:

root@kitploit:~

Это оказалось p5.js-анимацией частиц (декоративный элемент hero-секции), а не сам челлендж. Настоящий челлендж находится в приложении Base44.


2. Ревёрс-инжиниринг приложения челленджа

Платформа: Base44

Челлендж запущен на Base44 — low-code платформе для приложений. Приложение представляет собой одностраничное React-приложение, вся логика которого находится в одном JS-бандле:

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

Размер: 410 809 байт (минифицированный React + логика приложения + SDK Base44)

Извлечение API-поверхности

С помощью grep по JS-бандлу мы составили карту всего API:

Сущности: GameSession, Participant

Шаблон URL API:

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

ID приложения: 69aea07cbcb9a3dd1039a58d

Ключевые константы из бандла

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

Обнаружение JSON-отправки

Критическая находка — в приложении есть режим пакетной JSON-отправки. Из минифицированного исходника:

root@kitploit:~
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
}

Текст-заглушка в модальном окне:

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

Это значит, что нам не нужно кликать 200 раз в интерфейсе — мы можем программно отправить JSON-сопоставление всех 200 пар.

Процесс аутентификации

SDK Base44 использует JWT-аутентификацию:

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

Полезная нагрузка JWT содержит:

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

Обязательные заголовки для всех 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. Понимание игровых данных

Ответ startGame

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

Структура ответа:

root@kitploit:~
{
  "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
}

Формат изображений: встроенный SVG (base64)

Каждое изображение — это SVG размером 200x200, содержащий:

  1. Фоновый градиент (светлая или тёмная тема)
  2. Необязательный оверлейный узор (линии, точки, кольца и т.д.)
  3. Геометрические фигуры с определёнными цветами, размерами, прозрачностью, поворотом и позицией

Пример декодированного SVG (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)"/>
  <!-- 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>

Формат описаний: структурированный естественный язык

Каждое описание следует строгому шаблону:

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

Пример:

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. Создание автоматического решателя

Шаг 1: Сопоставить все константы

Нам нужно было сопоставить свойства SVG со словарём описаний. Для этого потребовалось проанализировать все 200 изображений и все 200 описаний, чтобы найти точные соответствия.

Сопоставление фонов (12 типов)

Подсчитав вхождения с обеих сторон, мы установили взаимно-однозначное соответствие:

Метод: Посчитать уникальные начальные цвета градиентов во всех 200 SVG, посчитать уникальные первые слова во всех 200 описаниях и сопоставить по количеству.

Сопоставление оверлеев (7 типов)

Оверлеи SVG реализованы по-разному в зависимости от типа:

Ключевая идея: элементы фоновых узоров используют opacity как XML-атрибут (например, opacity="0.06"), тогда как фигуры переднего плана используют opacity внутри атрибута style (например, style="opacity:0.7"). Это различие было критически важным для отделения фоновых узоров от настоящих фигур.

Сопоставление цветов (28 цветов)

Во всех 200 SVG используется ровно 28 уникальных шестнадцатеричных цветов заливки. Во всех 200 описаниях используется ровно 28 уникальных названий цветов.

Сопоставление типов фигур (10 типов)

Сопоставление размеров

Основано на максимальном радиусе вершин фигуры от центра:

Сопоставление прозрачности

Значение opacity в SVGНазвание в описании
1.0solid
0.85nearly solid
0.7semi-transparent

Сопоставление поворотов

Сетка позиций

Фигуры размещаются на сетке 7x7 с координатами: [14.3, 42.9, 71.4, 100.0, 128.6, 157.1, 185.7] для X и Y.

Эти координаты соответствуют названиям столбцов: far-left, left, inner-left, center, inner-right, right, far-right И названиям строк: top, upper, upper-mid, center, lower-mid, lower, bottom

Шаг 2: Разобрать все SVG

Для каждого из 200 SVG-изображений:

  1. Декодировать base64 в «сырой» SVG XML
  2. Извлечь фон — прочитать цвет первого <stop> из <linearGradient id="bg">
  3. Классифицировать оверлей — проверить наличие <line>, <circle fill="none" stroke>, точек/фигур с низкой прозрачностью
  4. Извлечь фигуры — найти все элементы <polygon>, <circle>, <path>, <rect>, которые:
    • НЕ являются фоновым прямоугольником (пропустить width="200")
    • НЕ являются виньеточным прямоугольником (пропустить fill="url(#vig)")
    • НЕ являются элементами оверлейного узора (пропустить атрибут opacity="0.0x")
    • ИМЕЮТ style="opacity:..." (есть у всех фигур переднего плана)

Шаг 3: Разобрать все описания

Для каждого из 200 текстовых описаний:

  1. Фон: первое слово (например, "milky", "pitch", "tenebrous")
  2. Оверлей: текст после "overlaid with" в первом предложении
  3. Количество фигур: регулярное выражение (\d+) elements total
  4. Отдельные фигуры: шаблон регулярного выражения:
    root@kitploit:~
    (minuscule|modest|mid-sized|substantial|massive)
    (solid|nearly solid|semi-transparent|faint)
    (color name)
    (shape type)
    ((rotation), at (position))
    

Шаг 4: Сопоставление на основе оценок

Алгоритм сопоставления использует жадный подход с оценкой:

root@kitploit:~
def score_match(img, desc):
    # Hard constraints — must match exactly
    if img['num'] != desc['num']:      return -10000  # Shape count
    if img['bg'] != desc['bg']:        return -10000  # Background type
    if img['overlay'] != desc['overlay']: return -10000  # Overlay type

    score = 100  # Base score for matching hard constraints

    # Soft scoring — color overlap
    for color in img_colors:
        if color in desc_colors:
            score += 10

    # Soft scoring — type overlap
    for type in img_types:
        if type in desc_types:
            score += 8

    # Per-shape detail matching (Hungarian-style greedy)
    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

Затем жадным образом назначаются пары с наибольшими оценками:

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. Выполнение и отправка

Выполненные API-вызовы

1. Запуск игры

root@kitploit:~
POST /api/apps/69aea07cbcb9a3dd1039a58d/functions/startGame
Body: {"email": "[email protected]"}
Response: 200 OK (2.2 MB — all game data)

2. Оценка игры

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. Генерация токена

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

Хронология

ШагДействие
21:07:45 UTCИгра начата (вызван startGame)
21:07–21:15Разработка скрипта решателя (парсинг + сопоставление)
~21:15Отправлен scoreGame —

Общее время решения: ~8 минут из 30-минутного окна.


6. Сводка по техническому стеку


7. Ключевые выводы

Почему это сработало

  1. Эндпоинт JSON-отправки стал критически важным фактором — без него нам понадобилась бы автоматизация браузера для 400 кликов (выбор изображения + выбор описания x 200).

  2. Сопоставление на основе подсчёта стало прорывом для цветов, фонов и оверлеев. Вместо того чтобы угадывать, какому английскому слову соответствует hex-код #cd7f32, мы подсчитали, что он встречается 65 раз в SVG, а "brass" — 65 раз в описаниях — уникальное количество = гарантированное совпадение.

  3. Жёсткие ограничения быстро отсекают кандидатов. Каждое изображение имеет уникальную комбинацию (background_type, overlay_type, shape_count). При 12 фонах x 7 оверлеях x различных количествах фигур большинство изображений имеют лишь несколько возможных совпадений с описаниями, а не 200.

  4. SVG — это структурированные данные. В отличие от растровых изображений (PNG/JPG), SVG являются XML — каждая фигура, цвет, позиция и поворот явно закодированы текстом. Компьютерное зрение не нужно.

Что делало это сложным

  1. Определение оверлея было коварным. Фоновые узоры использовали 5 разных техник SVG: элементы <line>, <circle> только со stroke, <circle> с атрибутом низкой прозрачности, <polygon> с атрибутом низкой прозрачности и отсутствие всего этого. Ключевое различие — opacity как XML-атрибут (фон) против opacity внутри style (фигуры переднего плана).

  2. Цветовой словарь был неочевидным. Названия вроде "oxblood" (#800020), "brass" (#cd7f32) и "deep sapphire" (#0f52ba) потребовали подхода с подсчётом — по одним hex-значениям их невозможно надёжно угадать.

  3. Классификация фигур по SVG-путям потребовала понимания команд SVG-путей:

    • M (moveto), L (lineto), (arc), (closepath)
Скачать инструмент
ФункцияНазначение
startGameСоздаёт игровую сессию, возвращает 200 изображений + 200 описаний
scoreGameПринимает {matches: {}, sessionId: ""}, возвращает {correctCount: N}
generateTokenПринимает sessionId, возвращает токен завершения
getLeaderboardВозвращает лучшие результаты
saveNicknameСохраняет отображаемое имя для таблицы лидеров
Начальный цвет градиента SVGКол-воСлово в описанииКол-во
#0a162828pitch28
#eef2f720frosted20
#0d0d0d19tenebrous19
#fef9f018pearlescent18
#1e0a2e18nocturnal18
#0a1a0a17midnight17
#1a0a0a15inky15
#f5f0eb14milky14
#f7f3ee14ethereal14
#f0f0f014radiant14
#f0f7f413glowing13
#1a1a2e10somber10
SVG-узорМетод определенияКол-воНазвание в описанииКол-во
Элементы <line>, горизонтальные (dy=0)Проверка x1,y1,x2,y235striped overlay35
Элементы <line>, вертикальные (dx=0)Проверка x1,y1,x2,y234lattice pattern34
<circle> с opacity="0.06" (точки)Подсчёт окружностей с низкой прозрачностью30stippled layer30
Нет элементов оверлея вообщеНет линий, нет фоновых фигур28(none)28
Элементы <line>, диагональныеПроверка направления наклона27tilted cross-lines27
Небольшие <polygon>/<path> с opacity="0.06"Фигуры с атрибутом низкой прозрачности25arrow-band texture25
<circle> с fill="none" stroke="#888"Окружности только со stroke21ringed pattern21
HexНазвание в описанииHexНазвание в описании
#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
Элемент SVGЛогика определенияНазвание в описании
<polygon> 3 вершиныСчитать пары координат, разделённые пробеламиpike
<polygon> 4 вершиныtilted square
<polygon> 5 вершинquint form
<polygon> 6 вершинbee cell
<polygon> 10 вершинasterisk
<rect> (не фон)Имеет width/height, не 200x200tilted square
<circle>Имя тегаdisc
<path> с fill-rule="evenodd"Два концентрических дуговых путиdonut
<path> с одной дугой + ZПуть в виде полукругаhalf-disc
<path> с 6+ командами LФорма креста/плюсаcrosshair
<path> с 3-5 командами LСтрелообразная формаpointer
Диапазон радиусаНазвание в описании
0–12minuscule
13–16modest
17–20mid-sized
21–25substantial
26+massive
0.5
faint
Значение rotate() в SVGНазвание в описании
0°upright
1–20°slightly tilted
21–55°diagonal
56–75°steeply angled
76–105°sideways
  • Для каждой фигуры извлечь:
    • Цвет: из атрибута fill, сопоставляется через COLOR_MAP
    • Тип: по тегу элемента + количеству вершин / командам пути
    • Размер: по координатам вершин или радиусу
    • Прозрачность: из style="opacity:X"
    • Поворот: из transform="... rotate(X)"
    • Позиция: из transform="translate(X,Y) ..."
  • 200/200 правильных
    ~21:15Вызван generateToken — WKX-9245FA70-200
    КомпонентТехнология
    Платформа челленджаBase44 (low-code конструктор приложений)
    ФронтендReact SPA (один JS-бандл, ~410 КБ)
    БэкендPython/uvicorn за Cloudflare
    CDN/ПроксиCloudflare (HTTP/3, сжатие Brotli)
    АутентификацияJWT (HS256), хранится в localStorage
    Формат данныхSVG (base64 inline), JSON API
    РеалтаймWebSocket (socket.io) для обновлений сессии в реальном времени
    РешательPython 3 (парсинг регулярными выражениями, без внешних библиотек)
    A
    Z
  • Пончик = два концентрических дуговых пути с fill-rule="evenodd"
  • Полудиск = одиночный дуговой путь
  • Перекрестие = путь с 6+ отрезками (форма креста/плюса)
  • Указатель = путь с 3-5 отрезками (форма стрелки)