@@ -25,19 +25,28 @@ const (
25
25
)
26
26
27
27
type WAFDetector struct {
28
- client * http.Client
29
- headers map [string ]string
30
- hostHeader string
31
- target string
28
+ clientSettings * ClientSettings
29
+ headers map [string ]string
30
+ hostHeader string
31
+ target string
32
+ }
33
+
34
+ type ClientSettings struct {
35
+ dnsResolver * dnscache.Resolver
36
+ insecureSkipVerify bool
37
+ idleConnTimeout time.Duration
38
+ maxIdleConns int
39
+ maxIdleConnsPerHost int
40
+ proxyURL * url.URL
32
41
}
33
42
34
43
func NewDetector (cfg * config.Config , dnsResolver * dnscache.Resolver ) (* WAFDetector , error ) {
35
- tr := & http. Transport {
36
- DialContext : dnscache . DialFunc ( dnsResolver , nil ) ,
37
- TLSClientConfig : & tls. Config { InsecureSkipVerify : ! cfg .TLSVerify } ,
38
- IdleConnTimeout : time .Duration (cfg .IdleConnTimeout ) * time .Second ,
39
- MaxIdleConns : cfg .MaxIdleConns ,
40
- MaxIdleConnsPerHost : cfg .MaxIdleConns , // net.http hardcodes DefaultMaxIdleConnsPerHost to 2!
44
+ clientSettings := & ClientSettings {
45
+ dnsResolver : dnsResolver ,
46
+ insecureSkipVerify : ! cfg .TLSVerify ,
47
+ idleConnTimeout : time .Duration (cfg .IdleConnTimeout ) * time .Second ,
48
+ maxIdleConns : cfg .MaxIdleConns ,
49
+ maxIdleConnsPerHost : cfg .MaxIdleConns ,
41
50
}
42
51
43
52
if cfg .Proxy != "" {
@@ -46,17 +55,7 @@ func NewDetector(cfg *config.Config, dnsResolver *dnscache.Resolver) (*WAFDetect
46
55
return nil , errors .Wrap (err , "couldn't parse proxy URL" )
47
56
}
48
57
49
- tr .Proxy = http .ProxyURL (proxyURL )
50
- }
51
-
52
- jar , err := cookiejar .New (nil )
53
- if err != nil {
54
- return nil , errors .Wrap (err , "couldn't create cookie jar" )
55
- }
56
-
57
- client := & http.Client {
58
- Transport : tr ,
59
- Jar : jar ,
58
+ clientSettings .proxyURL = proxyURL
60
59
}
61
60
62
61
target , err := url .Parse (cfg .URL )
@@ -73,20 +72,71 @@ func NewDetector(cfg *config.Config, dnsResolver *dnscache.Resolver) (*WAFDetect
73
72
}
74
73
75
74
return & WAFDetector {
76
- client : client ,
77
- headers : configuredHeaders ,
78
- hostHeader : configuredHeaders ["Host" ],
79
- target : GetTargetURLStr (target ),
75
+ clientSettings : clientSettings ,
76
+ headers : configuredHeaders ,
77
+ hostHeader : configuredHeaders ["Host" ],
78
+ target : GetTargetURLStr (target ),
80
79
}, nil
81
80
}
82
81
83
- // doRequest sends HTTP-request with malicious payload to trigger WAF.
82
+ func (w * WAFDetector ) getHttpClient () (* http.Client , error ) {
83
+ tr := & http.Transport {
84
+ DialContext : dnscache .DialFunc (w .clientSettings .dnsResolver , nil ),
85
+ TLSClientConfig : & tls.Config {InsecureSkipVerify : w .clientSettings .insecureSkipVerify },
86
+ IdleConnTimeout : w .clientSettings .idleConnTimeout ,
87
+ MaxIdleConns : w .clientSettings .maxIdleConns ,
88
+ MaxIdleConnsPerHost : w .clientSettings .maxIdleConns , // net.http hardcodes DefaultMaxIdleConnsPerHost to 2!
89
+ }
90
+
91
+ if w .clientSettings .proxyURL != nil {
92
+ tr .Proxy = http .ProxyURL (w .clientSettings .proxyURL )
93
+ }
94
+
95
+ jar , err := cookiejar .New (nil )
96
+ if err != nil {
97
+ return nil , errors .Wrap (err , "couldn't create cookie jar" )
98
+ }
99
+
100
+ client := & http.Client {
101
+ Transport : tr ,
102
+ Jar : jar ,
103
+ }
104
+
105
+ return client , nil
106
+ }
107
+
108
+ // doRequest sends HTTP-request without malicious payload to trigger WAF.
84
109
func (w * WAFDetector ) doRequest (ctx context.Context ) (* http.Response , error ) {
85
110
req , err := http .NewRequestWithContext (ctx , http .MethodGet , w .target , nil )
86
111
if err != nil {
87
112
return nil , errors .Wrap (err , "couldn't create request" )
88
113
}
89
114
115
+ for header , value := range w .headers {
116
+ req .Header .Set (header , value )
117
+ }
118
+ req .Host = w .hostHeader
119
+
120
+ client , err := w .getHttpClient ()
121
+ if err != nil {
122
+ return nil , errors .Wrap (err , "couldn't create HTTP client" )
123
+ }
124
+
125
+ resp , err := client .Do (req )
126
+ if err != nil {
127
+ return nil , errors .Wrap (err , "failed to sent request" )
128
+ }
129
+
130
+ return resp , nil
131
+ }
132
+
133
+ // doMaliciousRequest sends HTTP-request with malicious payload to trigger WAF.
134
+ func (w * WAFDetector ) doMaliciousRequest (ctx context.Context ) (* http.Response , error ) {
135
+ req , err := http .NewRequestWithContext (ctx , http .MethodGet , w .target , nil )
136
+ if err != nil {
137
+ return nil , errors .Wrap (err , "couldn't create request" )
138
+ }
139
+
90
140
queryParams := req .URL .Query ()
91
141
queryParams .Add ("a" , xssPayload )
92
142
queryParams .Add ("b" , sqliPayload )
@@ -101,7 +151,12 @@ func (w *WAFDetector) doRequest(ctx context.Context) (*http.Response, error) {
101
151
}
102
152
req .Host = w .hostHeader
103
153
104
- resp , err := w .client .Do (req )
154
+ client , err := w .getHttpClient ()
155
+ if err != nil {
156
+ return nil , errors .Wrap (err , "couldn't create HTTP client" )
157
+ }
158
+
159
+ resp , err := client .Do (req )
105
160
if err != nil {
106
161
return nil , errors .Wrap (err , "failed to sent request" )
107
162
}
@@ -111,19 +166,31 @@ func (w *WAFDetector) doRequest(ctx context.Context) (*http.Response, error) {
111
166
112
167
// DetectWAF performs WAF identification. Returns WAF name and vendor after
113
168
// the first positive match.
114
- func (w * WAFDetector ) DetectWAF (ctx context.Context ) (name , vendor string , err error ) {
169
+ func (w * WAFDetector ) DetectWAF (ctx context.Context ) (name , vendor string , checkFunc detectors. Check , err error ) {
115
170
resp , err := w .doRequest (ctx )
116
171
if err != nil {
117
- return "" , "" , errors .Wrap (err , "couldn't identify WAF " )
172
+ return "" , "" , nil , errors .Wrap (err , "couldn't perform request without attack " )
118
173
}
119
174
120
175
defer resp .Body .Close ()
121
176
177
+ respToAttack , err := w .doMaliciousRequest (ctx )
178
+ if err != nil {
179
+ return "" , "" , nil , errors .Wrap (err , "couldn't perform request with attack" )
180
+ }
181
+
182
+ defer respToAttack .Body .Close ()
183
+
184
+ resps := & detectors.Responses {
185
+ Resp : resp ,
186
+ RespToAttack : respToAttack ,
187
+ }
188
+
122
189
for _ , d := range detectors .Detectors {
123
- if d .IsWAF (resp ) {
124
- return d .WAFName , d .Vendor , nil
190
+ if d .IsWAF (resps ) {
191
+ return d .WAFName , d .Vendor , d . Check , nil
125
192
}
126
193
}
127
194
128
- return "" , "" , nil
195
+ return "" , "" , nil , nil
129
196
}
0 commit comments