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