前端加密之使用firefox來解密


前端加密之使用firefox來解密

 文本采用(CC-BY-NC-ND) 非商用可轉載,不可修改本文內容

隨着等保2.0的實施,傳輸過程中加密變的必要了,很多APP或者手機瀏覽器端逐步加密了一些加密的措施來解決這個問題,比如以下這樣的數據包

 

一串亂碼,什么是什么都看不懂別說修改數據了。。那咋辦呢? 我們可以使用firefox,chrome之類的,我個人比較喜歡firefox,以下的都是基於firefox來講解。

 

1、觸發button

 

 打開調試器ctrl+F12然后進入 調試器,刷新頁面后會加載全部的js,根據習慣,一般login.js就看起來像是主要登錄模塊觸發的函數的頁面。

 

 

 我們回到頁面來觸發這個button。前端很都操作都是基於事件綁定的。js就是事件驅動的語言,會有大量的閉包,一旦寫不好就瀏覽器內存++,當然這個是題外話。既然是事件驅動就會有很多function.on('click',callback(){})之類的操作來定義一些檢測的邏輯。

我們先對需要出發的那個button,比如“登錄”,“發送”之類的button,點擊他的右鍵,然后選擇“查看元素”。 Firefox會快速定位到這個元素附近的HTML結構,當然也會因為CSS層疊的問題導致定位不准,不過這個不重要,多用幾次就知道如何快速定位到關鍵元素,我們目前還是在講解HTML,還沒開始講解JS部分。

 

 

然后點擊event,我們快速定位到這個button點擊后的事件邏輯塊代碼,比如以下的圖

 

 

可以看到這個button綁定了2個click事件,下面那個事件為冒泡事件,即在上面的那個click的同時,下面那個click也會被觸發。這個大概的觸發代碼應該是

$('#buttion_id').click(function(){...}) ,其中,紅色部分內容應該就是我們打開的這個click事件框框中的代碼,我把代碼全部貼出來。

以下代碼是基於jquery的,要看懂的話需要一些基礎,我講的盡可能簡單,讓大家都可以快速明白。

 

 1 function() {
 2   if (!$(this).prop('disabled')) {
 3     var mobilenum = /^(13[0-9]|15[0-9]|16[0-9]|17[013678]|18[0-9]|19[0-9]|14[57])[0-9]{8}$/;
 4     var mob = $('.row input').eq(3).val().replace(/\s/g, '')
 5     if (!mobilenum.test(mob)) {
 6       $('#modal-error').modal('show').children('.error-content').children('.modal-body').html('請輸入有效的手機號');
 7       return;
 8     }
 9     if (needImg) {
10       $('#modal-input').modal('show');
11       $('#modal-input .msg-code-img').eq(0).click();
12       var $_input = $('#modal-input input')[0];
13       setTimeout(function() {
14         $_input.focus();
15       }, 500);
16     } else {
17       var params = {
18         'MOBILE': $('.row input').eq(3).val().replace(/\s/g, '')
19       };
20       getPhoneCode(params);
21     }
22   }
23 }

最主要我們看17-20行的代碼,構造了一個對象傳入到getPhoneCode函數中,params的參數就是手機的參數 並且處理一定邏輯。

 

2、調試JS,下斷點

接來下我們去搜索getPhoneCode(params)函數。

 

 

我們通過全局查找快速定位到這個函數的位置。

 

 1 function getPhoneCode(params) {
 2     if (!$('#msg-btn').prop("disabled")) {
 3         $('#msg-btn').prop("disabled", true);
 4         params['ajaxUrl'] = "/user2/sendSmsCode";
 5         params['ajaxCallBack'] = function (data) {
 6             if (data.MSG_CODE == 0) {
 7                 if (data.MESSAGE_CODE) {
 8                     $('.msg-code-input').val(data.MESSAGE_CODE);
 9                     checkNull();
10                 }
11                 /*if (show_input) {
12                  $('.tel-code-row').show();
13                  }*/
14                 $(".msg-code-img:not('#modal-input .msg-code-img'):last").click();
15                 time($('#msg-btn'));
16                 return;
17             } else if (data.MSG_CODE == '102') {
18                 if ($('.tel-code-row').css('display') == 'none') {
19                     alerterror('您的失敗次數過多<br/>請輸入圖片驗證碼');
20                     $('.tel-code-row').show();
21                 } else {
22                     alerterror('圖片驗證碼輸入錯誤');
23                 }
24                 if (typeof show_input != 'undefined' && !show_input) {
25                     show_input = true;
26                 }
27             } else {
28                 alertfail();
29             }
30             $('#msg-btn').prop("disabled", false);
31         }
32         submitAjax(params);
33     }
34 }

 

32行之前都是一些邏輯和判斷的代碼 和我們想要調試的沒關系,4-5行是把一些信息包裝進了params參數中,最終通過32行的submitAjax函數來執行,我們繼續跟蹤這個函數 ,方法和上面一樣,全局搜索這個方法

 

 

submitAjax函數的定義如下:

 

 1 //提交數據
 2 var submitAjax = function (dataObject) {
 3     var ajaxUrl = UC_URL + dataObject.ajaxUrl
 4     delete dataObject.ajaxUrl
 5     var ajaxCallBack = dataObject.ajaxCallBack;
 6     delete dataObject.ajaxCallBack;
 7     dataObject.CHNLID = dataStore.getItem("CHNLID");
 8     dataObject.BACKURL = unescape(Base64.decode(dataStore.getItem("backUrl")));
 9     dataObject.VERSION = "1.32";
10     if (null != dataObject.BACKURL) {
11         dataObject.BACKURL = getDomain(dataObject.BACKURL);
12     }
13     if (null == dataObject.CHNLID && null == dataObject.BACKURL) {
14         dataObject.CHNLID = 'SF';
15         dataObject.BACKURL = location.host;
16     }
17     var JsonParams = JSON.stringify(dataObject);
18     var rsaCommit = function (JsonParams) {
19         var RSAParams = RSAUtils.encryptedString(rsaKey, JsonParams);
20         $.ajax({
21             type: "POST",
22             cache: false,
23             dataType: "json",
24             url: ajaxUrl,
25             contentType: 'text/plain',
26             data: RSAParams,
27           ...82 }

 

去掉了一些不重要的代碼,我們主要來看dataObject參數。我們先對這個地方下個斷點

 

 然后我們回到頁面,填寫好手機號,點擊“獲取驗證碼”,讓代碼跑起來。

 

我們可以看到dataObject參數就2個屬性,然后繼續往下跟,在Json.stringfy之前下斷點,因為最后加密的函數是第19行,RSAUtils.encryptedString(rsaKey, JsonParams);

其實有經驗的同學可以直接在這里下斷點查看,這里的rasKey是沒有定義的,只有這個JsonParams,而 JsonParams就是剛才的dataObject對象的json序列化。

 

 

我們可以看到,在變成json格式之前,程序加入了一些其他的參數,這個不重要。接下來我們下斷點到 RSAUtils.encryptedString函數。然后來看rsaKey參數是什么,鼠標移上去顯示是undefined..

因為代碼只var rsaKey,並沒賦值任何。。。不知道程序員在想什么,這個類的加密string方法顯然是需要一個加密的key的,也就是私鑰,其實這個程序有一個密鑰,不過不是這個函數里的

 

 

所以其實前端加密來阻止參數修改沒意義的。。

 

3、構造參數

最后我們需要理解上面的流程和邏輯

  1. 獲取需要的參數比如mobile,版本等信息
  2. json序列化
  3. 加密
  4. 發送至服務端

 

最后的代碼是

 

1 var my = {MOBILE:"15*******",CHNLID:"SF",BACKURL:"*****.com.cn",VERSION:"1.32"};
2 var data = JSON.stringify(my);
3 var rsaKey;
4 RSAUtils.encryptedString(rsaKey, data);

 最后得出的值是

 

我們再和直接用burpsuite抓到的值對比下

 

 

 

其他的一些思考

 

基本上就結束了,接來下我們可以構造任何我們想要的值來替換掉傳輸,因為至此我們可以構造任何想要的參數來篡改。

其實換個思路,我們可以編寫一些插件(其實已經有類似的插件) ,例如早起的Tamper Data 插件之類的,或者自己寫腳本引入,在一些關鍵代碼之前反射出對象的全部屬性.

從程序員角度來看,我們可以看到他有好幾個加密方法,有的傳了密鑰,有的沒有。而且代碼都是部分混淆,部分沒有,其實提高一下門檻的話,應該把login.js之類的 也混淆了。雖然這種混淆對我這樣的安全工程師沒什么用,但是waf不也是這樣的思路嗎?

把全部的js混淆了不僅可以壓縮代碼的字符數,減少服務器壓力和帶寬,還可以提高安全的門檻,增加破解難度,在交互中多次傳輸一些長度很高的token,迷惑安全人員,再使用一些js和瀏覽器的hack技術,使得門檻可以非常高。 比如這些js的奇怪特性。

javascript中那些奇怪的特性

JavaScript中的"奇奇怪怪"

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM