-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmain.go
More file actions
executable file
·122 lines (112 loc) · 3.13 KB
/
Copy pathmain.go
File metadata and controls
executable file
·122 lines (112 loc) · 3.13 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
package main
import (
"bufio"
"flag"
"fmt"
"io"
"log"
"os"
"os/exec"
"strings"
)
const (
colorRed = "\033[31m"
colorGreen = "\033[32m"
colorReset = "\033[0m"
pollutionParam = "__proto__[testparam]=testval"
detectorJS = "window.testparam == 'testval'? 'Vulnerable' : 'Not Vulnerable'"
)
func ensurePageFetch() error {
if _, err := exec.LookPath("page-fetch"); err == nil {
return nil
}
fmt.Println("page-fetch not found in PATH, attempting `go install github.com/detectify/page-fetch@latest`...")
cmd := exec.Command("go", "install", "github.com/detectify/page-fetch@latest")
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
return fmt.Errorf("failed to install page-fetch: %w", err)
}
if _, err := exec.LookPath("page-fetch"); err != nil {
return fmt.Errorf("page-fetch still not in PATH after install — check that $GOPATH/bin (or $HOME/go/bin) is in PATH")
}
return nil
}
func buildTestURL(line string) string {
if strings.Contains(line, "?") {
return line + "&" + pollutionParam
}
return line + "?" + pollutionParam
}
func testURL(testURL string) (vulnerable bool, raw string, err error) {
cmd := exec.Command("page-fetch", "-j", detectorJS, "-o", "/tmp/page-fetch-out")
stdin, err := cmd.StdinPipe()
if err != nil {
return false, "", err
}
stdout, err := cmd.StdoutPipe()
if err != nil {
return false, "", err
}
if err := cmd.Start(); err != nil {
return false, "", err
}
if _, err := io.WriteString(stdin, testURL+"\n"); err != nil {
return false, "", err
}
stdin.Close()
out, err := io.ReadAll(stdout)
if err != nil {
return false, "", err
}
if err := cmd.Wait(); err != nil {
return false, string(out), err
}
output := string(out)
for _, l := range strings.Split(output, "\n") {
if strings.HasPrefix(l, "JS") && strings.Contains(l, "Vulnerable") {
return !strings.Contains(l, "Not Vulnerable"), l, nil
}
}
return false, output, nil
}
func main() {
flag.Usage = func() {
fmt.Fprintf(os.Stderr, "Usage: %s [-v] < urls.txt\n\n", os.Args[0])
fmt.Fprintln(os.Stderr, "Reads URLs from stdin and tests each for client-side prototype pollution")
fmt.Fprintln(os.Stderr, "by appending __proto__[testparam]=testval and checking if the rendered page")
fmt.Fprintln(os.Stderr, "exposes window.testparam == 'testval'.")
fmt.Fprintln(os.Stderr)
flag.PrintDefaults()
}
verbose := flag.Bool("v", false, "Verbose mode — also print Not Vulnerable results")
flag.Parse()
if err := ensurePageFetch(); err != nil {
log.Fatal(err)
}
fmt.Println("Starting.....")
scanner := bufio.NewScanner(os.Stdin)
for scanner.Scan() {
line := strings.TrimSpace(scanner.Text())
if line == "" {
continue
}
probe := buildTestURL(line)
vuln, _, err := testURL(probe)
if err != nil {
if *verbose {
fmt.Fprintf(os.Stderr, "%sError testing %s: %v%s\n", colorRed, probe, err, colorReset)
}
continue
}
if vuln {
fmt.Printf("%sVulnerable --> %s%s\n", colorRed, probe, colorReset)
} else if *verbose {
fmt.Printf("%sNot Vulnerable --> %s%s\n", colorGreen, probe, colorReset)
}
}
if err := scanner.Err(); err != nil {
log.Println(err)
os.Exit(1)
}
}