javascript代碼混淆與加解密方式
javascript代碼混淆與加解密
開發(fā)一個(gè)python的程序,功能很簡(jiǎn)單,對(duì)某個(gè)網(wǎng)頁發(fā)送post請(qǐng)求,把response的結(jié)果解析后存入數(shù)據(jù)庫,供后續(xù)分析。
抓包
首先是抓包,使用burp suite,發(fā)現(xiàn)該網(wǎng)頁原始的post請(qǐng)求如下:
因存在隱私信息,部分字段值已修改
uid=487f764baab641be902f4dxxxxxxxxx×tamp=1596436610125&sign=iNtdr75WKc7VHmbn8pu4VLqddoLEhupmin6LSi8vj%2Bn%2FyJ0zHlu34LVqQojiN4K6OoB6ggz0Q5NSx6rxxxxxxx%3D%3D
uid應(yīng)該是該用戶的id,timestamp為當(dāng)前時(shí)間戳,很好理解,sign暫時(shí)不去管它。
在burp里,使用repeat重新發(fā)包,修改timestamp時(shí)間戳字段后,無法收到response,懷疑sign為驗(yàn)證標(biāo)識(shí)。
繼續(xù)使用原來的時(shí)間戳,可以收到response包,但是一段時(shí)間后,時(shí)間戳失效,不再能收到response。
分析如下:
網(wǎng)站為了防止DDOS攻擊,設(shè)置了請(qǐng)求驗(yàn)證,通過加密算法把用戶id和當(dāng)前時(shí)間戳進(jìn)行加密后,放入sign字段中。在服務(wù)器端解密后驗(yàn)證,如驗(yàn)證通過,返回response,驗(yàn)證不通過,丟棄請(qǐng)求包。
timestamp字段應(yīng)該是用于sign有效期判斷,超過一定時(shí)間,該sign失效。
代碼審計(jì)
打開網(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>
打開最后一個(gè)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;
這個(gè)是標(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用逗號(hào)分隔的等式拼接后,用CryptoJS的加解密庫進(jìn)行DES加密
從js代碼中還可以看到加解密使用的key和IV
var key = "keycansr", iv = "asdewqrf";
解密驗(yàn)證
為了驗(yàn)證我們代碼審計(jì)的結(jié)論,把抓包的sign用python進(jìn)行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'
驗(yàn)證了我們之前代碼審計(jì)的結(jié)論
構(gòu)造POST請(qǐng)求
知道加解密算法后,很容易就構(gòu)造POST請(qǐng)求
代碼如下:
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ù)就是對(duì)req.text的內(nèi)容進(jìn)行json解碼和正則分析,然后插入到PostgreSQL的數(shù)據(jù)庫中進(jìn)行分析。
js代碼混淆處理辦法
將JavaScript 代碼轉(zhuǎn)換成顏文字網(wǎng)絡(luò)表情的編碼已達(dá)到混淆的目的
原理:這類混淆通常都是使用構(gòu)造函數(shù)將字符串作為代碼運(yùn)行
例如:
const sum = new Function('a','b','return a+b'); console.log(sum(2,6));
解決方法:
1.直接將混淆后的代碼粘貼到控制臺(tái)通過VM 查看源代碼 只對(duì)報(bào)錯(cuò)代碼有效
2.對(duì)于在控制臺(tái)輸出不報(bào)錯(cuò)的代碼,
第一種方法:刪除代碼結(jié)尾的“(’_’);”
第二種方法:刪除后替換為“toString()”方法輸出,再將修改后的代碼粘貼至控制臺(tái)運(yùn)行
將JavaScript 代碼轉(zhuǎn)換成只有6種字符([, ], (, ), !, +)的編碼
以達(dá)到混淆的目的
例如:
‘0':'[+[]]' ‘1':'[+!+[]]' …
解決方法:
1.直接將混淆后的代碼粘貼至控制臺(tái)通過查看VM查看源代碼
2.代碼最后有成對(duì)的括號(hào),刪除代碼結(jié)尾的‘()’ ;或者替換為 ‘toString()’ 或?qū)⑿薷暮蟮拇a粘貼至控制臺(tái)運(yùn)行。
3.代碼最后沒有成對(duì)括號(hào),只有列如‘)’這種,將代碼通過https://beautifier.io/ 這個(gè)網(wǎng)站美化以后,復(fù)制到編輯器中,通過最后一個(gè)括號(hào)找到前面括號(hào),把括號(hào)之中的代碼復(fù)制出來,在控制帶輸出就會(huì)得到源碼
通過eval()編碼的代碼
解決方法:
將eval 中的代碼復(fù)制出來,更改為alert / document.write / console.log 在控制臺(tái)輸出就能解密
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
javascript檢測(cè)瀏覽器flash版本的實(shí)現(xiàn)代碼
javascript檢測(cè)瀏覽器flash版本的實(shí)現(xiàn)代碼,需要的朋友可以參考下。2011-12-12Bootstrap樹形組件jqTree的簡(jiǎn)單封裝
這篇文章主要介紹了Bootstrap樹形組件jqTree的簡(jiǎn)單封裝,封裝一個(gè)稍微完整點(diǎn)的樹形組件,感興趣的小伙伴們可以參考一下2016-01-01在ES5與ES6環(huán)境下處理函數(shù)默認(rèn)參數(shù)的實(shí)現(xiàn)方法
本文給大家介紹在ES5與ES6環(huán)境下處理函數(shù)默認(rèn)參數(shù)的實(shí)現(xiàn)方法,本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,感興趣的朋友跟隨腳本之家小編一起學(xué)習(xí)吧2018-05-05js實(shí)現(xiàn)用戶離開頁面前提示是否離開此頁面的方法(包括瀏覽器按鈕事件)
這篇文章主要介紹了js實(shí)現(xiàn)用戶離開頁面前提示是否離開此頁面的方法,較為詳細(xì)的分析了javascript針對(duì)瀏覽器事件的操作技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-07-07微信小程序?qū)W習(xí)筆記之本地?cái)?shù)據(jù)緩存功能詳解
這篇文章主要介紹了微信小程序?qū)W習(xí)筆記之本地?cái)?shù)據(jù)緩存功能,結(jié)合實(shí)例形式分析了微信小程序wx.setStorage、wx.getStorage以及wx.removeStorage、wx.clearStorage針對(duì)數(shù)據(jù)緩存的存取、刪除等相關(guān)操作技巧,需要的朋友可以參考下2019-03-03簡(jiǎn)單實(shí)用的反饋表單無刷新提交帶驗(yàn)證
表單無刷新提交帶驗(yàn)證,非常適用于反饋,具體的實(shí)現(xiàn)如下包含各個(gè)功能代碼,喜歡的朋友可以參考下2013-11-11JavaScript字符串String和Array操作的有趣方法
字符串和數(shù)組在程序編寫過程中是十分常用的類型,因此程序語言都會(huì)將String和Array作為基本類型,并提供許多字符串和數(shù)組的方法來簡(jiǎn)化對(duì)字符串的操作2012-12-12