Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
projectdiscovery
GitHub Repository: projectdiscovery/nuclei
Path: blob/dev/pkg/protocols/common/protocolstate/state.go
2845 views
1
package protocolstate
2
3
import (
4
"context"
5
"fmt"
6
"net"
7
"net/url"
8
"time"
9
10
"github.com/go-sql-driver/mysql"
11
"github.com/pkg/errors"
12
"golang.org/x/net/proxy"
13
14
"github.com/projectdiscovery/fastdialer/fastdialer"
15
"github.com/projectdiscovery/mapcidr/asn"
16
"github.com/projectdiscovery/networkpolicy"
17
"github.com/projectdiscovery/nuclei/v3/pkg/types"
18
"github.com/projectdiscovery/nuclei/v3/pkg/utils/expand"
19
"github.com/projectdiscovery/retryablehttp-go"
20
mapsutil "github.com/projectdiscovery/utils/maps"
21
)
22
23
var (
24
dialers *mapsutil.SyncLockMap[string, *Dialers]
25
)
26
27
func init() {
28
dialers = mapsutil.NewSyncLockMap[string, *Dialers]()
29
}
30
31
func GetDialers(ctx context.Context) *Dialers {
32
executionContext := GetExecutionContext(ctx)
33
dialers, ok := dialers.Get(executionContext.ExecutionID)
34
if !ok {
35
return nil
36
}
37
return dialers
38
}
39
40
func GetDialersWithId(id string) *Dialers {
41
dialers, ok := dialers.Get(id)
42
if !ok {
43
return nil
44
}
45
return dialers
46
}
47
48
func ShouldInit(id string) bool {
49
dialer, ok := dialers.Get(id)
50
if !ok {
51
return true
52
}
53
return dialer == nil
54
}
55
56
// Init creates the Dialers instance based on user configuration
57
func Init(options *types.Options) error {
58
if GetDialersWithId(options.ExecutionId) != nil {
59
return nil
60
}
61
62
return initDialers(options)
63
}
64
65
// initDialers is the internal implementation of Init
66
func initDialers(options *types.Options) error {
67
opts := fastdialer.DefaultOptions
68
opts.DialerTimeout = options.GetTimeouts().DialTimeout
69
if options.DialerKeepAlive > 0 {
70
opts.DialerKeepAlive = options.DialerKeepAlive
71
}
72
73
var expandedDenyList []string
74
for _, excludeTarget := range options.ExcludeTargets {
75
switch {
76
case asn.IsASN(excludeTarget):
77
expandedDenyList = append(expandedDenyList, expand.ASN(excludeTarget)...)
78
default:
79
expandedDenyList = append(expandedDenyList, excludeTarget)
80
}
81
}
82
83
if options.RestrictLocalNetworkAccess {
84
expandedDenyList = append(expandedDenyList, networkpolicy.DefaultIPv4DenylistRanges...)
85
expandedDenyList = append(expandedDenyList, networkpolicy.DefaultIPv6DenylistRanges...)
86
}
87
npOptions := &networkpolicy.Options{
88
DenyList: expandedDenyList,
89
}
90
opts.WithNetworkPolicyOptions = npOptions
91
92
switch {
93
case options.SourceIP != "" && options.Interface != "":
94
isAssociated, err := isIpAssociatedWithInterface(options.SourceIP, options.Interface)
95
if err != nil {
96
return err
97
}
98
if isAssociated {
99
opts.Dialer = &net.Dialer{
100
LocalAddr: &net.TCPAddr{
101
IP: net.ParseIP(options.SourceIP),
102
},
103
}
104
} else {
105
return fmt.Errorf("source ip (%s) is not associated with the interface (%s)", options.SourceIP, options.Interface)
106
}
107
case options.SourceIP != "":
108
isAssociated, err := isIpAssociatedWithInterface(options.SourceIP, "any")
109
if err != nil {
110
return err
111
}
112
if isAssociated {
113
opts.Dialer = &net.Dialer{
114
LocalAddr: &net.TCPAddr{
115
IP: net.ParseIP(options.SourceIP),
116
},
117
}
118
} else {
119
return fmt.Errorf("source ip (%s) is not associated with any network interface", options.SourceIP)
120
}
121
case options.Interface != "":
122
ifadrr, err := interfaceAddress(options.Interface)
123
if err != nil {
124
return err
125
}
126
opts.Dialer = &net.Dialer{
127
LocalAddr: &net.TCPAddr{
128
IP: ifadrr,
129
},
130
}
131
}
132
if options.AliveSocksProxy != "" {
133
proxyURL, err := url.Parse(options.AliveSocksProxy)
134
if err != nil {
135
return err
136
}
137
var forward *net.Dialer
138
if opts.Dialer != nil {
139
forward = opts.Dialer
140
} else {
141
forward = &net.Dialer{
142
Timeout: opts.DialerTimeout,
143
KeepAlive: opts.DialerKeepAlive,
144
DualStack: true,
145
}
146
}
147
dialer, err := proxy.FromURL(proxyURL, forward)
148
if err != nil {
149
return err
150
}
151
opts.ProxyDialer = &dialer
152
}
153
154
if options.SystemResolvers {
155
opts.ResolversFile = true
156
opts.EnableFallback = true
157
}
158
if len(options.InternalResolversList) > 0 {
159
opts.BaseResolvers = options.InternalResolversList
160
}
161
162
opts.Deny = append(opts.Deny, expandedDenyList...)
163
164
opts.WithDialerHistory = true
165
opts.SNIName = options.SNI
166
// this instance is used in javascript protocol libraries and
167
// dial history is required to get dialed ip of a host
168
opts.WithDialerHistory = true
169
170
// fastdialer now by default fallbacks to ztls when there are tls related errors
171
dialer, err := fastdialer.NewDialer(opts)
172
if err != nil {
173
return errors.Wrap(err, "could not create dialer")
174
}
175
176
networkPolicy, _ := networkpolicy.New(*npOptions)
177
178
httpClientPool := mapsutil.NewSyncLockMap(
179
// evicts inactive httpclientpool entries after 24 hours
180
// of inactivity (long running instances)
181
mapsutil.WithEviction[string, *retryablehttp.Client](24*time.Hour, 12*time.Hour),
182
)
183
184
dialersInstance := &Dialers{
185
Fastdialer: dialer,
186
NetworkPolicy: networkPolicy,
187
HTTPClientPool: httpClientPool,
188
LocalFileAccessAllowed: options.AllowLocalFileAccess,
189
}
190
191
_ = dialers.Set(options.ExecutionId, dialersInstance)
192
193
// Set a custom dialer for the "nucleitcp" protocol. This is just plain TCP, but it's registered
194
// with a different name so that we do not clobber the "tcp" dialer in the event that nuclei is
195
// being included as a package in another application.
196
mysql.RegisterDialContext("nucleitcp", func(ctx context.Context, addr string) (net.Conn, error) {
197
// Because we're not using the default TCP workflow, quietly add the default port
198
// number if no port number was specified.
199
if _, _, err := net.SplitHostPort(addr); err != nil {
200
addr += ":3306"
201
}
202
203
var executionId string
204
if val := ctx.Value("executionId"); val != nil {
205
executionId = val.(string)
206
}
207
dialer := GetDialersWithId(executionId)
208
if dialer == nil {
209
return nil, fmt.Errorf("dialers not initialized for %s", executionId)
210
}
211
return dialer.Fastdialer.Dial(ctx, "tcp", addr)
212
})
213
214
StartActiveMemGuardian(context.Background())
215
216
SetLfaAllowed(options)
217
218
return nil
219
}
220
221
// isIpAssociatedWithInterface checks if the given IP is associated with the given interface.
222
func isIpAssociatedWithInterface(sourceIP, interfaceName string) (bool, error) {
223
addrs, err := interfaceAddresses(interfaceName)
224
if err != nil {
225
return false, err
226
}
227
for _, addr := range addrs {
228
if ipnet, ok := addr.(*net.IPNet); ok {
229
if ipnet.IP.String() == sourceIP {
230
return true, nil
231
}
232
}
233
}
234
return false, nil
235
}
236
237
// interfaceAddress returns the first IPv4 address of the given interface.
238
func interfaceAddress(interfaceName string) (net.IP, error) {
239
addrs, err := interfaceAddresses(interfaceName)
240
if err != nil {
241
return nil, err
242
}
243
var address net.IP
244
for _, addr := range addrs {
245
if ipnet, ok := addr.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
246
if ipnet.IP.To4() != nil {
247
address = ipnet.IP
248
break
249
}
250
}
251
}
252
if address == nil {
253
return nil, fmt.Errorf("no suitable address found for interface: `%s`", interfaceName)
254
}
255
return address, nil
256
}
257
258
// interfaceAddresses returns all interface addresses.
259
func interfaceAddresses(interfaceName string) ([]net.Addr, error) {
260
if interfaceName == "any" {
261
return net.InterfaceAddrs()
262
}
263
ief, err := net.InterfaceByName(interfaceName)
264
if err != nil {
265
return nil, errors.Wrapf(err, "failed to get interface: `%s`", interfaceName)
266
}
267
addrs, err := ief.Addrs()
268
if err != nil {
269
return nil, errors.Wrapf(err, "failed to get interface addresses for: `%s`", interfaceName)
270
}
271
return addrs, nil
272
}
273
274
// Close closes the global shared fastdialer
275
func Close(executionId string) {
276
dialersInstance, ok := dialers.Get(executionId)
277
if !ok {
278
return
279
}
280
281
if dialersInstance != nil {
282
dialersInstance.Fastdialer.Close()
283
}
284
285
dialers.Delete(executionId)
286
287
if dialers.IsEmpty() {
288
StopActiveMemGuardian()
289
}
290
}
291
292