题目信息:I had this million-dollar app idea the other day, but I can’t get my routing to work! I’m only using state-of-the-art tools and frameworks, so that can’t be the problem… right? Can you navigate me to the endpoint of my dreams?
题目使用 vite
{"name": "src","private": true,"version": "0.0.0","type": "module","scripts": {"dev": "vite","build": "tsc -b && vite build","lint": "eslint .","preview": "vite preview"},"dependencies": {"@tailwindcss/vite": "^4.1.3","react": "^19.0.0","react-dom": "^19.0.0","react-router": "^7.5.0","tailwindcss": "^4.1.3"},"devDependencies": {"@types/react": "^19.0.10","@types/react-dom": "^19.0.4","@vitejs/plugin-react": "^4.3.4","globals": "^15.15.0","typescript": "~5.7.2","vite": "^6.2.0","vite-plugin-javascript-obfuscator": "^3.1.0"}
}
查看路由
// 从 react 库中导入 StrictMode 组件,它可以帮助我们在开发模式下发现潜在问题
import { StrictMode } from 'react';
// 从 react-dom/client 库中导入 createRoot 函数,用于创建 React 根节点
import { createRoot } from 'react-dom/client';
// 从 react-router 库中导入 BrowserRouter、Routes 和 Route 组件,用于实现路由功能
import { BrowserRouter, Routes, Route } from 'react-router';// 导入页面组件
// 导入 App 组件,作为首页展示
import App from './App.tsx';
// 导入 Flag 组件,用于 /flag 路径的页面展示
import Flag from './Flag.tsx';// 导入全局样式文件
import './index.css';/*** 使用 createRoot 函数创建一个 React 根节点,挂载到 id 为 'root' 的 DOM 元素上* 并渲染 React 应用程序*/
createRoot(document.getElementById('root')!).render(// 使用 StrictMode 组件包裹应用,在开发模式下检查潜在问题<StrictMode>{/* 使用 BrowserRouter 组件为应用提供路由功能 */}<BrowserRouter>{/* 使用 Routes 组件来定义一组路由规则 */}<Routes>{/* 定义首页路由,当路径为根路径时,渲染 App 组件 */}<Route index element={<App />} />{/* 定义 /flag 路径的路由,当路径为 /flag 时,渲染 Flag 组件 */}<Route path="/flag" element={<Flag />} /></Routes></BrowserRouter></StrictMode>
);
看看flag
export default function Flag() {return (<section className="text-center pt-24"><div className="flex items-center text-5xl font-bold justify-center">{'bctf{test_flag}'}</div></section>)
}
访问 /flag 返回 404 , 根据题目,我不可能修改服务器端代码,接下来我怎么访问 /flag?
The site configured at this address does not contain the requested file.If this is your site, make sure that the filename case matches the URL as well as any file permissions.
For root URLs (like `http://example.com/`) you must provide an `index.html` file.[Read the full documentation](https://help.github.com/pages/) for more information about using **GitHub Pages**.
import Header from './Header.tsx';
import AdCard from './AdCard.tsx';
import Footer from './Footer.tsx';function App() {const sponsors = ['microsoft.svg', 'google.svg', 'amazon.svg', 'netflix.svg', 'meta.svg', 'apple.svg'];return (<><Header /><section className="text-center pt-24"><h1 className="text-7xl mb-4 font-bold">Quantum b01lerchain</h1><p>Quantum-computing based blockchain technology at scale, automatically secured by cutting-edgeAI threat-assessment models.</p></section><section className="pt-14 pb-2 mb-20"><div className="w-full mx-auto -z-10 bg-gradient-to-r from-yellow-500 via-pink-400 to-red-500 transform -skew-y-3 flex flex-row"><div className="transform skew-y-3 mx-auto -my-4 flex flex-row space-x-12"><AdCardtitle="Quantum powered"src="/0f5146d5ed9441853c3f2821745a4173.jpg">Leveraging distributed quantum compute power, b01lerchain technology achieves energyefficiency where other blockchains fail.</AdCard><AdCardtitle="Blockchain at scale"src="/0bee89b07a248e27c83fc3d5951213c1.jpg">All b01lerchain transactions are backed up across 10 different blockchains,ensuring data security.</AdCard><AdCardtitle="AI++"src="/5ab557c937e38f15291c04b7e99544ad.jpg">Leveraging scalable quantum compute power, b01lerchain technology achieves energy efficiencywhere other blockchains fail.</AdCard></div></div></section><section className="mb-8"><p className="text-sm text-center text-secondary mb-3">Proudly powered by</p><div className="flex gap-x-10 gap-y-2 items-center flex-wrap justify-center px-6">{sponsors.map((l) => (<imgsrc={l}className="h-12"key={l}/>))}</div></section><h3 className="font-bold text-xl text-center mb-8">Join 1,200+ investors sharing in b01lerchain's vision.</h3><section><p className="max-w-5xl px-8 mx-auto mb-8">View our demo below:</p><imgsrc="/483032a6422b3ba7005dfa12dda874b5.jpg"className="w-full h-[30rem] object-cover object-center opacity-50"/></section><Footer /></>)
}
'use client'import { useScroll } from '../hooks/useScroll';export default function Header() {const scroll = useScroll();return (<header className={`sticky top-0 z-50 ${scroll > 0 ? 'bg-midnight/90 shadow-md backdrop-blur-sm' : 'bg-midnight hover:bg-midnight/90 hover:shadow-md hover:backdrop-blur-sm'} transition-shadow duration-300 ease-in-out`}><nav className="px-8 py-4 flex gap-4 items-center"><a href="/" className="flex items-center gap-2"><imgsrc="/icon.svg"alt="b01lerchain logo"className="size-6"/><h3 className="font-semibold">b01lerchain</h3></a><a href="/#" className="ml-auto text-inherit hover:no-underline">About</a><a href="/#" className="text-inherit hover:no-underline">FAQ</a><a href="/flag" className="text-inherit hover:no-underline">Flag</a></nav></header>)
}
查一手漏洞
Report Summary┌───────────────────┬──────┬─────────────────┬─────────┐
│ Target │ Type │ Vulnerabilities │ Secrets │
├───────────────────┼──────┼─────────────────┼─────────┤
│ package-lock.json │ npm │ 1 │ - │
└───────────────────┴──────┴─────────────────┴─────────┘
Legend:
- '-': Not scanned
- '0': Clean (no security findings detected)package-lock.json (npm)Total: 1 (UNKNOWN: 0, LOW: 0, MEDIUM: 1, HIGH: 0, CRITICAL: 0)┌─────────┬────────────────┬──────────┬────────┬───────────────────┬──────────────────────────────────────┬───────────────────────────────────────────────────────────┐
│ Library │ Vulnerability │ Severity │ Status │ Installed Version │ Fixed Version │ Title │
├─────────┼────────────────┼──────────┼────────┼───────────────────┼──────────────────────────────────────┼───────────────────────────────────────────────────────────┤
│ vite │ CVE-2025-32395 │ MEDIUM │ fixed │ 6.2.5 │ 6.2.6, 6.1.5, 6.0.15, 5.4.18, 4.5.13 │ vite: Vite has an `server.fs.deny` bypass with an invalid │
│ │ │ │ │ │ │ `request-target` │
│ │ │ │ │ │ │ https://avd.aquasec.com/nvd/cve-2025-32395 │
└─────────┴────────────────┴──────────┴────────┴───────────────────┴──────────────────────────────────────┴───────────────────────────────────────────────────────────┘
复现与修复指南:Vite再次bypass(CVE-2025-32395)
但是服务器没有 /@fs
放弃 ------------------------------------------------------------------------------------------------------------
赛后
1. SPA 是什么?
-
定义:
SPA(单页应用)是一种通过动态重写当前页面内容(而非加载新页面)实现交互的 Web 应用。所有逻辑(路由、数据获取、渲染)均在浏览器中运行,只需加载一次index.html
,后续通过 JavaScript 与 API 交互更新内容。 -
核心特点:
- 无整页刷新:页面切换时仅局部更新内容,体验更流畅。
- 前端路由:通过
react-router
或Vue Router
等库管理 URL 路径与组件的映射。 - 前后端分离:后端仅提供 API 接口,前端独立处理业务逻辑与渲染。
-
技术依赖:
依赖现代前端框架(React、Vue、Angular)及工具链(Webpack、Vite)。
2. SPA 的工作原理
- 首次加载:
- 用户访问网站时,服务器返回
index.html
和打包后的 JS/CSS 文件。 - 浏览器解析并执行 JS,渲染初始页面(如登录页或主页)。
- 用户访问网站时,服务器返回
- 后续交互:
- 用户点击链接或按钮时,前端路由库拦截请求,根据 URL 匹配对应组件。
- 前端通过 API 异步获取数据,动态更新 DOM(无需整页刷新)。
- URL 管理:
- 使用
history.pushState()
或window.location.hash
修改 URL,避免浏览器向服务器发送请求。
- 使用
我们可以使用,有效负载欺骗前端js渲染隐藏路由
以下代码实现了 修改 URL + 触发路由更新 的核心逻辑:
history.pushState(null, '', '/flag'); // Step 1: 修改 URL
window.dispatchEvent(new PopStateEvent('popstate')); // Step 2: 触发事件
Step 1: history.pushState
的作用
-
底层行为:
调用浏览器 History API 的pushState
方法,向浏览器的 会话历史栈(Session History) 中添加一条新记录。 -
关键细节:
- 修改 URL 但不刷新页面:仅更新地址栏显示,不触发 HTTP 请求(对比
window.location.href = '/flag'
会触发页面跳转)。 - 关联状态数据:第一个参数
state
允许存储任意 JSON 序列化对象(如{ page: 'flag', auth: true }
),绑定到新历史记录条目。 - 同源策略限制:新 URL 必须与当前页面同源(Same Origin),否则抛出安全错误。
- 修改 URL 但不刷新页面:仅更新地址栏显示,不触发 HTTP 请求(对比
-
数据结构示例:
history.pushState({ timestamp: Date.now(), user: 'guest' }, // 有效负载(状态数据)'', // 标题(忽略)'/flag' // 新 URL );
Step 2: PopStateEvent
的触发机制
-
事件本质:
popstate
是浏览器原生事件,通常在 用户点击后退/前进按钮 或调用history.back()
/history.go()
时触发。 -
手动触发的意义:
通过dispatchEvent
主动派发popstate
事件,模拟浏览器历史导航行为,欺骗前端路由库(如 React Router)执行路由更新逻辑。 -
事件对象细节:
new PopStateEvent('popstate', {state: history.state, // 必须与当前历史条目的 state 一致bubbles: true, // 事件是否冒泡(默认 false)cancelable: true // 事件是否可取消(默认 false) });
state
字段的强制一致性:
若手动传递的state
与history.state
不一致,React Router 等库可能忽略此次事件(视为非法状态变更)。