javascript代碼混淆與加解密方式
javascript代碼混淆與加解密
開發(fā)一個python的程序,功能很簡單,對某個網(wǎng)頁發(fā)送post請求,把response的結(jié)果解析后存入數(shù)據(jù)庫,供后續(xù)分析。
抓包
首先是抓包,使用burp suite,發(fā)現(xiàn)該網(wǎng)頁原始的post請求如下:
因存在隱私信息,部分字段值已修改
uid=487f764baab641be902f4dxxxxxxxxx×tamp=1596436610125&sign=iNtdr75WKc7VHmbn8pu4VLqddoLEhupmin6LSi8vj%2Bn%2FyJ0zHlu34LVqQojiN4K6OoB6ggz0Q5NSx6rxxxxxxx%3D%3D
uid應(yīng)該是該用戶的id,timestamp為當(dāng)前時間戳,很好理解,sign暫時不去管它。
在burp里,使用repeat重新發(fā)包,修改timestamp時間戳字段后,無法收到response,懷疑sign為驗證標(biāo)識。
繼續(xù)使用原來的時間戳,可以收到response包,但是一段時間后,時間戳失效,不再能收到response。
分析如下:
網(wǎng)站為了防止DDOS攻擊,設(shè)置了請求驗證,通過加密算法把用戶id和當(dāng)前時間戳進行加密后,放入sign字段中。在服務(wù)器端解密后驗證,如驗證通過,返回response,驗證不通過,丟棄請求包。
timestamp字段應(yīng)該是用于sign有效期判斷,超過一定時間,該sign失效。
代碼審計
打開網(wǎng)頁源代碼,發(fā)現(xiàn)加密js代碼:
<!-- 加密start--> <script src="js/httpUtil.min.js"></script> <script type="text/javascript" src="plugins/CryptoJSv3.1.2/rollups/tripledes.js"></script> <script type="text/javascript" src="plugins/CryptoJSv3.1.2/components/mode-ecb-min.js"></script> <script src="plugins/bootstrap-table-1.11.0/bootstrap-table.min.js"></script> <script src="plugins/bootstrap-table-1.11.0/locale/bootstrap-table-zh-CN.min.js"></script> <script src="plugins/jquery.tmpl.min.js"></script> <script src="plugins/DESUtil.min.js"></script>
打開最后一個js文件,內(nèi)容如下:
eval(function(p, a, c, k, e, d) { ? ? e = function(c) { ? ? ? ? return (c < a ? "": e(parseInt(c / a))) + ((c = c % a) > 35 ? String.fromCharCode(c + 29) : c.toString(36)) ? ? }; ? ? if (!''.replace(/^/, String)) { ? ? ? ? while (c--) d[e(c)] = k[c] || e(c); ? ? ? ? k = [function(e) { ? ? ? ? ? ? return d[e] ? ? ? ? }]; ? ? ? ? e = function() { ? ? ? ? ? ? return '\\w+' ? ? ? ? }; ? ? ? ? c = 1; ? ? }; ? ? while (c--) if (k[c]) p = p.replace(new RegExp('\\b' + e(c) + '\\b', 'g'), k[c]); ? ? return p; } ('3 T(e,t,n){t=G==t||""==t||D 0===t?{A:(9 b).B()}:t;5 r=(9 b).x();t.v=r;5 o=f(d(t));t.u=o,$.s({w:"l",i:t,J:e,P:"R",m:3(e){},p:3(e){n(e)},k:3(e,t){},z:3(e){}})}3 Q(e,t,n){t=G==t||""==t||D 0===t?{A:(9 b).B()}:t;5 r=(9 b).x();t.v=r;5 o=f(d(t));t.u=o,$.s({w:"l",i:t,J:e,14:!1,10:!1,11:!1,m:3(e){},p:3(e){n(e)},k:3(e,t){},z:3(e){}})}5 c="12",7="Z";3 f(e){5 t=2.4.8.6(c),n=2.4.8.6(7);h y=2.j.13(e,t,{7:n,a:2.a.E,I:2.L.O}),2.4.q.U(y.K)}3 W(e){e=2.4.q.6(e).N();5 t=2.4.8.6(c),n=2.4.8.6(7);h 2.j.S({K:2.4.V.6(e)},t,{7:n,a:2.a.E,I:2.L.O}).N(2.4.8)}3 d(e){5 t="",n=[];H(M C e)n[n.F]=M;H(g C n.Y(),n)t=t+n[g]+"="+e[n[g]]+",";h t=t.X(0,t.F-1)}', 62, 67, '||CryptoJS|function|enc|var|parse|iv|Utf8|new|mode|Date|key|mapSortToString||encryptByDESModeCBC|value|return|data|DES|complete|post|beforeSend|||success|Base64||ajax||sign|timestamp|type|valueOf|encrypted|error|date|getTime|in|void|CBC|length|null|for|padding|url|ciphertext|pad|thisKey|toString|Pkcs7|dataType|ajaxDESWebUploadImg|json|decrypt|ajaxDESWeb|stringify|Hex|decryptByDESModeCBC|substring|sort|asdewqrf|processData|contentType|keycansr|encrypt|cache'.split('|'), 0, {}))
我們看到有這部分代碼
while(c–)if(k[c])p=p.replace(new RegExp('\b'+e?+'\b',‘g'),k[c]);return p;
這個是標(biāo)準(zhǔn)的javascript混淆加密壓縮
用document.getElementById(‘textareaID’).innerText=p; 替代 return p;
放入普通html文件的body部分打開,得到j(luò)avascript代碼如下:
function ajaxDESWeb(e, t, n) { ? ? t = null == t || "" == t || void 0 === t ? { ? ? ? ? date: (new Date).getTime() ? ? }: t; ? ? var r = (new Date).valueOf(); ? ? t.timestamp = r; ? ? var o = encryptByDESModeCBC(mapSortToString(t)); ? ? t.sign = o, ? ? $.ajax({ ? ? ? ? type: "post", ? ? ? ? data: t, ? ? ? ? url: e, ? ? ? ? dataType: "json", ? ? ? ? beforeSend: function(e) {}, ? ? ? ? success: function(e) { ? ? ? ? ? ? n(e) ? ? ? ? }, ? ? ? ? complete: function(e, t) {}, ? ? ? ? error: function(e) {} ? ? }) } function ajaxDESWebUploadImg(e, t, n) { ? ? t = null == t || "" == t || void 0 === t ? { ? ? ? ? date: (new Date).getTime() ? ? }: t; ? ? var r = (new Date).valueOf(); ? ? t.timestamp = r; ? ? var o = encryptByDESModeCBC(mapSortToString(t)); ? ? t.sign = o, ? ? $.ajax({ ? ? ? ? type: "post", ? ? ? ? data: t, ? ? ? ? url: e, ? ? ? ? cache: !1, ? ? ? ? processData: !1, ? ? ? ? contentType: !1, ? ? ? ? beforeSend: function(e) {}, ? ? ? ? success: function(e) { ? ? ? ? ? ? n(e) ? ? ? ? }, ? ? ? ? complete: function(e, t) {}, ? ? ? ? error: function(e) {} ? ? }) } var key = "keycansr", iv = "asdewqrf"; function encryptByDESModeCBC(e) { ? ? var t = CryptoJS.enc.Utf8.parse(key), ? ? n = CryptoJS.enc.Utf8.parse(iv); ? ? return encrypted = CryptoJS.DES.encrypt(e, t, { ? ? ? ? iv: n, ? ? ? ? mode: CryptoJS.mode.CBC, ? ? ? ? padding: CryptoJS.pad.Pkcs7 ? ? }), ? ? CryptoJS.enc.Base64.stringify(encrypted.ciphertext) } function decryptByDESModeCBC(e) { ? ? e = CryptoJS.enc.Base64.parse(e).toString(); ? ? var t = CryptoJS.enc.Utf8.parse(key), ? ? n = CryptoJS.enc.Utf8.parse(iv); ? ? return CryptoJS.DES.decrypt({ ? ? ? ? ciphertext: CryptoJS.enc.Hex.parse(e) ? ? }, ? ? t, { ? ? ? ? iv: n, ? ? ? ? mode: CryptoJS.mode.CBC, ? ? ? ? padding: CryptoJS.pad.Pkcs7 ? ? }).toString(CryptoJS.enc.Utf8) } function mapSortToString(e) { ? ? var t = "", ? ? n = []; ? ? for (thisKey in e) n[n.length] = thisKey; ? ? for (value in n.sort(), n) t = t + n[value] + "=" + e[n[value]] + ","; ? ? return t = t.substring(0, t.length - 1) }
注意到以下代碼
o = encryptByDESModeCBC(mapSortToString(t)); ? ? t.sign = o,
sign是通過這里得到的
繼續(xù)尋找encryptByDESModeCBC和mapSortToString
看到mapSortToString是把uid和timestamp用逗號分隔的等式拼接后,用CryptoJS的加解密庫進行DES加密
從js代碼中還可以看到加解密使用的key和IV
var key = "keycansr", iv = "asdewqrf";
解密驗證
為了驗證我們代碼審計的結(jié)論,把抓包的sign用python進行DES解密
代碼如下:
myDes.py
from Crypto.Cipher import DES import base64 class DESUtil: ? ? __BLOCK_SIZE_8 = BLOCK_SIZE_8 = DES.block_size ? ? __IV = "asdewqrf" # __IV = chr(0)*8 ? ? @staticmethod ? ? def encryt(str, key): ? ? ? ? cipher = DES.new(key, DES.MODE_CBC, DESUtil.__IV) ? ? ? ? x = DESUtil.__BLOCK_SIZE_8 - (len(str) % DESUtil.__BLOCK_SIZE_8) ? ? ? ? if x != 0: ? ? ? ? ? ? str ?= str + chr(x)*x ? ? ? ? msg = cipher.encrypt(str) ? ? ? ? # msg = base64.urlsafe_b64encode(msg).replace('=', '') ? ? ? ? msg = base64.b64encode(msg) ? ? ? ? return msg ? ? @staticmethod ? ? def decrypt(enStr, key): ? ? ? ? cipher = DES.new(key, DES.MODE_CBC,DESUtil.__IV) ? ? ? ? # enStr += (len(enStr) % 4)*"=" ? ? ? ? # decryptByts = base64.urlsafe_b64decode(enStr) ? ? ? ? decryptByts = base64.b64decode(enStr) ? ? ? ? msg = cipher.decrypt(decryptByts) ? ? ? ? print(msg) ? ? ? ? print(len(msg)) ? ? ? ? print(msg[len(msg)-1]) ? ? ? ? #paddingLen = ord((msg[len(msg)-1])) ? ? ? ? paddingLen = msg[len(msg) - 1] ? ? ? ? return msg[0:-paddingLen] if __name__ == "__main__": ?? ?mySign = 'iNtdr75WKc7VHmbn8pu4VLqddoLEhupmin6LSi8vj%2Bn%2FyJ0zHlu34LVqQojiN4K6OoB6ggz0Q5NSx6rxxxxxxx==' ?? ?key = 'keycansr' ?? ?print(DESUtil.decrypt(mySign, key)
執(zhí)行結(jié)果如下:
b'timestamp=1596436610125,uid=487f764baab641be902f4dxxxxxxxxx'
驗證了我們之前代碼審計的結(jié)論
構(gòu)造POST請求
知道加解密算法后,很容易就構(gòu)造POST請求
代碼如下:
sendHttps.py import requests import time from myDes import * from requests.packages import urllib3 urllib3.disable_warnings() uuid = '487f764baab641be902f4dxxxxxxxxx' ttime = time.time()*1000 key='keycansr' sstr = "timestamp="+str(ttime)+',uid='+uuid ssign = DESUtil.encryt(sstr, key) #print(ssign) data = {'uid':uuid, 'timestamp':ttime, 'sign':ssign} req = requests.post('https://xxxx.com/api/user/userAccount/list',data=data,verify=False) print(req.text)
后續(xù)就是對req.text的內(nèi)容進行json解碼和正則分析,然后插入到PostgreSQL的數(shù)據(jù)庫中進行分析。
js代碼混淆處理辦法
將JavaScript 代碼轉(zhuǎn)換成顏文字網(wǎng)絡(luò)表情的編碼已達到混淆的目的
原理:這類混淆通常都是使用構(gòu)造函數(shù)將字符串作為代碼運行
例如:
const sum = new Function('a','b','return a+b'); console.log(sum(2,6));
解決方法:
1.直接將混淆后的代碼粘貼到控制臺通過VM 查看源代碼 只對報錯代碼有效
2.對于在控制臺輸出不報錯的代碼,
第一種方法:刪除代碼結(jié)尾的“(’_’);”
第二種方法:刪除后替換為“toString()”方法輸出,再將修改后的代碼粘貼至控制臺運行
將JavaScript 代碼轉(zhuǎn)換成只有6種字符([, ], (, ), !, +)的編碼
以達到混淆的目的
例如:
‘0':'[+[]]' ‘1':'[+!+[]]' …
解決方法:
1.直接將混淆后的代碼粘貼至控制臺通過查看VM查看源代碼
2.代碼最后有成對的括號,刪除代碼結(jié)尾的‘()’ ;或者替換為 ‘toString()’ 或?qū)⑿薷暮蟮拇a粘貼至控制臺運行。
3.代碼最后沒有成對括號,只有列如‘)’這種,將代碼通過https://beautifier.io/ 這個網(wǎng)站美化以后,復(fù)制到編輯器中,通過最后一個括號找到前面括號,把括號之中的代碼復(fù)制出來,在控制帶輸出就會得到源碼
通過eval()編碼的代碼
解決方法:
將eval 中的代碼復(fù)制出來,更改為alert / document.write / console.log 在控制臺輸出就能解密
總結(jié)
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
基于pako.js實現(xiàn)gzip的壓縮和解壓功能示例
這篇文章主要介紹了基于pako.js實現(xiàn)gzip的壓縮和解壓功能,結(jié)合具體實例形式分析了pako.js實現(xiàn)字符串壓縮與解壓縮的相關(guān)操作技巧,需要的朋友可以參考下2017-06-06Electron中關(guān)于靜態(tài)資源加載問題的解決方案
通常,我們在使用electron框架的時候會使用到loadFile/loadURL進行頁面的加載,分別是加載本地文件和加載網(wǎng)絡(luò)文件,當(dāng)nuxtjs/nextjs想引入到electron中顯示時,你會遇到資源路徑引用的問題,所以本文給大家介紹了Electron中關(guān)于靜態(tài)資源加載問題的解決方案2024-12-12新發(fā)現(xiàn)原來documenet.URL也可以實現(xiàn)頁面跳轉(zhuǎn)
新發(fā)現(xiàn)原來documenet.URL也可以實現(xiàn)頁面跳轉(zhuǎn)...2007-08-08JavaScript在form表單中使用button按鈕實現(xiàn)submit提交方法
在form表單提交中,使用button來間接完成submit的提交更為靈活。接下來通過本文給大家介紹JavaScript在form表單中使用button按鈕實現(xiàn)submit提交方法,需要的朋友參考下吧2017-01-01