diff --git a/Makefile b/Makefile index 6dd2e73..a991df1 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,8 @@ all: - GOOS=windows GOARCH=386 go build -o binaries/respounder-x86.exe respounder.go - GOOS=windows GOARCH=amd64 go build -o binaries/respounder-x64.exe respounder.go - GOOS=linux GOARCH=386 go build -o binaries/respounder-x86 respounder.go - GOOS=linux GOARCH=amd64 go build -o binaries/respounder-x64 respounder.go - GOOS=darwin GOARCH=386 go build -o binaries/respounder-osx respounder.go + GOOS=windows GOARCH=386 go build -o binaries/respounder-win32.exe respounder.go + GOOS=windows GOARCH=amd64 go build -o binaries/respounder-win64.exe respounder.go + GOOS=linux GOARCH=386 go build -o binaries/respounder-linux32 respounder.go + GOOS=linux GOARCH=amd64 go build -o binaries/respounder-linux64 respounder.go + GOOS=darwin GOARCH=386 go build -o binaries/respounder-osx32 respounder.go GOOS=darwin GOARCH=amd64 go build -o binaries/respounder-osx64 respounder.go + diff --git a/README.md b/README.md index 76d36fd..5fbd307 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,8 @@ ### Latest Releases Respounder is available for 32/64 bit linux, OS X and Windows systems. -Latest versions can be downloaded from the [Release](https://github.com/codeexpress/respounder/releases) tab above. +Latest versions can be downloaded from the +[Release](https://github.com/codeexpress/respounder/releases) tab above. ### Build from source This is a golang project with no dependencies. Assuming you have golang compiler installed, @@ -33,7 +34,7 @@ the following will build the binary from scratch ``` $ git clone https://github.com/codeexpress/respounder $ cd respounder -$ go build respounder +$ go build -o respounder respounder.go ``` ## Usage @@ -58,24 +59,26 @@ $ ./respounder ### Flags ``` -$ ./respounder [-json] [-debug] +$ ./respounder [-json] [-debug] [-hostname testhostname | -rhostname] Flags: -json Prints a JSON to STDOUT if a responder is detected on - network. Other text is sent to STDERR + the network. Other text is sent to STDERR -debug Creates a debug.log file with a trace of the program - -help - Displays this help + -hostname string + Hostname to search for (default "aweirdcomputername") + -rhostname + Searches for a hostname comprised of random string instead + of the default hostname ("aweirdcomputername") ``` - ### Typical usage scenario #### Personal Detect rogue hosts running responder on public Wi-Fi networks -e.g. like Airports, Cafés and avoid joining such networks +e.g. like airports, cafés and avoid joining such networks (especially if you are running windows OS) #### Corporate diff --git a/respounder.go b/respounder.go index 60b665d..e1e7b24 100644 --- a/respounder.go +++ b/respounder.go @@ -1,6 +1,7 @@ package main import ( + "crypto/sha1" "encoding/hex" "encoding/json" "flag" @@ -24,10 +25,17 @@ const ( '-' ` - Version = 1.0 - TimeoutSec = 3 - BcastAddr = "224.0.0.252" - LLMNRPort = 5355 + Version = 1.1 + TimeoutSec = 3 + BcastAddr = "224.0.0.252" + LLMNRPort = 5355 + DefaultHostname = "aweirdcomputername" +) + +const ( + def = 0x00 + newHostname = 0x01 + randHostname = 0x02 ) var ( @@ -40,14 +48,35 @@ var ( // argument flags jsonPtr = flag.Bool("json", false, `Prints a JSON to STDOUT if a responder is detected on - network. Other text is sent to STDERR`) + the network. 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+`")`) + + hostnameType byte ) +func init() { + rand.Seed(time.Now().UnixNano()) +} + func main() { initFlags() + flag.Parse() + + if *hostnamePtr != "aweirdcomputername" { + hostnameType = newHostname + } else if *randHostnamePtr { + hostnameType = randHostname + } else { + hostnameType = def + } fmt.Fprintln(os.Stderr, Banner) @@ -105,16 +134,23 @@ func checkResponderOnInterface(inf net.Interface) map[string]string { // 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 - rand.Seed(time.Now().UnixNano()) - randomTransactionId := fmt.Sprintf("%04x", rand.Intn(65535)) + 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 - // TODO: generate a new computer name evertime instead of the - // hardcoded value 'awierdcomputername' - llmnrRequest := randomTransactionId + - "0000000100000000000012617769657264636f6d70757465726e616d650000010001" + llmnrRequest := randomTransactionID + + "00000001000000000000" + cNameLen + encCName + "0000010001" n, _ := hex.DecodeString(llmnrRequest) remoteAddr := net.UDPAddr{IP: net.ParseIP(BcastAddr), Port: LLMNRPort} @@ -124,6 +160,7 @@ func sendLLMNRProbe(ip net.IP) string { fmt.Println("Couldn't bind to a UDP interface. Bailing out!") logger.Printf("Bind error: %+v\nSource IP: %v\n", err, ip) fmt.Println(err) + logger.Printf("LLMNR request payload was: %x\n", llmnrRequest) } defer conn.Close() @@ -134,6 +171,7 @@ func sendLLMNRProbe(ip net.IP) string { 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 { @@ -142,6 +180,18 @@ func sendLLMNRProbe(ip net.IP) string { return responderIP } +// Calculate random hostname by taking random lenght +// 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 { @@ -159,7 +209,7 @@ func getValidIPv4Addr(addrs []net.Addr) net.IP { func initFlags() { flag.Usage = func() { fmt.Fprintf(os.Stderr, "Respounder version %1.1f\n", Version) - fmt.Fprintf(os.Stderr, "Usage: $ respounder [-json] [-debug]") + fmt.Fprintf(os.Stderr, "Usage: $ respounder [-json] [-debug] [-hostname testhostname | -rhostname]") fmt.Fprintf(os.Stderr, "\n\nFlags:\n") flag.PrintDefaults() }