Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
projectdiscovery
GitHub Repository: projectdiscovery/nuclei
Path: blob/dev/pkg/protocols/http/request_annotations.go
2847 views
1
package http
2
3
import (
4
"context"
5
"crypto/tls"
6
"net"
7
"regexp"
8
"strings"
9
"time"
10
11
"github.com/projectdiscovery/fastdialer/fastdialer"
12
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/http/httpclientpool"
13
"github.com/projectdiscovery/nuclei/v3/pkg/types/nucleierr"
14
"github.com/projectdiscovery/retryablehttp-go"
15
"github.com/projectdiscovery/utils/errkit"
16
iputil "github.com/projectdiscovery/utils/ip"
17
stringsutil "github.com/projectdiscovery/utils/strings"
18
)
19
20
var (
21
// @Host:target overrides the input target with the annotated one (similar to self-contained requests)
22
reHostAnnotation = regexp.MustCompile(`(?m)^@Host:\s*(.+)\s*$`)
23
// @tls-sni:target overrides the input target with the annotated one
24
// special values:
25
// request.host: takes the value from the host header
26
// target: overrides with the specific value
27
reSniAnnotation = regexp.MustCompile(`(?m)^@tls-sni:\s*(.+)\s*$`)
28
// @timeout:duration overrides the input timeout with a custom duration
29
reTimeoutAnnotation = regexp.MustCompile(`(?m)^@timeout:\s*(.+)\s*$`)
30
// @once sets the request to be executed only once for a specific URL
31
reOnceAnnotation = regexp.MustCompile(`(?m)^@once\s*$`)
32
// maxAnnotationTimeout is the maximum timeout allowed for @timeout
33
// annotations to prevent DoS attacks via extremely large timeout values.
34
maxAnnotationTimeout = 5 * time.Minute
35
// ErrTimeoutAnnotationDeadline is the error returned when a specific amount of time was exceeded for a request
36
// which was allotted using @timeout annotation this usually means that vulnerability was not found
37
// in rare case it could also happen due to network congestion
38
// the assigned class is TemplateLogic since this in almost every case means that server is not vulnerable
39
ErrTimeoutAnnotationDeadline = errkit.New("timeout annotation deadline exceeded").SetKind(nucleierr.ErrTemplateLogic).Build()
40
// ErrRequestTimeoutDeadline is the error returned when a specific amount of time was exceeded for a request
41
// this happens when the request execution exceeds allotted time
42
ErrRequestTimeoutDeadline = errkit.New("request timeout deadline exceeded when notimeout is set").SetKind(errkit.ErrKindDeadline).Build()
43
)
44
45
type flowMark int
46
47
const (
48
Once flowMark = iota
49
)
50
51
// parseFlowAnnotations and override requests flow
52
func parseFlowAnnotations(rawRequest string) (flowMark, bool) {
53
var fm flowMark
54
// parse request for known override annotations
55
var hasFlowOverride bool
56
// @once
57
if reOnceAnnotation.MatchString(rawRequest) {
58
fm = Once
59
hasFlowOverride = true
60
}
61
62
return fm, hasFlowOverride
63
}
64
65
type annotationOverrides struct {
66
request *retryablehttp.Request
67
cancelFunc context.CancelFunc
68
interactshURLs []string
69
}
70
71
// parseAnnotations and override requests settings
72
func (r *Request) parseAnnotations(rawRequest string, request *retryablehttp.Request) (overrides annotationOverrides, modified bool) {
73
// parse request for known override annotations
74
75
// @Host:target
76
if hosts := reHostAnnotation.FindStringSubmatch(rawRequest); len(hosts) > 0 {
77
value := strings.TrimSpace(hosts[1])
78
// handle scheme
79
switch {
80
case stringsutil.HasPrefixI(value, "http://"):
81
request.Scheme = "http"
82
case stringsutil.HasPrefixI(value, "https://"):
83
request.Scheme = "https"
84
}
85
86
value = stringsutil.TrimPrefixAny(value, "http://", "https://")
87
88
if isHostPort(value) {
89
request.URL.Host = value
90
} else {
91
hostPort := value
92
port := request.Port()
93
if port != "" {
94
hostPort = net.JoinHostPort(hostPort, port)
95
}
96
request.URL.Host = hostPort
97
}
98
modified = true
99
}
100
101
// @tls-sni:target
102
if hosts := reSniAnnotation.FindStringSubmatch(rawRequest); len(hosts) > 0 {
103
value := strings.TrimSpace(hosts[1])
104
value = stringsutil.TrimPrefixAny(value, "http://", "https://")
105
106
var literal bool
107
switch value {
108
case "request.host":
109
value = request.Host
110
case "interactsh-url":
111
if interactshURL, err := r.options.Interactsh.NewURLWithData("interactsh-url"); err == nil {
112
value = interactshURL
113
}
114
overrides.interactshURLs = append(overrides.interactshURLs, value)
115
default:
116
literal = true
117
}
118
ctx := context.WithValue(request.Context(), fastdialer.SniName, value)
119
request = request.Clone(ctx)
120
121
if literal {
122
request.TLS = &tls.ConnectionState{ServerName: value}
123
}
124
modified = true
125
}
126
127
// @timeout:duration
128
if r.connConfiguration.NoTimeout {
129
modified = true
130
var ctx context.Context
131
132
if duration := reTimeoutAnnotation.FindStringSubmatch(rawRequest); len(duration) > 0 {
133
value := strings.TrimSpace(duration[1])
134
if parsedTimeout, err := time.ParseDuration(value); err == nil {
135
// Cap at maximum allowed timeout to prevent DoS via extremely large timeout values
136
if parsedTimeout > maxAnnotationTimeout {
137
parsedTimeout = maxAnnotationTimeout
138
}
139
140
//nolint:govet // cancelled automatically by withTimeout
141
// global timeout is overridden by annotation by replacing context
142
ctx, overrides.cancelFunc = context.WithTimeoutCause(context.TODO(), parsedTimeout, ErrTimeoutAnnotationDeadline)
143
// add timeout value to context
144
ctx = context.WithValue(ctx, httpclientpool.WithCustomTimeout{}, httpclientpool.WithCustomTimeout{Timeout: parsedTimeout})
145
request = request.Clone(ctx)
146
}
147
} else {
148
//nolint:govet // cancelled automatically by withTimeout
149
// global timeout is overridden by annotation by replacing context
150
ctx, overrides.cancelFunc = context.WithTimeoutCause(context.TODO(), r.options.Options.GetTimeouts().HttpTimeout, ErrRequestTimeoutDeadline)
151
request = request.Clone(ctx)
152
}
153
}
154
155
overrides.request = request
156
157
return
158
}
159
160
func isHostPort(value string) bool {
161
_, port, err := net.SplitHostPort(value)
162
if err != nil {
163
return false
164
}
165
if !iputil.IsPort(port) {
166
return false
167
}
168
return true
169
}
170
171