Ticket #20348: grepsonar.go

File grepsonar.go, 3.1 KB (added by dcf, 2 years ago)

Program to search sonar.http reports for HTTP fingerprints.

Line 
1/*
2Searches for patterns in Project Sonar HTTP (TCP/80) data files.
3https://scans.io/study/sonar.http
4Usage: ./grepsonar 20160830-http.gz
5*/
6
7package main
8
9import (
10        "bufio"
11        "bytes"
12        "encoding/base64"
13        "encoding/json"
14        "flag"
15        "fmt"
16        "io"
17        "os"
18        "os/exec"
19        "runtime"
20        "strings"
21)
22
23var lineChan chan []byte
24
25func openGzip(filename string) (io.ReadCloser, error) {
26        cmd := exec.Command("gzip", "-d", "-c", filename)
27        stdout, err := cmd.StdoutPipe()
28        if err != nil {
29                return nil, err
30        }
31        err = cmd.Start()
32        return stdout, err
33}
34
35type Entry struct {
36        IP   string `json:"ip"`
37        Port string `json:"port"`
38        Data string `json:"data"`
39        // VHost string `json:"vhost"`
40        // Host  string `json:"host"`
41}
42
43func predicate(data []byte) bool {
44        // https://bugs.torproject.org/20348#comment:149
45        /*
46        // many false positives
47        if bytes.Index(data, []byte("21 Jul 1977 07:30:00 GMT")) != -1 {
48                return true
49        }
50        */
51        if bytes.Index(data, []byte("Mon, 16-Jan-2017 17:01:22 GMT")) != -1 {
52                return true
53        }
54        if bytes.Index(data, []byte("<meta http-equiv=\"REFRESH\" content=\"1; URL='")) != -1 {
55                return true
56        }
57        /*
58        // false positives
59        if bytes.HasPrefix(data, []byte("HTTP/1.1 302 Moved Temporarily\r\nServer: nginx\r\n")) &&
60                bytes.Index(data, []byte("\r\nLOCATION: ")) != -1 {
61                return true
62        }
63        */
64        // https://bugs.torproject.org/20348#comment:159
65        if bytes.Index(data, []byte("Content-Type: text/html; charset=UTF-8\r\n\r\n<HTML><HEAD><meta http-equiv=\"content-type\" content=\"text/html;charset=utf-8\">\n<TITLE>302 Found</TITLE></HEAD><BODY>\n<H1>302 Found</H1>\nThe document has moved\n<A HREF=\"")) != -1 {
66                return true
67        }
68
69        return false
70}
71
72func processLine(line []byte) error {
73        var entry Entry
74        err := json.Unmarshal(line, &entry)
75        if err != nil {
76                return err
77        }
78        data, err := base64.StdEncoding.DecodeString(entry.Data)
79        if err != nil {
80                return nil
81        }
82        if predicate(data) {
83                fmt.Printf("%15s %s %q\n", entry.IP, entry.Port, data)
84        }
85        return nil
86}
87
88// processFromChan reads from lineChan and feeds them to processLine.
89// panics on any error.
90func processFromChan() {
91        for line := range lineChan {
92                err := processLine(line)
93                if err != nil {
94                        fmt.Fprintln(os.Stderr, err)
95                        os.Exit(1)
96                }
97        }
98}
99
100// processInput reads lines and passes them into lineChan.
101func processInput(r io.Reader) error {
102        s := bufio.NewScanner(r)
103        s.Buffer(nil, 1024*1024)
104
105        for s.Scan() {
106                line := s.Bytes()
107                // Make a copy because the memory used by s.Bytes may be
108                // overwritten.
109                lineCopy := make([]byte, len(line))
110                copy(lineCopy, line)
111                lineChan <- lineCopy
112        }
113
114        return s.Err()
115}
116
117func processInputFile(filename string) error {
118        var f io.ReadCloser
119        var err error
120
121        if strings.HasSuffix(filename, ".gz") {
122                f, err = openGzip(filename)
123        } else {
124                f, err = os.Open(filename)
125        }
126        if err != nil {
127                return err
128        }
129        defer f.Close()
130
131        return processInput(f)
132}
133
134func main() {
135        flag.Parse()
136
137        lineChan = make(chan []byte, 100)
138        defer func() { close(lineChan) }()
139
140        for i := 0; i < runtime.GOMAXPROCS(0); i++ {
141                go processFromChan()
142        }
143
144        for _, filename := range flag.Args() {
145                err := processInputFile(filename)
146                if err != nil {
147                        fmt.Fprintln(os.Stderr, err)
148                        os.Exit(1)
149                }
150        }
151}