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 挑战赛 2026 — 完整 Writeup

结果: 200/200 (100%) — Token: WKX-9245FA70-200 日期: 2026年3月24日


1. 发现与侦察

挑战公告

LinkedIn 上的一篇帖子宣布了 Wix KickstartX 的一项特别挑战——一个面向初级开发者的项目。 规则如下:

  • 30 分钟,只有一次机会
  • 得分最高的前 10 名直接晋级考试阶段
  • 提供了两个链接:wix-kickstartx-challenge-2026.base44.app/ 和 wixkickstart.com

找到挑战入口

主站点 wixkickstart.com 是 Wix 托管的站点(由 Wix 的 Thunderbolt 引擎服务端渲染)。 标准 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 粒子动画(首屏部分的视觉特效),并不是挑战本身。 真正的挑战位于 Base44 应用中。


2. 挑战应用的逆向工程

平台:Base44

挑战运行在 Base44 上——一个低代码应用平台。该应用是一个 单页 React 应用,所有逻辑都在一个 JS bundle 中:

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

大小: 410,809 字节(压缩后的 React + 应用逻辑 + Base44 SDK)

提取 API 表面

通过 grep 这个 JS bundle,我们绘制出了整个 API 的全貌:

数据实体: GameSession, Participant

API URL 模式:

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

App ID: 69aea07cbcb9a3dd1039a58d

Bundle 中的关键常量

root@kitploit:~
const or = 1800;   // 时间限制:1800 秒(30 分钟)
const rm = 200;    // 项目总数:200 对图片-描述

发现 JSON 提交功能

一个关键发现——该应用有 JSON 批量提交模式。来自压缩后的源码:

root@kitploit:~
function Ik({onSubmit:r, onClose:n}) {
    // ...
    h = JSON.parse(s)  // 解析 JSON 输入
    // 验证:必须是类似 { "IMG-001": "DESC-042", ... } 的对象
    r(h)  // 一次性提交所有匹配
}

模态框中的占位文本:

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

这意味着我们不需要在 UI 中点击 200 次——我们可以通过编程方式提交一个包含 全部 200 个匹配的 JSON 映射。

认证流程

Base44 SDK 使用 JWT 认证:

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

JWT payload 包含:

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

所有 API 调用所需的 headers:

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 项(显示顺序)
  "descOrder": ["DESC-125", "DESC-109", ...],  // 200 项(显示顺序)
  "imagesData": { "IMG-001": "data:image/svg+xml;base64,...", ... },  // 200 个 SVG
  "descriptionsData": { "DESC-105": "milky field, overlaid with...", ... }  // 200 段文本
}

图片格式:内联 SVG(base64 编码)

每张图片是一个 200x200 的 SVG,包含:

  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)"/>
  <!-- 可选的叠加线条/圆点/圆环在此 -->
  <polygon points="..." fill="#E2725B" transform="translate(128.6,185.7) rotate(90)"
           style="opacity:0.5;filter:drop-shadow(...)"/>
  <!-- 更多形状... -->
</svg>

描述格式:结构化自然语言

每条描述都遵循严格的模式:

root@kitploit:~
{背景} field[, overlaid with {叠加}]. {N} elements total:
{大小} {不透明度} {颜色} {形状} ({旋转}, at {位置}) ·
{大小} {不透明度} {颜色} {形状} ({旋转}, at {位置}) · ...

示例:

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 种)

通过统计两侧的出现次数,我们建立了 1:1 的映射:

方法: 统计全部 200 个 SVG 中唯一的渐变起始颜色,统计全部 200 条描述中唯一的 首单词,按次数进行匹配。

叠加图案映射(7 种)

SVG 叠加图案根据类型使用不同的实现方式:

关键洞察: 背景图案元素将 opacity 用作 XML 属性(例如,opacity="0.06"), 而前景形状将 opacity 放在 style 属性中(例如,style="opacity:0.7")。 这一区别对于将背景图案与实际形状区分开来至关重要。

颜色映射(28 种颜色)

全部 200 个 SVG 恰好使用 28 种唯一的十六进制填充颜色。全部 200 条描述恰好使用 28 种 唯一的颜色名称。

形状类型映射(10 种)

大小映射

基于形状顶点到原点的最大半径:

不透明度映射

SVG 不透明度值描述名称
1.0solid
0.85nearly solid
0.7semi-transparent
0.5

旋转映射

位置网格

形状放置在 7x7 的网格上,坐标为: X 和 Y 方向均为 [14.3, 42.9, 71.4, 100.0, 128.6, 157.1, 185.7]。

这些坐标映射到列名: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. 提取背景 — 读取 <linearGradient id="bg"> 中的第一个 <stop> 颜色
  3. 分类叠加图案 — 检查 <line>、<circle fill="none" stroke>、低不透明度圆点/形状
  4. 提取形状 — 找到所有 <polygon>、<circle>、<path>、<rect> 元素,这些元素需要:
    • 不是背景矩形(跳过 width="200")
    • 不是暗角矩形(跳过 fill="url(#vig)")
    • 不是叠加图案元素(跳过作为属性的 opacity="0.0x")
    • 必须有 style="opacity:..."(所有前景形状都有)
  5. 对于每个形状,提取:
    • 颜色:来自 fill 属性,通过 COLOR_MAP 映射

第 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)
    (颜色名称)
    (形状类型)
    ((旋转), at (位置))
    

第 4 步:基于评分的匹配

匹配算法使用贪心评分方法:

root@kitploit:~
def score_match(img, desc):
    # 硬约束 — 必须完全匹配
    if img['num'] != desc['num']:      return -10000  # 形状数量
    if img['bg'] != desc['bg']:        return -10000  # 背景类型
    if img['overlay'] != desc['overlay']: return -10000  # 叠加类型

    score = 100  # 硬约束匹配的基础分

    # 软评分 — 颜色重叠
    for color in img_colors:
        if color in desc_colors:
            score += 10

    # 软评分 — 类型重叠
    for type in img_types:
        if type in desc_types:
            score += 8

    # 逐形状细节匹配(匈牙利式贪心)
    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 — 所有游戏数据)

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. 生成 Token

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 — 200/200 正确
~21:15调用 generateToken —

总求解时间:在 30 分钟的窗口内约 8 分钟。


6. 技术栈总结


7. 关键洞察

为什么这能成功

  1. JSON 提交端点是关键推动因素——没有它,我们就需要浏览器自动化来点击 400 次(选择图片 + 选择描述 x 200)。

  2. 基于计数的映射是颜色、背景和叠加图案匹配的突破点。 我们不是猜测十六进制 #cd7f32 在英语中对应什么,而是统计出它在 SVG 中出现了 65 次,而 "brass" 在描述中出现了 65 次——唯一计数 = 保证匹配。

  3. **硬约束能快速排除候选。**每张图片都有独特的 (背景类型, 叠加类型, 形状数量) 组合。有 12 种背景 x 7 种叠加 x 不同的形状数量,大多数图片只有少数几个可能的 描述匹配,而不是 200 个。

  4. **SVG 是结构化数据。**与光栅图像(PNG/JPG)不同,SVG 是 XML——每个形状、 颜色、位置和旋转都被显式编码为文本。不需要计算机视觉。

挑战在哪里

  1. **叠加图案检测很棘手。**背景图案使用了 5 种不同的 SVG 技术: <line> 元素、仅描边的 <circle>、具有低不透明度属性的 <circle>、 具有低不透明度属性的 <polygon>,以及以上全部都没有的情况。关键区别在于 opacity 是 XML 属性(背景)还是在 style 内(前景形状)。

  2. **颜色词汇并不直观。**像 "oxblood" (#800020)、"brass" (#cd7f32) 和 "deep sapphire" (#0f52ba) 这样的名称需要计数方法——仅凭十六进制值 你无法可靠地猜出来。

  3. 从 SVG 路径进行形状分类需要理解 SVG 路径命令:

    • M(moveto)、L(lineto)、A(arc)、Z(closepath)
    • donut(甜甜圈)= 两条带 fill-rule="evenodd" 的同心圆弧路径
下载工具
函数用途
startGame创建一个游戏会话,返回 200 张图片 + 200 条描述
scoreGame接受 {matches: {}, sessionId: ""},返回 {correctCount: N}
generateToken接受 sessionId,返回完成 token
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
带 opacity="0.06" 的 <circle>(圆点)统计低不透明度圆形30stippled layer30
完全没有叠加元素无线条、无背景形状28(无)28
<line> 元素,对角线检查斜率方向27tilted cross-lines27
带 opacity="0.06" 的小型 <polygon>/<path>具有低不透明度属性的形状25arrow-band texture25
带 fill="none" stroke="#888" 的 <circle>仅描边的圆形21ringed 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
带 fill-rule="evenodd" 的 <path>两条同心圆弧路径donut
带单条弧线 + Z 的 <path>半圆路径half-disc
带 6+ 个 L 命令的 <path>十字/加号形状crosshair
带 3-5 个 L 命令的 <path>箭头状形状pointer
半径范围描述名称
0–12minuscule
13–16modest
17–20mid-sized
21–25substantial
26+massive
faint
SVG rotate() 值描述名称
0°upright
1–20°slightly tilted
21–55°diagonal
56–75°steeply angled
76–105°sideways
  • 类型:来自元素标签 + 顶点数 / 路径命令
  • 大小:来自顶点坐标或半径
  • 不透明度:来自 style="opacity:X"
  • 旋转:来自 transform="... rotate(X)"
  • 位置:来自 transform="translate(X,Y) ..."
  • WKX-9245FA70-200
    组件技术
    挑战平台Base44(低代码应用构建器)
    前端React SPA(单个 JS bundle,约 410KB)
    后端Python/uvicorn,位于 Cloudflare 之后
    CDN/代理Cloudflare(HTTP/3,Brotli 压缩)
    认证JWT(HS256),存储在 localStorage 中
    数据格式SVG(base64 内联)、JSON API
    实时WebSocket(socket.io),用于实时会话更新
    求解器Python 3(正则解析,无外部库)
  • half-disc(半圆) = 单条圆弧路径
  • crosshair(十字准星) = 带 6+ 条线段(十字/加号)的路径
  • pointer(指针) = 带 3-5 条线段(箭头)的路径