題目信息: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 等庫可能忽略此次事件(視為非法狀態變更)。