Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
projectdiscovery
GitHub Repository: projectdiscovery/nuclei
Path: blob/dev/pkg/protocols/http/request_annotations.go
2070 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
33
// ErrTimeoutAnnotationDeadline is the error returned when a specific amount of time was exceeded for a request
34
// which was alloted using @timeout annotation this usually means that vulnerability was not found
35
// in rare case it could also happen due to network congestion
36
// the assigned class is TemplateLogic since this in almost every case means that server is not vulnerable
37
ErrTimeoutAnnotationDeadline = errkit.New("timeout annotation deadline exceeded").SetKind(nucleierr.ErrTemplateLogic).Build()
38
// ErrRequestTimeoutDeadline is the error returned when a specific amount of time was exceeded for a request
39
// this happens when the request execution exceeds alloted time
40
ErrRequestTimeoutDeadline = errkit.New("request timeout deadline exceeded when notimeout is set").SetKind(errkit.ErrKindDeadline).Build()
41
)
42
43
type flowMark int
44
45
const (
46
Once flowMark = iota
47
)
48
49
// parseFlowAnnotations and override requests flow
50
func parseFlowAnnotations(rawRequest string) (flowMark, bool) {
51
var fm flowMark
52
// parse request for known override annotations
53
var hasFlowOverride bool
54
// @once
55
if reOnceAnnotation.MatchString(rawRequest) {
56
fm = Once
57
hasFlowOverride = true
58
}
59
60
return fm, hasFlowOverride
61
}
62
63
type annotationOverrides struct {
64
request *retryablehttp.Request
65
cancelFunc context.CancelFunc
66
interactshURLs []string
67
}
68
69
// parseAnnotations and override requests settings
70
func (r *Request) parseAnnotations(rawRequest string, request *retryablehttp.Request) (overrides annotationOverrides, modified bool) {
71
// parse request for known override annotations
72
73
// @Host:target
74
if hosts := reHostAnnotation.FindStringSubmatch(rawRequest); len(hosts) > 0 {
75
value := strings.TrimSpace(hosts[1])
76
// handle scheme
77
switch {
78
case stringsutil.HasPrefixI(value, "http://"):
79
request.Scheme = "http"
80
case stringsutil.HasPrefixI(value, "https://"):
81
request.Scheme = "https"
82
}
83
84
value = stringsutil.TrimPrefixAny(value, "http://", "https://")
85
86
if isHostPort(value) {
87
request.URL.Host = value
88
} else {
89
hostPort := value
90
port := request.Port()
91
if port != "" {
92
hostPort = net.JoinHostPort(hostPort, port)
93
}
94
request.URL.Host = hostPort
95
}
96
modified = true
97
}
98
99
// @tls-sni:target
100
if hosts := reSniAnnotation.FindStringSubmatch(rawRequest); len(hosts) > 0 {
101
value := strings.TrimSpace(hosts[1])
102
value = stringsutil.TrimPrefixAny(value, "http://", "https://")
103
104
var literal bool
105
switch value {
106
case "request.host":
107
value = request.Host
108
case "interactsh-url":
109
if interactshURL, err := r.options.Interactsh.NewURLWithData("interactsh-url"); err == nil {
110
value = interactshURL
111
}
112
overrides.interactshURLs = append(overrides.interactshURLs, value)
113
default:
114
literal = true
115
}
116
ctx := context.WithValue(request.Context(), fastdialer.SniName, value)
117
request = request.Clone(ctx)
118
119
if literal {
120
request.TLS = &tls.ConnectionState{ServerName: value}
121
}
122
modified = true
123
}
124
125
// @timeout:duration
126
if r.connConfiguration.NoTimeout {
127
modified = true
128
var ctx context.Context
129
130
if duration := reTimeoutAnnotation.FindStringSubmatch(rawRequest); len(duration) > 0 {
131
value := strings.TrimSpace(duration[1])
132
if parsed, err := time.ParseDuration(value); err == nil {
133
// to avoid dos via timeout request annotation in http template we set it to maximum of 2 minutes
134
if parsed > 2*time.Minute {
135
parsed = 2 * time.Minute
136
}
137
//nolint:govet // cancelled automatically by withTimeout
138
// global timeout is overridden by annotation by replacing context
139
ctx, overrides.cancelFunc = context.WithTimeoutCause(context.TODO(), parsed, ErrTimeoutAnnotationDeadline)
140
// add timeout value to context
141
ctx = context.WithValue(ctx, httpclientpool.WithCustomTimeout{}, httpclientpool.WithCustomTimeout{Timeout: parsed})
142
request = request.Clone(ctx)
143
}
144
} else {
145
//nolint:govet // cancelled automatically by withTimeout
146
// global timeout is overridden by annotation by replacing context
147
ctx, overrides.cancelFunc = context.WithTimeoutCause(context.TODO(), r.options.Options.GetTimeouts().HttpTimeout, ErrRequestTimeoutDeadline)
148
request = request.Clone(ctx)
149
}
150
}
151
152
overrides.request = request
153
154
return
155
}
156
157
func isHostPort(value string) bool {
158
_, port, err := net.SplitHostPort(value)
159
if err != nil {
160
return false
161
}
162
if !iputil.IsPort(port) {
163
return false
164
}
165
return true
166
}
167
168