hikaridatasource 加密后登陆不上_渗透测试 | 突破前端JS加密限制
前言
現在前端開發為了提高爬蟲的難度及加強安全性,都會在數據包提交前進行加密,最典型的就是傳參加密,相信大家在測試的時候都遇到過,那么我們在抓取數據包并修改之后,修改之后的參數無法通過后端程序數據完整性的校驗,就無法進行進一步測試。如果我們逆向解析出加密的過程,就可以模擬出相同的密文,通過后端接口的校驗。
最近由于工作需要,在搜索資料的時候,學到了很多爬蟲大佬們的關于JS逆向、APK逆向、代碼分析等方面的經驗和技巧,后續會分部分記錄并總結下來。
0x01 常見加密算法
比較簡單的base64、hex等這些編碼就不再說了。
1.1 對稱加密
常用算法:DES、DES3、AES
根據密鑰長度不同又分為:AES-128、AES-192、AES-256
其中AES-192和AES-256在Java中使用需獲取無政策限制權限文件
加密/解密使用相同的密鑰
加密和解密的過程是可逆的
1.2 非對稱加密
常用算法:RSA
使用公鑰加密,使用私鑰解密
公鑰是公開的,私鑰保密
加密處理安全,但是性能極差,單次加密長度有限制
RSA既可用于數據交換,也可用于數據校驗
數據校驗通常結合消息摘要算法 MD5withRSA 等
1.3 信息摘要算法/簽名算法
常用算法:MD5、HMAC(HmacMD5、HmacSHA1、HmacSHA256)、SHA(SHA1、SHA256、SHA512)
不管明文多長,散列后的密文定長
明文不一樣,散列后結果一定不一樣
散列后的密文不可逆
一般用于校驗數據完整性、簽名 sign
由于密文不可逆,所以后臺無法還原,也就是說他要驗證,會在后臺以跟前臺一樣的方式去重新簽名一遍。也就是說他會把源數據和簽名后的值一起提交到后臺。所以我們要保證在簽名時候的數據和提交上去的源數據一致,這種算法特喜歡在內部加入時間戳
0x02 JS逆向流程
以登錄為例的基本流程:
如果網頁有跳轉,必須勾選preserve log(F12-Network)防止丟包
看一下有沒有框架,右鍵查看框架源代碼(彈出式登陸界面)
登陸盡量使用錯誤密碼,防止跳轉
查看關鍵登陸包,分析哪些參數是加密的
使用別的瀏覽器分析哪些參數是固定的值
初步猜測加密的方法
搜索
直接搜索參數,比如:pwd=,pwd =,pwd:,pwd :
密碼框地方右鍵 檢查 查看 id name type
找到加密的地方(重點)
進行代碼調試
找出所有的加密代碼
從最后一步開始寫起,缺啥找啥
如果找的是函數的話,search 要帶上 function xxx
如果看到加密的地方有個類,并且之后是用 prototype 把方法加在原生對象上的話,要把 所有加在原生對象上的方法都找出來
函數找多了沒關系,只要不報錯不會影響結果,但是不能找少了
0x03 實例操作
打開網站,抓包
修改個數據,比如修改num為1000
有些杠精該說了:我滲透測試改這有毛用。
是沒用,只是以此為例。。。
可以看到,修改過參數數據后,后端數據校驗之后不合法,所以沒有返回數據。
打開測試網站 -> F12控制臺 -> 切換至 XHR
這里我們看下需要做的有什么?
請求中有token(t明顯是時間戳),token和數據不匹配后端不返回數據
返回的數據是加密的,需要解密
接下來我們就定位具體的加密函數和解密函數。
按照上面提到的流程步驟
打開控制臺 -> source ->搜索
搜索加密參數名 token
根據搜索結果的文件名判斷,基本上就是第二個文件,點擊打開
token的生成代碼
var token = md5(String(page) + String(num) + String(timestamp));設置斷點,刷新
成功進入斷點,沒毛病了
根據加密函數,編寫腳本
可以看到,生成的token和URL中的一致,至此,加密部分完成。
解密部分同樣的道理,搜索返回包中的參數,直接搜索list發現有點多,不太好觀察,還有一種方法
可以看到數據部分html的id為ip-list,再次搜索
成功找到解密數據包的代碼
設置斷點,進一步確認
沒毛病,可以看到decode_str后就開始出現我們需要的明文數據了,所以這里的 decode_str 就是我們要的解密方法。剩下就是分析代碼,編寫解密腳本了。
function decode_str(scHZjLUh1) { scHZjLUh1 = Base64["\x64\x65\x63\x6f\x64\x65"](scHZjLUh1); key = '\x6e\x79\x6c\x6f\x6e\x65\x72'; len = key["\x6c\x65\x6e\x67\x74\x68"]; code = ''; for (i = 0; i < scHZjLUh1["\x6c\x65\x6e\x67\x74\x68"]; i++) { var coeFYlqUm2 = i % len; code += window["\x53\x74\x72\x69\x6e\x67"]["\x66\x72\x6f\x6d\x43\x68\x61\x72\x43\x6f\x64\x65"](scHZjLUh1["\x63\x68\x61\x72\x43\x6f\x64\x65\x41\x74"](i) ^ key["\x63\x68\x61\x72\x43\x6f\x64\x65\x41\x74"](coeFYlqUm2)) } return Base64["\x64\x65\x63\x6f\x64\x65"](code)}先運行下看看
報錯,提示Base64未定義,設置斷點,找到Base64的具體代碼
復制粘貼進代碼,再次運行
艸,提示Windows未定義,根據流程,缺啥補啥,debug,找對應的值
可以看到分別對應的是String和fromCharCode
那就是調用了String.fromCharCode方法了,替換掉,再次運行
bingo~
成功解密獲取到明文數據。
0x04 Python實現加密方法合集
關于上述第一部分的常見加密算法,GitHub有對應的倉庫,直接可以用的
GitHub:https://github.com/dhfjcuff/R-A-M-D-D3-S-M-H/
# -*- coding:utf-8 -*-import base64import rsafrom Crypto.Cipher import AESfrom Crypto.PublicKey import RSAfrom pyDes import des, CBC, PAD_PKCS5from Crypto.Cipher import DES3import hashlibimport hmacclass USE_AES: """ AES 除了MODE_SIV模式key長度為:32, 48, or 64, 其余key長度為16, 24 or 32 詳細見AES內部文檔 CBC模式傳入iv參數 本例使用常用的ECB模式 """ def __init__(self, key): if len(key) > 32: key = key[:32] self.key = self.to_16(key) def to_16(self, key): """ 轉為16倍數的bytes數據 :param key: :return: """ key = bytes(key, encoding="utf8") while len(key) % 16 != 0: key += b'\0' return key # 返回bytes def aes(self): return AES.new(self.key, AES.MODE_ECB) # 初始化加密器 def encrypt(self, text): aes = self.aes() return str(base64.encodebytes(aes.encrypt(self.to_16(text))), encoding='utf8').replace('\n', '') # 加密 def decodebytes(self, text): aes = self.aes() return str(aes.decrypt(base64.decodebytes(bytes( text, encoding='utf8'))).rstrip(b'\0').decode("utf8")) # 解密class USE_RSA: """ 生成密鑰可保存.pem格式文件 1024位的證書,加密時最大支持117個字節,解密時為128; 2048位的證書,加密時最大支持245個字節,解密時為256。 加密大文件時需要先用AES或者DES加密,再用RSA加密密鑰,詳細見文檔 文檔:https://stuvel.eu/files/python-rsa-doc/usage.html#generating-keys """ def __init__(self, number=1024): """ :param number: 公鑰、私鑰 """ self.pubkey, self.privkey = rsa.newkeys(number) def rsaEncrypt(self, text): """ :param test: str :return: bytes """ content = text.encode('utf-8') crypto = rsa.encrypt(content, self.pubkey) return crypto def rsaDecrypt(self, text): """ :param text:bytes :return: str """ content = rsa.decrypt(text, self.privkey) con = content.decode('utf-8') return con def savePem(self, path_name, text): """ :param path_name: 保存路徑 :param text: str :return:bytes """ if "PEM" in path_name.upper(): path_name = path_name[:-4] with open('{}.pem'.format(path_name), 'bw') as f: f.write(text.save_pkcs1()) def readPem(self, path_name, key_type): """ :param path_name: 密鑰文件 :param key_type:類型 :return: """ if 'pubkey' in key_type: self.pubkey = rsa.PublicKey.load_pkcs1(path_name) else: self.privkey = rsa.PublicKey.load_pkcs1(path_name) return True def sign(self, message, priv_key=None, hash_method='SHA-1'): """ 生成明文的哈希簽名以便還原后對照 :param message: str :param priv_key: :param hash_method: 哈希的模式 :return: """ if None == priv_key: priv_key = self.privkey return rsa.sign(message.encode(), priv_key, hash_method) def checkSign(self, mess, result, pubkey=None): """ 驗證簽名:傳入解密后明文、簽名、公鑰,驗證成功返回哈希方法,失敗則報錯 :param mess: str :param result: bytes :param pubkey: :return: str """ if None == pubkey: pubkey = self.privkey try: result = rsa.verify(mess, result, pubkey) return result except: return Falseclass USE_DES: """ des(key,[mode], [IV], [pad], [pad mode]) key:必須正好8字節 mode(模式):ECB、CBC iv:CBC模式中必須提供長8字節 pad:填充字符 padmode:加密填充模式PAD_NORMAL or PAD_PKCS5 """ def __init__(self, key, iv): if not isinstance(key, bytes): key = bytes(key, encoding="utf8") if not isinstance(iv, bytes): iv = bytes(iv, encoding="utf8") self.key = key self.iv = iv def encrypt(self, text): """ DES 加密 :param text: 原始字符串 :return: 加密后字符串,bytes """ if not isinstance(text, bytes): text = bytes(text, "utf-8") secret_key = self.key iv = self.iv k = des(secret_key, CBC, iv, pad=None, padmode=PAD_PKCS5) en = k.encrypt(text, padmode=PAD_PKCS5) return en def descrypt(self, text): """ DES 解密 :param text: 加密后的字符串,bytes :return: 解密后的字符串 """ secret_key = self.key iv = self.iv k = des(secret_key, CBC, iv, pad=None, padmode=PAD_PKCS5) de = k.decrypt(text, padmode=PAD_PKCS5) return de.decode()class USE_DES3: """ new(key, mode, *args, **kwargs) key:必須8bytes倍數介于16-24 mode: iv:初始化向量適用于MODE_CBC、MODE_CFB、MODE_OFB、MODE_OPENPGP,4種模式 ``MODE_CBC``, ``MODE_CFB``, and ``MODE_OFB``長度為8bytes ```MODE_OPENPGP```加密時8bytes解密時10bytes 未提供默認隨機生成 nonce:僅在 ``MODE_EAX`` and ``MODE_CTR``模式中使用 ``MODE_EAX``建議16bytes ``MODE_CTR``建議[0, 7]長度 未提供則隨機生成 segment_size:分段大小,僅在 ``MODE_CFB``模式中使用,長度為8倍數,未指定則默認為8 mac_len:適用``MODE_EAX``模式,身份驗證標記的長度(字節),它不能超過8(默認值) initial_value:適用```MODE_CTR```,計數器的初始值計數器塊。默認為**0**。 """ def __init__(self, key): self.key = key self.mode = DES3.MODE_ECB def encrypt(self, text): """ 傳入明文 :param text:bytes類型,長度是KEY的倍數 :return: """ if not isinstance(text, bytes): text = bytes(text, 'utf-8') x = len(text) % 8 text = text+b'\0'*x cryptor = DES3.new(self.key, self.mode) ciphertext = cryptor.encrypt(text) return ciphertext def decrypt(self, text): cryptor = DES3.new(self.key, self.mode) plain_text = cryptor.decrypt(text) st = str(plain_text.decode("utf-8")).rstrip('\0') return stdef USE_MD5(test): if not isinstance(test, bytes): test = bytes(test, 'utf-8') m = hashlib.md5() m.update(test) return m.hexdigest()def USE_HMAC(key, text): if not isinstance(key, bytes): key = bytes(key, 'utf-8') if not isinstance(text, bytes): text = bytes(text, 'utf-8') h = hmac.new(key, text, digestmod='MD5') return h.hexdigest()def USE_SHA(text): if not isinstance(text, bytes): text = bytes(text, 'utf-8') sha = hashlib.sha1(text) encrypts = sha.hexdigest() return encryptsif __name__ == '__main__': aes_test = USE_AES("assssssssdfasasasasa") a = aes_test.encrypt("測試") b = aes_test.decodebytes(a) rsa_test = USE_RSA() a = rsa_test.rsaEncrypt("測試加密") b = rsa_test.rsaDecrypt(a) des_test = USE_DES(b"12345678", b"12345678") a = des_test.encrypt("測試加密") b = des_test.descrypt(a) des3_test = USE_DES3(b"123456789qazxswe") a = des3_test.encrypt("測試加密") b = des3_test.decrypt(a) md5_test = USE_MD5("測試簽名") hmac_test = USE_HMAC("123456", "測試") sha_test = USE_SHA("測試加密")參考資料
公眾號:咸魚學python
公眾號:小周碼字????
總結
以上是生活随笔為你收集整理的hikaridatasource 加密后登陆不上_渗透测试 | 突破前端JS加密限制的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Jeep 牧马人 2028 年将全面转向
- 下一篇: AMD RX 7900M显卡跑分首次曝光