-
Notifications
You must be signed in to change notification settings - Fork 322
/
Copy pathprocessing.go
273 lines (252 loc) · 8.54 KB
/
processing.go
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
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
package zgrab2
import (
"context"
"encoding/json"
"fmt"
log "github.com/sirupsen/logrus"
"github.com/zmap/zcrypto/tls"
"net"
"sync"
"github.com/zmap/zgrab2/lib/output"
)
// Grab contains all scan responses for a single host
type Grab struct {
IP string `json:"ip,omitempty"`
Port uint `json:"port,omitempty"`
Domain string `json:"domain,omitempty"`
Data map[string]ScanResponse `json:"data,omitempty"`
}
// ScanTarget is the host that will be scanned
type ScanTarget struct {
IP net.IP
Domain string
Tag string
Port uint
}
func (target ScanTarget) String() string {
if target.IP == nil && target.Domain == "" {
return "<empty target>"
}
res := ""
if target.IP != nil && target.Domain != "" {
res = target.Domain + "(" + target.IP.String() + ")"
} else if target.IP != nil {
res = target.IP.String()
} else {
res = target.Domain
}
if target.Tag != "" {
res += " tag:" + target.Tag
}
return res
}
// Host gets the host identifier as a string: the IP address if it is available,
// or the domain if not.
func (target *ScanTarget) Host() string {
if target.IP != nil {
return target.IP.String()
} else if target.Domain != "" {
return target.Domain
}
log.Fatalf("Bad target %s: no IP/Domain", target.String())
panic("unreachable")
}
// GetDefaultTCPDialer returns a TCP dialer suitable for modules with default TCP behavior
func GetDefaultTCPDialer(flags *BaseFlags) func(ctx context.Context, t *ScanTarget, addr string) (net.Conn, error) {
// create dialer once and reuse it
dialer := GetTimeoutConnectionDialer(flags.Timeout)
return func(ctx context.Context, t *ScanTarget, addr string) (net.Conn, error) {
// If the scan is for a specific IP, and a domain name is provided, we
// don't want to just let the http library resolve the domain. Create
// a fake resolver that we will use, that always returns the IP we are
// given to scan.
if t.IP != nil && t.Domain != "" {
host, _, err := net.SplitHostPort(addr)
if err != nil {
log.Errorf("http/scanner.go dialContext: unable to split host:port '%s'", addr)
log.Errorf("No fake resolver, IP address may be incorrect: %s", err)
} else {
// In the case of redirects, we don't want to blindly use the
// IP we were given to scan, however. Only use the fake
// resolver if the domain originally specified for the scan
// target matches the current address being looked up in this
// DialContext.
if host == t.Domain {
resolver, err := NewFakeResolver(t.IP.String())
if err != nil {
return nil, err
}
dialer.Dialer.Resolver = resolver
}
}
}
err := dialer.SetRandomLocalAddr("tcp", config.localAddrs, config.localPorts)
if err != nil {
return nil, fmt.Errorf("could not set random local address: %w", err)
}
conn, err := dialer.DialContext(ctx, "tcp", addr)
if err != nil {
return nil, err
}
return conn, nil
}
}
// GetDefaultTLSDialer returns a TLS-over-TCP dialer suitable for modules with default TLS behavior
func GetDefaultTLSDialer(flags *BaseFlags, tlsFlags *TLSFlags) func(ctx context.Context, t *ScanTarget, addr string) (net.Conn, error) {
return func(ctx context.Context, t *ScanTarget, addr string) (net.Conn, error) {
l4Conn, err := GetDefaultTCPDialer(flags)(ctx, t, addr)
if err != nil {
return nil, fmt.Errorf("could not initiate a L4 connection with L4 dialer: %v", err)
}
return GetDefaultTLSWrapper(tlsFlags)(ctx, t, l4Conn)
}
}
// GetDefaultTLSWrapper uses the TLS flags to create a wrapper that upgrades a TCP connection to a TLS connection.
func GetDefaultTLSWrapper(tlsFlags *TLSFlags) func(ctx context.Context, t *ScanTarget, conn net.Conn) (*TLSConnection, error) {
return func(ctx context.Context, t *ScanTarget, conn net.Conn) (*TLSConnection, error) {
tlsConfig, err := tlsFlags.GetTLSConfigForTarget(t)
if err != nil {
return nil, fmt.Errorf("could not get tls config for target %s: %w", t.String(), err)
}
// Set SNI server name on redirects unless --server-name was used (issue #300)
// - t.Domain is always set to the *original* Host so it's not useful for setting SNI
// - host is the current target of the request in this context; this is true for the
// initial request as well as subsequent requests caused by redirects
// - scan.scanner.config.ServerName is the value from --server-name if one was specified
// If SNI is enabled and --server-name is not set, use the target host for the SNI server name
if !tlsFlags.NoSNI && tlsFlags.ServerName == "" {
host := t.Domain
// RFC4366: Literal IPv4 and IPv6 addresses are not permitted in "HostName"
if i := net.ParseIP(host); i == nil {
tlsConfig.ServerName = host
}
}
tlsConn := TLSConnection{
Conn: *(tls.Client(conn, tlsConfig)),
flags: tlsFlags,
}
err = tlsConn.Handshake()
if err != nil {
return nil, fmt.Errorf("could not perform tls handshake for target %s: %w", t.String(), err)
}
return &tlsConn, err
}
}
// GetDefaultUDPDialer returns a UDP dialer suitable for modules with default UDP behavior
func GetDefaultUDPDialer(flags *BaseFlags) func(ctx context.Context, t *ScanTarget, addr string) (net.Conn, error) {
// create dialer once and reuse it
dialer := GetTimeoutConnectionDialer(flags.Timeout)
return func(ctx context.Context, t *ScanTarget, addr string) (net.Conn, error) {
err := dialer.SetRandomLocalAddr("udp", config.localAddrs, config.localPorts)
if err != nil {
return nil, fmt.Errorf("could not set random local address: %w", err)
}
return dialer.DialContext(ctx, "udp", addr)
}
}
// BuildGrabFromInputResponse constructs a Grab object for a target, given the
// scan responses.
func BuildGrabFromInputResponse(t *ScanTarget, responses map[string]ScanResponse) *Grab {
var ipstr string
if t.IP != nil {
ipstr = t.IP.String()
}
return &Grab{
IP: ipstr,
Port: t.Port,
Domain: t.Domain,
Data: responses,
}
}
// EncodeGrab serializes a Grab to JSON, handling the debug fields if necessary.
func EncodeGrab(raw *Grab, includeDebug bool) ([]byte, error) {
var outputData any
if includeDebug {
outputData = raw
} else {
// If the caller doesn't explicitly request debug data, strip it out.
// TODO: Migrate this to the ZMap fork of sheriff, once it's more
// stable.
processor := output.Processor{Verbose: false}
stripped, err := processor.Process(raw)
if err != nil {
log.Debugf("Error processing results: %v", err)
stripped = raw
}
outputData = stripped
}
return json.Marshal(outputData)
}
// grabTarget calls handler for each action
func grabTarget(input ScanTarget, m *Monitor) []byte {
moduleResult := make(map[string]ScanResponse)
for _, scannerName := range orderedScanners {
scanner := scanners[scannerName]
trigger := (*scanner).GetTrigger()
if input.Tag != trigger {
continue
}
defer func(name string) {
if e := recover(); e != nil {
log.Errorf("Panic on scanner %s when scanning target %s: %#v", scannerName, input.String(), e)
// Bubble out original error (with original stack) in lieu of explicitly logging the stack / error
panic(e)
}
}(scannerName)
name, res := RunScanner(*scanner, m, input)
moduleResult[name] = res
if res.Error != nil && !config.Multiple.ContinueOnError {
break
}
if res.Status == SCAN_SUCCESS && config.Multiple.BreakOnSuccess {
break
}
}
raw := BuildGrabFromInputResponse(&input, moduleResult)
result, err := EncodeGrab(raw, includeDebugOutput())
if err != nil {
log.Errorf("unable to marshal data: %s", err)
}
return result
}
// Process sets up an output encoder, input reader, and starts grab workers.
func Process(mon *Monitor) {
workers := config.Senders
processQueue := make(chan ScanTarget, workers*4)
outputQueue := make(chan []byte, workers*4)
//Create wait groups
var workerDone sync.WaitGroup
var outputDone sync.WaitGroup
workerDone.Add(int(workers))
outputDone.Add(1)
// Start the output encoder
go func() {
defer outputDone.Done()
if err := config.outputResults(outputQueue); err != nil {
log.Fatal(err)
}
}()
//Start all the workers
for i := 0; i < workers; i++ {
go func(i int) {
for _, scannerName := range orderedScanners {
scanner := *scanners[scannerName]
scanner.InitPerSender(i)
}
for obj := range processQueue {
for run := uint(0); run < uint(config.ConnectionsPerHost); run++ {
result := grabTarget(obj, mon)
outputQueue <- result
}
}
workerDone.Done()
}(i)
}
if err := config.inputTargets(processQueue); err != nil {
log.Fatal(err)
}
close(processQueue)
workerDone.Wait()
close(outputQueue)
outputDone.Wait()
}