Codebase list nextnet / 676386f
New upstream version 0.0.2 Sophie Brun 4 years ago
9 changed file(s) with 953 addition(s) and 0 deletion(s). Raw diff Collapse all Expand all
0 # Binaries for programs and plugins
1 *.exe
2 *.dll
3 *.so
4 *.dylib
5
6 # Test binary, build with `go test -c`
7 *.test
8
9 # Output of the go coverage tool, specifically when used with LiteIDE
10 *.out
11
12 # Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736
13 .glide/
0 # .goreleaser.yml
1 # Build customization
2 builds:
3 - binary: nextnet
4 goos:
5 - linux
6 - windows
7 - darwin
8 - freebsd
9 goarch:
10 - amd64
11 - 386
12 - mips
13 - mipsle
14 - mips64
15 - mips64le
16 - arm
17 - arm64
18 - ppc
19 - ppc64
0 BSD 2-Clause License
1
2 Copyright (c) 2018, HD Moore
3 All rights reserved.
4
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions are met:
7
8 * Redistributions of source code must retain the above copyright notice, this
9 list of conditions and the following disclaimer.
10
11 * Redistributions in binary form must reproduce the above copyright notice,
12 this list of conditions and the following disclaimer in the documentation
13 and/or other materials provided with the distribution.
14
15 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
19 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
21 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
22 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
23 OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
0 ALL:
1 @go get github.com/mitchellh/gox && \
2 go get -u ./... && \
3 go fmt ./... && \
4 go vet ./... && \
5 go build ./... && \
6 go install ./...
7
8
9 .PHONY: ALL
0 nextnet
1 ===
2
3 [![GoDoc](https://godoc.org/github.com/hdm/nextnet?status.svg)](https://godoc.org/github.com/hdm/nextnet)
4
5 nextnet is a pivot point discovery tool written in Go.
6
7 ## Download
8
9 Binary packages are available [here](https://github.com/hdm/nextnet/releases/latest).
10
11 ## Install
12
13 Most folks should download a compiled binary from the releases page. If you have Go installed, you can build your own binary by running:
14 ```
15 $ go get github.com/hdm/nextnet
16 ```
17
18 ## Usage
19
20 ```
21 Usage: ./nextnet [options] <cidr or ip address> ... <cidr or ip address>
22
23 Probes a list of networks for potential pivot points.
24
25 Options:
26
27 -h, --help Show Usage and exit.
28
29 -rate int
30 Set the maximum packets per second rate (default 1000)
31
32 -version
33 Show the application version
34 ```
35
36 ### Local Network Discovery
37 Identify a multi-homed Windows desktop on the local network.
38
39 ```
40 $ nextnet 192.168.0.0/24
41
42 {"host":"192.168.0.112","port":"137","proto":"udp","probe":"netbios","name":"DESKTOP-H14GTIO","nets":["192.168.10.12","192.168.20.12"],"info":{"domain":"WORKGROUP","hwaddr":"14:dd:a9:e4:10:a0"}}
43
44 ```
45
46 ### Fast External Network Scans
47 Quickly identify multi-homed hosts running Netbios on the internet
48
49 ```
50 $ nextnet -rate 10000 114.80.0.0/16 | grep nets
51
52 {"host":"114.80.62.194","port":"137","proto":"udp","probe":"netbios","name":"WIN-6F47E00F5JS","nets":["192.168.80.2","192.168.90.8"],"info":{"domain":"WORKGROUP","hwaddr":"b8:2a:72:d6:e6:b7"}}
53 {"host":"114.80.60.40","port":"137","proto":"udp","probe":"netbios","name":"ESX140-2008G","nets":["192.168.11.40","114.80.60.40"],"info":{"domain":"WORKGROUP","hwaddr":"00:0c:29:03:df:5a"}}
54 {"host":"114.80.86.222","port":"137","proto":"udp","probe":"netbios","name":"SHOFFICE-ISA","nets":["114.80.86.222"],"info":{"domain":"\u0001\u0002__MSBROWSE__\u0002","hwaddr":"00:0c:29:5f:ba:d1"}}
55 {"host":"114.80.153.49","port":"137","proto":"udp","probe":"netbios","name":"WIN-0GRAGSOGFGS","nets":["114.80.153.49","172.16.0.157"],"info":{"domain":"WORKGROUP","hwaddr":"90:b1:1c:40:72:31"}}
56 {"host":"114.80.156.143","port":"137","proto":"udp","probe":"netbios","name":"WIN-E1GEEJQBT45","nets":["114.80.156.143","192.168.60.1"],"info":{"domain":"WORKGROUP","hwaddr":"b0:83:fe:e9:3b:d0"}}
57 {"host":"114.80.157.110","port":"137","proto":"udp","probe":"netbios","name":"SHWGQ-DBWEB","nets":["112.65.248.9"],"info":{"domain":"WORKGROUP","hwaddr":"00:26:55:1e:8c:04"}}
58 {"host":"114.80.157.108","port":"137","proto":"udp","probe":"netbios","name":"DATA-FCMB","nets":["112.65.248.8"],"info":{"domain":"WORKGROUP","hwaddr":"00:1f:29:64:e5:f4"}}
59 {"host":"114.80.157.170","port":"137","proto":"udp","probe":"netbios","name":"DZH-TRS6","nets":["10.10.2.13"],"info":{"domain":"WORKGROUP","hwaddr":"ac:16:2d:7a:ff:f0"}}
60 {"host":"114.80.157.44","port":"137","proto":"udp","probe":"netbios","name":"WINAD2","nets":["169.254.35.57","114.80.157.44"],"info":{"domain":"WINAD02","hwaddr":"00:50:56:9d:4f:e1"}}
61 {"host":"114.80.156.99","port":"137","proto":"udp","probe":"netbios","name":"WUHAN","nets":["10.1.1.2","169.254.95.120"],"info":{"domain":"WORKGROUP","hwaddr":"34:40:b5:9e:cf:28"}}
62 {"host":"114.80.166.28","port":"137","proto":"udp","probe":"netbios","name":"KEDE-MOBILE-131","nets":["114.80.166.28"],"info":{"domain":"WORKGROUP","hwaddr":"00:50:56:94:54:5c"}}
63 {"host":"114.80.167.219","port":"137","proto":"udp","probe":"netbios","name":"WIN-DB2BC7UU0CM","nets":["192.168.100.191"],"info":{"domain":"WORKGROUP","hwaddr":"00:50:56:94:49:7c"}}
64 {"host":"114.80.157.135","port":"137","proto":"udp","probe":"netbios","name":"WIN-L3DFEEB","nets":["169.254.54.216"],"info":{"domain":"WORKGROUP","hwaddr":"90:b1:1c:09:61:cc"}}
65 {"host":"114.80.207.27","port":"137","proto":"udp","probe":"netbios","name":"R420","nets":["192.168.126.1","192.168.72.1","169.254.73.205"],"info":{"domain":"WORKGROUP","hwaddr":"44:a8:42:3f:8a:23"}}
66 {"host":"114.80.215.81","port":"137","proto":"udp","probe":"netbios","name":"WINDOWS-7III9JS","nets":["114.80.215.81","192.168.108.1","192.168.233.1"],"info":{"domain":"WORKGROUP","hwaddr":"6c:ae:8b:38:51:f3"}}
67 {"host":"114.80.215.90","port":"137","proto":"udp","probe":"netbios","name":"HYDROGEN","nets":["114.80.215.90","2.0.1.1","1.1.1.1","192.168.118.1"],"info":{"domain":"WORKGROUP","hwaddr":"e4:1f:13:95:ee:c2"}}
68 {"host":"114.80.222.219","port":"137","proto":"udp","probe":"netbios","name":"WIN-VINFEGJ7HP8","nets":["114.80.222.219"],"info":{"domain":"WORKGROUP","hwaddr":"14:18:77:41:17:25"}}
69 {"host":"114.80.245.193","port":"137","proto":"udp","probe":"netbios","name":"WINDOWS-OT2WS9T","nets":["114.80.245.193"],"info":{"domain":"WORKGROUP","hwaddr":"a0:d3:c1:f2:3e:26"}}
70 ```
+212
-0
ip.go less more
0 package main
1
2 import (
3 "encoding/binary"
4 "errors"
5 "fmt"
6 "math"
7 "net"
8 "os"
9 "regexp"
10 "strings"
11 )
12
13 var Match_IPv6 = regexp.MustCompile(`^((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?$`)
14
15 var Match_IPv4 = regexp.MustCompile(`^(?:(?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2}))$`)
16
17 var IPv4_Masks = map[uint32]uint32{
18 1: 32,
19 2: 31,
20 4: 30,
21 8: 29,
22 16: 28,
23 32: 27,
24 64: 26,
25 128: 25,
26 256: 24,
27 512: 23,
28 1024: 22,
29 2048: 21,
30 4096: 20,
31 8192: 19,
32 16384: 18,
33 32768: 17,
34 65536: 16,
35 131072: 15,
36 262144: 14,
37 524288: 13,
38 1048576: 12,
39 2097152: 11,
40 4194304: 10,
41 8388608: 9,
42 16777216: 8,
43 33554432: 7,
44 67108864: 6,
45 134217728: 5,
46 268435456: 4,
47 536870912: 3,
48 1073741824: 2,
49 2147483648: 1,
50 }
51
52 var IPv4_Mask_Sizes = []uint32{
53 2147483648,
54 1073741824,
55 536870912,
56 268435456,
57 134217728,
58 67108864,
59 33554432,
60 16777216,
61 8388608,
62 4194304,
63 2097152,
64 1048576,
65 524288,
66 262144,
67 131072,
68 65536,
69 32768,
70 16384,
71 8192,
72 4096,
73 2048,
74 1024,
75 512,
76 256,
77 128,
78 64,
79 32,
80 16,
81 8,
82 4,
83 2,
84 1,
85 }
86
87 func IPv4_to_UInt(ips string) (uint32, error) {
88 ip := net.ParseIP(ips)
89 if ip == nil {
90 return 0, errors.New("Invalid IPv4 address")
91 }
92 ip = ip.To4()
93 return binary.BigEndian.Uint32(ip), nil
94 }
95
96 func UInt_to_IPv4(ipi uint32) string {
97 ipb := make([]byte, 4)
98 binary.BigEndian.PutUint32(ipb, ipi)
99 ip := net.IP(ipb)
100 return ip.String()
101 }
102
103 func IPv4Range2CIDRs(s_ip string, e_ip string) ([]string, error) {
104
105 s_i, s_e := IPv4_to_UInt(s_ip)
106 if s_e != nil {
107 return []string{}, s_e
108 }
109
110 e_i, e_e := IPv4_to_UInt(e_ip)
111 if e_e != nil {
112 return []string{}, e_e
113 }
114
115 if s_i > e_i {
116 return []string{}, errors.New("Start address is bigger than end address")
117 }
118
119 return IPv4UIntRange2CIDRs(s_i, e_i), nil
120 }
121
122 func IPv4UIntRange2CIDRs(s_i uint32, e_i uint32) []string {
123 cidrs := []string{}
124
125 // Ranges are inclusive
126 size := e_i - s_i + 1
127
128 if size == 0 {
129 return cidrs
130 }
131
132 for i := range IPv4_Mask_Sizes {
133
134 mask_size := IPv4_Mask_Sizes[i]
135
136 if mask_size > size {
137 continue
138 }
139
140 // Exact match of the block size
141 if mask_size == size {
142 cidrs = append(cidrs, fmt.Sprintf("%s/%d", UInt_to_IPv4(s_i), IPv4_Masks[mask_size]))
143 break
144 }
145
146 // Chop off the biggest block that fits
147 cidrs = append(cidrs, fmt.Sprintf("%s/%d", UInt_to_IPv4(s_i), IPv4_Masks[mask_size]))
148 s_i = s_i + mask_size
149
150 // Look for additional blocks
151 new_cidrs := IPv4UIntRange2CIDRs(s_i, e_i)
152
153 // Merge those blocks into out results
154 for x := range new_cidrs {
155 cidrs = append(cidrs, new_cidrs[x])
156 }
157 break
158
159 }
160 return cidrs
161 }
162
163 func AddressesFromCIDR(cidr string, o chan<- string) {
164 if len(cidr) == 0 {
165 return
166 }
167
168 // We may receive bare IP addresses, not CIDRs sometimes
169 if !strings.Contains(cidr, "/") {
170 if strings.Contains(cidr, ":") {
171 cidr = cidr + "/128"
172 } else {
173 cidr = cidr + "/32"
174 }
175 }
176
177 // Parse CIDR into base address + mask
178 ip, net, err := net.ParseCIDR(cidr)
179 if err != nil {
180 fmt.Fprintf(os.Stderr, "Invalid CIDR %s: %s\n", cidr, err.Error())
181 return
182 }
183
184 // Verify IPv4 for now
185 ip4 := net.IP.To4()
186 if ip4 == nil {
187 fmt.Fprintf(os.Stderr, "Invalid IPv4 CIDR %s\n", cidr)
188 return
189 }
190
191 net_base, err := IPv4_to_UInt(net.IP.String())
192 if err != nil {
193 fmt.Fprintf(os.Stderr, "Invalid IPv4 Address %s: %s\n", ip.String(), err.Error())
194 return
195 }
196
197 mask_ones, mask_total := net.Mask.Size()
198
199 // Does not work for IPv6 due to cast to uint32
200 net_size := uint32(math.Pow(2, float64(mask_total-mask_ones)))
201
202 cur_base := net_base
203 end_base := net_base + net_size
204 cur_addr := cur_base
205
206 for cur_addr = cur_base; cur_addr < end_base; cur_addr++ {
207 o <- UInt_to_IPv4(cur_addr)
208 }
209
210 return
211 }
0 package main
1
2 import (
3 "encoding/json"
4 "flag"
5 "fmt"
6 "golang.org/x/time/rate"
7 "os"
8 "runtime"
9 "sync"
10 "time"
11 )
12
13 type ScanResult struct {
14 Host string `json:"host"`
15 Port string `json:"port,omitempty"`
16 Proto string `json:"proto,omitempty"`
17 Probe string `json:"probe,omitempty"`
18 Name string `json:"name,omitempty"`
19 Nets []string `json:"nets,omitempty"`
20 Info map[string]string `json:"info"`
21 }
22
23 type Prober interface {
24 Setup()
25 Initialize()
26 Wait()
27 AddTarget(string)
28 CloseInput()
29 SetOutput(chan<- ScanResult)
30 CheckRateLimit()
31 SetLimiter(*rate.Limiter)
32 }
33
34 type Probe struct {
35 name string
36 options map[string]string
37 waiter sync.WaitGroup
38 input chan string
39 output chan<- ScanResult
40 limiter *rate.Limiter
41 }
42
43 func (this *Probe) String() string {
44 return fmt.Sprintf("%s", this.name)
45 }
46
47 func (this *Probe) Wait() {
48 this.waiter.Wait()
49 return
50 }
51
52 func (this *Probe) Setup() {
53 this.name = "generic"
54 this.input = make(chan string)
55 return
56 }
57
58 func (this *Probe) Initialize() {
59 this.Setup()
60 this.name = "generic"
61 return
62 }
63
64 func (this *Probe) SetOutput(c_out chan<- ScanResult) {
65 this.output = c_out
66 return
67 }
68
69 func (this *Probe) AddTarget(t string) {
70 this.input <- t
71 return
72 }
73
74 func (this *Probe) CloseInput() {
75 close(this.input)
76 return
77 }
78
79 func (this *Probe) SetLimiter(limiter *rate.Limiter) {
80 this.limiter = limiter
81 return
82 }
83
84 func (this *Probe) CheckRateLimit() {
85 for this.limiter.Allow() == false {
86 time.Sleep(10 * time.Millisecond)
87 }
88 }
89
90 var limiter *rate.Limiter
91 var ppsrate *int
92 var probes []Prober
93 var wi sync.WaitGroup
94 var wo sync.WaitGroup
95
96 func usage() {
97 fmt.Println("Usage: " + os.Args[0] + " [cidr] ... [cidr]")
98 fmt.Println("")
99 fmt.Println("Probes a list of networks for potential pivot points.")
100 fmt.Println("")
101 fmt.Println("Options:")
102 flag.PrintDefaults()
103 }
104
105 func outputWriter(o <-chan ScanResult) {
106
107 for found := range o {
108 j, err := json.Marshal(found)
109 if err != nil {
110 fmt.Fprintf(os.Stderr, "Error marshaling result: '%v' : %s\n", found, err)
111 continue
112 }
113 os.Stdout.Write(j)
114 os.Stdout.Write([]byte("\n"))
115 }
116 wo.Done()
117 }
118
119 func initializeProbes(c_out chan<- ScanResult) {
120 for _, probe := range probes {
121 probe.Initialize()
122 probe.SetOutput(c_out)
123 probe.SetLimiter(limiter)
124 }
125 }
126
127 func waitProbes() {
128 for _, probe := range probes {
129 probe.Wait()
130 }
131 }
132
133 func processAddress(i <-chan string, o chan<- ScanResult) {
134 for addr := range i {
135 for _, probe := range probes {
136 probe.AddTarget(addr)
137 }
138 }
139
140 for _, probe := range probes {
141 probe.CloseInput()
142 }
143 wi.Done()
144 }
145
146 func main() {
147
148 runtime.GOMAXPROCS(runtime.NumCPU())
149
150 flag.Usage = func() { usage() }
151 version := flag.Bool("version", false, "Show the application version")
152 ppsrate = flag.Int("rate", 1000, "Set the maximum packets per second rate")
153
154 flag.Parse()
155
156 if *version {
157 PrintVersion("nextnet")
158 os.Exit(0)
159 }
160
161 limiter = rate.NewLimiter(rate.Limit(*ppsrate), *ppsrate*3)
162
163 // Input addresses
164 c_addr := make(chan string)
165
166 // Output structs
167 c_out := make(chan ScanResult)
168
169 // Configure the probes
170 initializeProbes(c_out)
171
172 // Launch a single input address processor
173 wi.Add(1)
174 go processAddress(c_addr, c_out)
175
176 // Launch a single output writer
177 wo.Add(1)
178 go outputWriter(c_out)
179
180 // Parse CIDRs and feed IPs to the input channel
181 for _, cidr := range flag.Args() {
182 AddressesFromCIDR(cidr, c_addr)
183 }
184
185 // Close the cidr input channel
186 close(c_addr)
187
188 // Wait for the input feed to complete
189 wi.Wait()
190
191 // Wait for pending probes
192 waitProbes()
193
194 // Close the output handle
195 close(c_out)
196
197 // Wait for the output goroutine
198 wo.Wait()
199 }
0 package main
1
2 import (
3 "bytes"
4 "encoding/binary"
5 "fmt"
6 "log"
7 "math/rand"
8 "net"
9 "strings"
10 "time"
11 )
12
13 const MaxPendingReplies int = 256
14 const MaxProbeResponseTime time.Duration = time.Second * 2
15
16 type NetbiosInfo struct {
17 statusRecv time.Time
18 nameSent time.Time
19 nameRecv time.Time
20 statusReply NetbiosReplyStatus
21 nameReply NetbiosReplyStatus
22 }
23
24 type ProbeNetbios struct {
25 Probe
26 socket net.PacketConn
27 replies map[string]*NetbiosInfo
28 }
29
30 type NetbiosReplyHeader struct {
31 XID uint16
32 Flags uint16
33 QuestionCount uint16
34 AnswerCount uint16
35 AuthCount uint16
36 AdditionalCount uint16
37 QuestionName [34]byte
38 RecordType uint16
39 RecordClass uint16
40 RecordTTL uint32
41 RecordLength uint16
42 }
43
44 type NetbiosReplyName struct {
45 Name [15]byte
46 Type uint8
47 Flag uint16
48 }
49
50 type NetbiosReplyAddress struct {
51 Flag uint16
52 Address [4]uint8
53 }
54
55 type NetbiosReplyStatus struct {
56 Header NetbiosReplyHeader
57 HostName [15]byte
58 UserName [15]byte
59 Names []NetbiosReplyName
60 Addresses []NetbiosReplyAddress
61 HWAddr string
62 }
63
64 func (this *ProbeNetbios) ProcessReplies() {
65 buff := make([]byte, 1500)
66
67 this.replies = make(map[string]*NetbiosInfo)
68
69 for {
70 rlen, raddr, rerr := this.socket.ReadFrom(buff)
71 if rerr != nil {
72 if nerr, ok := rerr.(net.Error); ok && nerr.Timeout() {
73 log.Printf("probe %s receiver timed out: %s", this, rerr)
74 continue
75 }
76
77 // Complain about other error types
78 log.Printf("probe %s receiver returned error: %s", this, rerr)
79 return
80 }
81
82 ip := raddr.(*net.UDPAddr).IP.String()
83
84 reply := this.ParseReply(buff[0 : rlen])
85 if len(reply.Names) == 0 && len(reply.Addresses) == 0 {
86 continue
87 }
88
89 _, found := this.replies[ip]
90 if !found {
91 nbinfo := new(NetbiosInfo)
92 this.replies[ip] = nbinfo
93 }
94
95 // Handle status replies by sending a name request
96 if reply.Header.RecordType == 0x21 {
97 // log.Printf("probe %s received a status reply of %d bytes from %s", this, rlen, raddr)
98 this.replies[ip].statusReply = reply
99 this.replies[ip].statusRecv = time.Now()
100
101 ntime := time.Time{}
102 if this.replies[ip].nameSent == ntime {
103 this.replies[ip].nameSent = time.Now()
104 this.SendNameRequest(ip)
105 }
106 }
107
108 // Handle name replies by reporting the result
109 if reply.Header.RecordType == 0x20 {
110 // log.Printf("probe %s received a name reply of %d bytes from %s", this, rlen, raddr)
111 this.replies[ip].nameReply = reply
112 this.replies[ip].nameRecv = time.Now()
113 this.ReportResult(ip)
114 }
115 }
116 }
117
118 func (this *ProbeNetbios) SendRequest(ip string, req []byte) {
119 addr, aerr := net.ResolveUDPAddr("udp", ip+":137")
120 if aerr != nil {
121 log.Printf("probe %s failed to resolve %s (%s)", this, ip, aerr)
122 return
123 }
124
125 // Retry in case of network buffer congestion
126 wcnt := 0
127 for wcnt = 0; wcnt < 5; wcnt++ {
128
129 this.CheckRateLimit()
130
131 _, werr := this.socket.WriteTo(req, addr)
132 if werr != nil {
133 log.Printf("probe %s [%d/%d] failed to send to %s (%s)", this, wcnt+1, 5, ip, werr)
134 time.Sleep(100 * time.Millisecond)
135 continue
136 }
137 break
138 }
139
140 // Were we able to send it eventually?
141 if wcnt == 5 {
142 log.Printf("probe %s [%d/%d] gave up sending to %s", this, wcnt, 5, ip)
143 }
144 }
145
146 func (this *ProbeNetbios) SendStatusRequest(ip string) {
147 // log.Printf("probe %s is sending a status request to %s", this, ip)
148 this.SendRequest(ip, this.CreateStatusRequest())
149 }
150
151 func (this *ProbeNetbios) SendNameRequest(ip string) {
152 sreply := this.replies[ip].statusReply
153 name := TrimName(string(sreply.HostName[:]))
154 this.SendRequest(ip, this.CreateNameRequest(name))
155 }
156
157 func (this *ProbeNetbios) ResultFromIP(ip string) ScanResult {
158 sreply := this.replies[ip].statusReply
159 nreply := this.replies[ip].nameReply
160
161 res := ScanResult{
162 Host: ip,
163 Port: "137",
164 Proto: "udp",
165 Probe: this.String(),
166 }
167
168 res.Info = make(map[string]string)
169
170 res.Name = TrimName(string(sreply.HostName[:]))
171
172 if nreply.Header.RecordType == 0x20 {
173 for _, ainfo := range nreply.Addresses {
174
175 net := fmt.Sprintf("%d.%d.%d.%d", ainfo.Address[0], ainfo.Address[1], ainfo.Address[2], ainfo.Address[3])
176 if net == "0.0.0.0" {
177 continue
178 }
179
180 res.Nets = append(res.Nets, net)
181 }
182 }
183
184 if sreply.HWAddr != "00:00:00:00:00:00" {
185 res.Info["hwaddr"] = sreply.HWAddr
186 }
187
188 username := TrimName(string(sreply.UserName[:]))
189 if len(username) > 0 && username != res.Name {
190 res.Info["username"] = username
191 }
192
193 for _, rname := range sreply.Names {
194
195 tname := TrimName(string(rname.Name[:]))
196 if tname == res.Name {
197 continue
198 }
199
200 if rname.Flag&0x0800 != 0 {
201 continue
202 }
203
204 res.Info["domain"] = tname
205 }
206
207 return res
208 }
209
210 func (this *ProbeNetbios) ReportResult(ip string) {
211 this.output <- this.ResultFromIP(ip)
212 delete(this.replies, ip)
213 }
214
215 func (this *ProbeNetbios) ReportIncompleteResults() {
216 for ip, _ := range this.replies {
217 this.ReportResult(ip)
218 }
219 }
220
221 func (this *ProbeNetbios) EncodeNetbiosName(name [16]byte) [32]byte {
222 encoded := [32]byte{}
223
224 for i := 0; i < 16; i++ {
225 if name[i] == 0 {
226 encoded[(i*2)+0] = 'C'
227 encoded[(i*2)+1] = 'A'
228 } else {
229 encoded[(i*2)+0] = byte((name[i] / 16) + 0x41)
230 encoded[(i*2)+1] = byte((name[i] % 16) + 0x41)
231 }
232 }
233
234 return encoded
235 }
236
237 func (this *ProbeNetbios) DecodeNetbiosName(name [32]byte) [16]byte {
238 decoded := [16]byte{}
239
240 for i := 0; i < 16; i++ {
241 if name[(i*2)+0] == 'C' && name[(i*2)+1] == 'A' {
242 decoded[i] = 0
243 } else {
244 decoded[i] = ((name[(i*2)+0] * 16) - 0x41) + (name[(i*2)+1] - 0x41)
245 }
246 }
247 return decoded
248 }
249
250 func (this *ProbeNetbios) ParseReply(buff []byte) NetbiosReplyStatus {
251
252 resp := NetbiosReplyStatus{}
253 temp := bytes.NewBuffer(buff)
254
255 binary.Read(temp, binary.BigEndian, &resp.Header)
256
257 if resp.Header.QuestionCount != 0 {
258 return resp
259 }
260
261 if resp.Header.AnswerCount == 0 {
262 return resp
263 }
264
265 // Names
266 if resp.Header.RecordType == 0x21 {
267 var rcnt uint8
268 var ridx uint8
269 binary.Read(temp, binary.BigEndian, &rcnt)
270
271 for ridx = 0; ridx < rcnt; ridx++ {
272 name := NetbiosReplyName{}
273 binary.Read(temp, binary.BigEndian, &name)
274 resp.Names = append(resp.Names, name)
275
276 if name.Type == 0x20 {
277 resp.HostName = name.Name
278 }
279
280 if name.Type == 0x03 {
281 resp.UserName = name.Name
282 }
283 }
284
285 var hwbytes [6]uint8
286 binary.Read(temp, binary.BigEndian, &hwbytes)
287 resp.HWAddr = fmt.Sprintf("%.2x:%.2x:%.2x:%.2x:%.2x:%.2x",
288 hwbytes[0], hwbytes[1], hwbytes[2], hwbytes[3], hwbytes[4], hwbytes[5],
289 )
290 return resp
291 }
292
293 // Addresses
294 if resp.Header.RecordType == 0x20 {
295 var ridx uint16
296 for ridx = 0; ridx < (resp.Header.RecordLength / 6); ridx++ {
297 addr := NetbiosReplyAddress{}
298 binary.Read(temp, binary.BigEndian, &addr)
299 resp.Addresses = append(resp.Addresses, addr)
300 }
301 }
302
303 return resp
304 }
305
306 func (this *ProbeNetbios) CreateStatusRequest() []byte {
307 return []byte{
308 byte(rand.Intn(256)), byte(rand.Intn(256)),
309 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
310 0x00, 0x00, 0x20, 0x43, 0x4b, 0x41, 0x41, 0x41,
311 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
312 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
313 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
314 0x41, 0x41, 0x41, 0x00, 0x00, 0x21, 0x00, 0x01,
315 }
316 }
317
318 func (this *ProbeNetbios) CreateNameRequest(name string) []byte {
319 nbytes := [16]byte{}
320 copy(nbytes[0:15], []byte(strings.ToUpper(name)[:]))
321
322 req := []byte{
323 byte(rand.Intn(256)), byte(rand.Intn(256)),
324 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
325 0x00, 0x00, 0x20,
326 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
327 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
328 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
329 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
330 0x00, 0x00, 0x20, 0x00, 0x01,
331 }
332
333 encoded := this.EncodeNetbiosName(nbytes)
334 copy(req[13:45], encoded[0:32])
335 return req
336 }
337
338 func (this *ProbeNetbios) Initialize() {
339 this.Setup()
340 this.name = "netbios"
341 this.waiter.Add(1)
342
343 // Open socket
344 this.socket, _ = net.ListenPacket("udp", "")
345
346 go func() {
347 go this.ProcessReplies()
348
349 for dip := range this.input {
350 this.SendStatusRequest(dip)
351
352 // If our pending replies gets > MAX, stop, process, report, clear, resume
353 if len(this.replies) > MaxPendingReplies {
354 log.Printf("probe %s is flushing due to maximum replies hit (%d)", this, len(this.replies))
355 time.Sleep(MaxProbeResponseTime)
356 this.ReportIncompleteResults()
357 }
358 }
359
360 // Sleep for packet timeout of initial probe
361 log.Printf("probe %s is waiting for final replies to status probe", this)
362 time.Sleep(MaxProbeResponseTime)
363
364 // The receiver is sending interface probes in response to status probes
365
366 log.Printf("probe %s is waiting for final replies to interface probe", this)
367 time.Sleep(MaxProbeResponseTime)
368
369 // Shut down receiver
370 this.socket.Close()
371
372 // Report any incomplete results (status reply but no name replies)
373 this.ReportIncompleteResults()
374
375 // Complete
376 this.waiter.Done()
377 }()
378
379 return
380 }
381
382 func init() {
383 probes = append(probes, new(ProbeNetbios))
384 }
0 package main
1
2 import (
3 "fmt"
4 "os"
5 "strings"
6 )
7
8 func PrintVersion(app string) {
9 var version = "master"
10 fmt.Fprintf(os.Stderr, "%s v%s\n", app, version)
11 }
12
13 func TrimName(name string) string {
14 return strings.TrimSpace(strings.Replace(name, "\x00", "", -1))
15 }