-
Notifications
You must be signed in to change notification settings - Fork 38
/
respounder.go
250 lines (215 loc) · 6.34 KB
/
respounder.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
package main
import (
"crypto/sha1"
"encoding/hex"
"encoding/json"
"flag"
"fmt"
"io/ioutil"
"log"
"math/rand"
"net"
"os"
"strings"
"time"
)
const (
Banner = `
.´/
/ ( .----------------.
[ ]░░░░░░░░░░░|// RESPOUNDER //|
) ( '----------------'
'-'
`
Version = 1.2
TimeoutSec = 3
BcastAddr = "224.0.0.252"
LLMNRPort = 5355
DefaultHostname = "aweirdcomputername"
)
const (
def = 0x00
newHostname = 0x01
randHostname = 0x02
)
var (
// stdout is default output
outFile = os.Stdout
// default logger is set to abyss
logger = log.New(ioutil.Discard, "", 0)
// argument flags
jsonPtr = flag.Bool("json", false,
`Prints a JSON to STDOUT if a responder is detected in the subnet.
Other text is sent to STDERR`)
debugPtr = flag.Bool("debug", false,
`Creates a debug.log file with a trace of the program`)
hostnamePtr = flag.String("hostname", DefaultHostname,
`Hostname to search for`)
randHostnamePtr = flag.Bool("rhostname", false,
`Searches for a hostname comprised of random string instead
of the default hostname ("`+DefaultHostname+`")`)
interfacePtr = flag.String("interface", "",
`Interface where responder will be searched (eg. eth0).
Not specifying this flag will search on all interfaces.`)
hostnameType byte
)
func init() {
rand.Seed(time.Now().UnixNano())
}
func main() {
initFlags()
flag.Parse()
if *hostnamePtr != DefaultHostname {
hostnameType = newHostname
} else if *randHostnamePtr {
hostnameType = randHostname
} else {
hostnameType = def
}
fmt.Fprintln(os.Stderr, Banner)
interfaces, _ := net.Interfaces()
logger.Println("======== Starting RESPOUNDER ========")
logger.Printf("List of all interfaces: \n %+v\n", interfaces)
var resultMap []map[string]string
// send probe on specific interface if -interface flag is set
if *interfacePtr != "" {
inf, err := net.InterfaceByName(*interfacePtr)
if err != nil {
fmt.Printf("Invalid interface '%s'. List of valid interfaces are:\n", *interfacePtr)
for _, inf := range interfaces {
fmt.Println("- " + inf.Name)
}
return
}
detailsMap := checkResponderOnInterface(*inf)
if len(detailsMap) > 0 {
resultMap = append(resultMap, detailsMap)
}
} else { // send probes from all interfaces if -interface flag isn't set
for _, inf := range interfaces {
detailsMap := checkResponderOnInterface(inf)
if len(detailsMap) > 0 {
resultMap = append(resultMap, detailsMap)
}
}
}
if *debugPtr {
fmt.Fprintln(os.Stderr, "Debug file 'debug.log' created.")
}
if *jsonPtr {
resultJSON, _ := json.Marshal(resultMap)
fmt.Println(string(resultJSON))
}
logger.Println("======== Ending RESPOUNDER Session ========")
}
// Test presence of responder on a given interface
func checkResponderOnInterface(inf net.Interface) map[string]string {
var json map[string]string
addrs, _ := inf.Addrs()
logger.Printf("List of all addresses on interface [%s]: %+v\n",
inf.Name, addrs)
ip := getValidIPv4Addr(addrs)
logger.Printf("Bind IP address for interface %+v is %+v\n",
inf.Name, ip)
if ip != nil {
fmt.Fprintf(outFile, "%-10s Sending probe from %s...\t",
"["+inf.Name+"]", ip)
responderIP := sendLLMNRProbe(ip)
if responderIP != "" {
fmt.Fprintf(outFile, "responder detected at %s\n", responderIP)
json = map[string]string{
"interface": inf.Name,
"sourceIP": ip.String(),
"responderIP": responderIP,
}
} else {
fmt.Fprintln(outFile, "responder not detected")
}
}
return json
}
// Creates and sends a LLMNR request to the UDP multicast address.
func sendLLMNRProbe(ip net.IP) string {
var cName string
responderIP := ""
// 2 byte random transaction id eg. 0x8e53
randomTransactionID := fmt.Sprintf("%04x", rand.Intn(65535))
switch hostnameType {
case def, newHostname:
cName = string(*hostnamePtr)
case randHostname:
cName = randomHostname()
}
cNameLen := fmt.Sprintf("%02x", len(cName))
encCName := hex.EncodeToString([]byte(cName))
// LLMNR request in raw bytes
llmnrRequest := randomTransactionID +
"00000001000000000000" + cNameLen + encCName + "0000010001"
n, _ := hex.DecodeString(llmnrRequest)
remoteAddr := net.UDPAddr{IP: net.ParseIP(BcastAddr), Port: LLMNRPort}
conn, err := net.ListenUDP("udp", &net.UDPAddr{IP: ip})
if err != nil {
fmt.Printf("Could not bind to the interface. Is it disabled? ")
logger.Printf("Bind error: %+v\nSource IP: %v\n", err, ip)
return responderIP // return with IP = ''
}
defer conn.Close()
_, _ = conn.WriteToUDP(n, &remoteAddr)
conn.SetReadDeadline(time.Now().Add(TimeoutSec * time.Second))
buffer := make([]byte, 1024)
bytes, clientIP, err := conn.ReadFromUDP(buffer)
if err == nil { // no timeout (or any other) error
responderIP = strings.Split(clientIP.String(), ":")[0]
logger.Printf("LLMNR request payload was: %x\n", n)
logger.Printf("Data received on %s from responder IP %s: %x\n",
ip, clientIP, buffer[:bytes])
} else {
logger.Printf("Error getting response: %s\n", err)
}
return responderIP
}
// Calculate random hostname by taking random length
// of the SHA1 of current time.
func randomHostname() string {
currentTime := time.Now().Format("2006-01-02 15:04:05")
h := sha1.New()
h.Write([]byte(currentTime))
bs := h.Sum(nil)
randomSlice := bs[:(rand.Intn(len(bs)-3) + 3)]
randomName := fmt.Sprintf("%x\n", randomSlice)
return randomName
}
// From all the IP addresses of this interface,
// extract the IPv4 address where we'll bind to
func getValidIPv4Addr(addrs []net.Addr) net.IP {
var ip net.IP
for _, addr := range addrs { // amongst all addrs,
ip = addr.(*net.IPNet).IP.To4() // pick the IPv4 addr
if ip != nil && ip.String() != "127.0.0.1" {
break
}
}
return ip
}
// parses cmd line flag and set appropriate variables
func initFlags() {
flag.Usage = func() {
fmt.Fprintf(os.Stderr, "Respounder version %1.1f\n", Version)
fmt.Fprintf(os.Stderr, "Usage: $ respounder [-json] [-debug] [-interface <iface>] [-hostname <name> | -rhostname]")
fmt.Fprintf(os.Stderr, "\n\nFlags:\n")
flag.PrintDefaults()
}
flag.Parse()
if *jsonPtr {
outFile = os.Stderr
}
if *debugPtr {
f, err := os.OpenFile("debug.log",
os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
if err != nil {
panic(err)
}
logger = log.New(f, "", 0)
logger.SetPrefix("[" + time.Now().Format("02-Jan-2006 15:04:05 MST") + "]: ")
}
}