├── README.txt ├── smb_scanner.exe └── smb_scanner.go /README.txt: -------------------------------------------------------------------------------- 1 | PS C:\Users\user\Desktop> .\scanner.exe -h 2 | Usage of C:\Users\user\Desktop\scanner.exe: 3 | -file string 4 | File with list of targets to scan. Each address or netmask on new line. 5 | -ip string 6 | IP address 7 | -net string 8 | IP network address. Example: 10.0.1.0/24 9 | -out string 10 | Output file with results of scan in CSV format. Example: results.csv 11 | -verbose 12 | Verbose output 13 | -workers int 14 | Count of concurrent workers. (default 200) 15 | 16 | -------------------------------------------------------------------------------- /smb_scanner.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Gh057Pr1nc3/smb_scanner/468af13e8240be38a2917759fa3f7edfd9a947f1/smb_scanner.exe -------------------------------------------------------------------------------- /smb_scanner.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "encoding/binary" 6 | "encoding/hex" 7 | "flag" 8 | "fmt" 9 | "log" 10 | "net" 11 | "os" 12 | "strings" 13 | "sync" 14 | "time" 15 | ) 16 | 17 | var ( 18 | negotiateProtocolRequest, _ = hex.DecodeString(strings.ReplaceAll("zzzzzz85ff534d4272zzzzzzzz1853czzzzzzzzzzzzzzzzzzzzzzzzzzzzzfffezzzz4zzzzz62zzz25z432z4e4554574f524b2z5z524f4752414d2z312e3zzzz24c414e4d414e312e3zzzz257696e646f77732z666f722z576f726b67726f757z732z332e3161zzz24c4d312e32583z3z32zzz24c414e4d414e322e31zzz24e542z4c4d2z3z2e3132zz","z","0")) 19 | sessionSetupRequest, _ = hex.DecodeString(strings.ReplaceAll("zzzzzz88ff534d4273zzzzzzzz18z7czzzzzzzzzzzzzzzzzzzzzzzzzzzzzfffezzzz4zzzzdffzz88zzz411zazzzzzzzzzzzzzzz1zzzzzzzzzzzzzzd4zzzzzz4bzzzzzzzzzzzz57zz69zz6ezz64zz6fzz77zz73zz2zzz32zz3zzz3zzz3zzz2zzz32zz31zz39zz35zzzzzz57zz69zz6ezz64zz6fzz77zz73zz2zzz32zz3zzz3zzz3zzz2zzz35zz2ezz3zzzzzzz","z","0")) 20 | treeConnectRequest, _ = hex.DecodeString(strings.ReplaceAll("zzzzzz6zff534d4275zzzzzzzz18z7czzzzzzzzzzzzzzzzzzzzzzzzzzzzzfffezzz84zzzz4ffzz6zzzz8zzz1zz35zzzz5czz5czz31zz39zz32zz2ezz31zz36zz38zz2ezz31zz37zz35zz2ezz31zz32zz38zz5czz49zz5zzz43zz24zzzzzz3f3f3f3f3fzz","z","0")) 21 | transNamedPipeRequest, _ = hex.DecodeString(strings.ReplaceAll("zzzzzz4aff534d4225zzzzzzzz18z128zzzzzzzzzzzzzzzzzzzzzzzzzzz88ea3z1z852981zzzzzzzzzffffffffzzzzzzzzzzzzzzzzzzzzzzzz4azzzzzz4azzz2zz23zzzzzzz7zz5c5z495z455czz","z","0")) 22 | trans2SessionSetupRequest, _ = hex.DecodeString(strings.ReplaceAll("zzzzzz4eff534d4232zzzzzzzz18z7czzzzzzzzzzzzzzzzzzzzzzzzzzzz8fffezzz841zzzfzczzzzzzz1zzzzzzzzzzzzzza6d9a4zzzzzzzczz42zzzzzz4ezzz1zzzezzzdzzzzzzzzzzzzzzzzzzzzzzzzzzzz","z","0")) 23 | ) 24 | 25 | type ScanStatus string 26 | 27 | const ( 28 | statusUnknown = ScanStatus("?") 29 | statusVulnerable = ScanStatus("+") 30 | statusBackdored = ScanStatus("!") 31 | ) 32 | 33 | type Target struct { 34 | IP string 35 | Netmask string 36 | } 37 | 38 | type Result struct { 39 | Netmask string 40 | IP string 41 | Text string 42 | Error error 43 | Status ScanStatus 44 | } 45 | 46 | func scanHost(t *Target) *Result { 47 | 48 | res := &Result{IP: t.IP, Netmask: t.Netmask} 49 | 50 | timeout := time.Second * 5 51 | conn, err := net.DialTimeout("tcp", t.IP+":445", timeout) 52 | if err != nil { 53 | res.Error = err 54 | return res 55 | } 56 | 57 | conn.SetDeadline(time.Now().Add(time.Second * 10)) 58 | conn.Write(negotiateProtocolRequest) 59 | reply := make([]byte, 1024) 60 | if n, err := conn.Read(reply); err != nil || n < 36 { 61 | res.Error = err 62 | return res 63 | } 64 | 65 | if binary.LittleEndian.Uint32(reply[9:13]) != 0 { 66 | res.Error = err 67 | return res 68 | } 69 | 70 | conn.Write(sessionSetupRequest) 71 | 72 | n, err := conn.Read(reply) 73 | if err != nil || n < 36 { 74 | res.Error = err 75 | return res 76 | } 77 | 78 | if binary.LittleEndian.Uint32(reply[9:13]) != 0 { 79 | res.Status = statusUnknown 80 | res.Text = fmt.Sprintf("Can't authorize to SMB. Imposible to check is host vulnerable or not.") 81 | res.Error = err 82 | return res 83 | } 84 | 85 | var os string 86 | sessionSetupResponse := reply[36:n] 87 | if wordCount := sessionSetupResponse[0]; wordCount != 0 { 88 | byteCount := binary.LittleEndian.Uint16(sessionSetupResponse[7:9]) 89 | if n != int(byteCount)+45 { 90 | fmt.Println("invalid session setup AndX response") 91 | } else { 92 | for i := 10; i < len(sessionSetupResponse)-1; i++ { 93 | if sessionSetupResponse[i] == 0 && sessionSetupResponse[i+1] == 0 { 94 | os = string(sessionSetupResponse[10:i]) 95 | break 96 | } 97 | } 98 | } 99 | 100 | } 101 | userID := reply[32:34] 102 | treeConnectRequest[32] = userID[0] 103 | treeConnectRequest[33] = userID[1] 104 | conn.Write(treeConnectRequest) 105 | 106 | if n, err := conn.Read(reply); err != nil || n < 36 { 107 | res.Error = err 108 | return res 109 | } 110 | 111 | treeID := reply[28:30] 112 | transNamedPipeRequest[28] = treeID[0] 113 | transNamedPipeRequest[29] = treeID[1] 114 | transNamedPipeRequest[32] = userID[0] 115 | transNamedPipeRequest[33] = userID[1] 116 | 117 | conn.Write(transNamedPipeRequest) 118 | if n, err := conn.Read(reply); err != nil || n < 36 { 119 | res.Error = err 120 | return res 121 | } 122 | 123 | if reply[9] == 0x05 && reply[10] == 0x02 && reply[11] == 0x00 && reply[12] == 0xc0 { 124 | res.Status = statusVulnerable 125 | res.Text = fmt.Sprintf("Seems vulnerable for MS17-010. Operation System: %s.", strings.Replace(os, "\x00", "", -1)) 126 | 127 | trans2SessionSetupRequest[28] = treeID[0] 128 | trans2SessionSetupRequest[29] = treeID[1] 129 | trans2SessionSetupRequest[32] = userID[0] 130 | trans2SessionSetupRequest[33] = userID[1] 131 | 132 | conn.Write(trans2SessionSetupRequest) 133 | 134 | if n, err := conn.Read(reply); err != nil || n < 36 { 135 | res.Error = err 136 | return res 137 | } 138 | 139 | if reply[34] == 0x51 { 140 | res.Status = statusBackdored 141 | res.Text += fmt.Sprintf(" Seems to be infected by DoublePulsar.") 142 | } 143 | } 144 | return res 145 | } 146 | 147 | func incIP(ip net.IP) { 148 | for j := len(ip) - 1; j >= 0; j-- { 149 | ip[j]++ 150 | if ip[j] > 0 { 151 | break 152 | } 153 | } 154 | } 155 | 156 | // func scanNetCIDR(netCIDR string) error { 157 | // ip, ipNet, err := net.ParseCIDR(netCIDR) 158 | // if err != nil { 159 | // return err 160 | // } 161 | // var wg sync.WaitGroup 162 | // for ip := ip.Mask(ipNet.Mask); ipNet.Contains(ip); incIP(ip) { 163 | // wg.Add(1) 164 | // go func(ip string) { 165 | // defer wg.Done() 166 | // // scanHost(ip) 167 | // }(ip.String()) 168 | // } 169 | // wg.Wait() 170 | // return nil 171 | // } 172 | 173 | func scanner(targets <-chan *Target, results chan<- *Result, verbose bool, wg *sync.WaitGroup) { 174 | defer wg.Done() 175 | for t := range targets { 176 | if verbose { 177 | fmt.Printf("[] Scanning target: %s\n", t.IP) 178 | } 179 | results <- scanHost(t) 180 | } 181 | } 182 | 183 | func reporter(results <-chan *Result, csv *os.File, verbose bool, wg *sync.WaitGroup) { 184 | defer wg.Done() 185 | for r := range results { 186 | if r.Text != "" { 187 | fmt.Printf("[%s] %s. %s\n", r.Status, r.IP, r.Text) 188 | csv.Write([]byte(r.Netmask + ";")) 189 | csv.Write([]byte(r.IP + ";")) 190 | csv.Write([]byte(fmt.Sprintf("[%s] %s\n", r.Status, r.Text))) 191 | } 192 | } 193 | } 194 | 195 | func main() { 196 | fmt.Println("EternalBlue scanner tool") 197 | host := flag.String("ip", "", "IP address") 198 | netmask := flag.String("net", "", "IP network address. Example: 10.0.1.0/24") 199 | workers := flag.Int("workers", 200, "Count of concurrent workers.") 200 | verbose := flag.Bool("verbose", false, "Verbose output") 201 | file := flag.String("file", "", "File with list of targets to scan. Each address or netmask on new line.") 202 | out := flag.String("out", "", "Output file with results of scan in CSV format. Example: results.csv") 203 | 204 | flag.Parse() 205 | 206 | targets := make(chan *Target, 100) 207 | results := make(chan *Result, 1) 208 | 209 | var wgWorkers sync.WaitGroup 210 | 211 | wgWorkers.Add(*workers) 212 | for w := 0; w < *workers; w++ { 213 | go scanner(targets, results, *verbose, &wgWorkers) 214 | } 215 | 216 | var csv *os.File 217 | 218 | if *out != "" { 219 | var err error 220 | csv, err = os.Create(*out) 221 | if err != nil { 222 | log.Fatal(err) 223 | } 224 | defer csv.Close() 225 | } 226 | 227 | var wgReporter sync.WaitGroup 228 | wgReporter.Add(1) 229 | go reporter(results, csv, *verbose, &wgReporter) 230 | 231 | if *host != "" { 232 | targets <- &Target{IP: *host} 233 | } 234 | 235 | if *netmask != "" { 236 | ip, ipNet, err := net.ParseCIDR(*netmask) 237 | if err != nil { 238 | log.Fatal(err) 239 | } 240 | 241 | for ip := ip.Mask(ipNet.Mask); ipNet.Contains(ip); incIP(ip) { 242 | targets <- &Target{IP: ip.String(), Netmask: ipNet.String()} 243 | } 244 | } 245 | 246 | if *file != "" { 247 | f, err := os.Open(*file) 248 | if err != nil { 249 | log.Fatal(err) 250 | } 251 | defer f.Close() 252 | 253 | scanner := bufio.NewScanner(f) 254 | for scanner.Scan() { 255 | if net.ParseIP(scanner.Text()) != nil { 256 | targets <- &Target{IP: scanner.Text()} 257 | } else { 258 | ip, ipNet, err := net.ParseCIDR(scanner.Text()) 259 | if err != nil { 260 | log.Fatal(err) 261 | } 262 | for ip := ip.Mask(ipNet.Mask); ipNet.Contains(ip); incIP(ip) { 263 | targets <- &Target{IP: ip.String(), Netmask: ipNet.String()} 264 | } 265 | } 266 | } 267 | } 268 | 269 | close(targets) 270 | wgWorkers.Wait() 271 | 272 | close(results) 273 | wgReporter.Wait() 274 | 275 | } 276 | --------------------------------------------------------------------------------