<pre id="vvttv"><mark id="vvttv"><progress id="vvttv"></progress></mark></pre>
    <pre id="vvttv"></pre>

      <p id="vvttv"></p>

          <p id="vvttv"></p>

                <p id="vvttv"></p>

                <pre id="vvttv"><cite id="vvttv"><progress id="vvttv"></progress></cite></pre>

                  <output id="vvttv"><dfn id="vvttv"><th id="vvttv"></th></dfn></output>

                    <p id="vvttv"></p>

                    原文地址:http://drops.wooyun.org/tips/14042

                    0x00 概述


                    本來是和上篇文章一起發的,后來出去,就擱置了。

                    比較高興有人參與討論和吐(B)槽(4),其實本身也沒啥高大上的技術,只是自己在對以前工具做review和重構的時候發現,這些東西很少人在討論分享,所以也就放出來,算是拋磚引玉。

                    今天分享兩個東西。

                    當然,干貨也就意味著乏味,如果大家不想看文章的可以直接看代碼。

                    第一個分享是對我上一篇文章的補充,QQ模擬登錄實現之四兩撥千斤(基于V8引擎)

                    自己參考TX JS代碼實現的加密流程,因為個人能力有限,所以TX的tea算法是直接引用的hoxide 2005基于python的實現。

                    第二個分享其實是主要是利用QQ客戶端實現快速登錄,快速登錄對環境有一定的依賴,但是也有很多好處,我們不用處理密碼;當然,這種登錄方式的使用場景比較有限,目前主要在爬蟲和掃描器的場景。

                    0x01 QQ模擬登錄實現之愚公移山(流程算法實現)


                    在上一篇文章:QQ模擬登錄實現之四兩撥千斤(基于V8引擎)

                    中我們分享了QQ帳號密碼登錄的流程和基于JS引擎實現的密碼加密方式,我們用一種簡單實用的方式實現了“能用”。

                    但是對于一個做安全愛好者,有時候我們需要深入一些,整個加密的流程和算法,我們是不是自己可以實現一套?所以,本文的重點,是對TX密碼處理流程的分析。

                    密碼處理流程

                    總體說明

                    了解了登錄流程,我們在要分析和實現模擬登錄需要考慮一個問題,密碼是如何處理的?

                    要了解密碼是如何處理的,我們先要了解以下3種算法:MD5,RSA,TEA。其中MD5是hash算法,比較常用;RSA是一種非對稱加密算法,大家也比較了解。這里需要說明一下TEA算法。

                    TEA算法Tiny Encryption Algorithm,是一種分組加密算法,實現比較簡單。TEA算法使用64位的明文分組和128位的密鑰,需要進行 64 輪迭代。

                    不過TX_TEA算法對傳統的TEA算法進行了一些修改,具體的原理可以參考登錄的JS。這里簡單說明下:TX只使用了16輪迭代;TX_TEA加密的是數據流,并且采用的是反饋隨機交織填充方式。

                    加密流程

                    加密總流程圖

                    p1

                    基本上看懂這個流程圖,就明白QQ密碼的加密流程了。

                    淺藍色的是來源數據,綠色是一些密碼的處理方法(加密、HASH、替換)。

                    來源數據:

                    密碼:password
                    salt:salt,來源于check接口的返回
                    verifycode: 來源于check接口的返回
                    rsaKey在js源碼里面可以獲取
                    

                    數據說明:

                    最后進行tea算法tea(v, k)

                    結果進行base64編碼

                    Replace是做一個簡單的替換,對以下3個字符進行替換

                    / -> -
                    + -> *
                    = -> _
                    

                    最后得出加密的密碼,長度為216的字符串,形式參考如下:

                    #!bash
                    37Hro2-AgR4d8ZkU1L-6FqYhTUdhywhLlD2WihfVZGqZmz5R1RlwBsYPNowY0ZHJxcISmwpW0e7ppcoEDTGYyM5*6ZPJNUnZnb4h4Ke*qIBnFlTkiYFUhUwvXgOEvfIDTgCZIWsiFT6EauXujkB2i5yNFobx9aN5vw2xFyE1E2VoF*LV952q0mQO-HiooQZfMocl13kxFgxtVQaSRpm7Rg__
                    

                    參考代碼:

                    #!python
                    def tx_pwd_encode(self, pwd, salt, verifycode):
                        """
                        js:getEncryption(t, e, i, n)
                        t=pwd, e=salt 二進制形式, i=verifycode, n:default undefined
                        # """
                        salt = salt.replace(r'\x', '')
                        e = self.fromhex(salt)
                        md5_pwd = o = self.tx_md5(pwd)
                        r = hashlib.md5(pwd).digest()
                        p = self.tx_md5( r + e )
                        a = rsa.encrypt(r, self.rsaKey)
                        rsaData = a = binascii.b2a_hex(a)
                    
                        # rsa length
                        s = self.hexToString( len(a)/2 )
                        s = s.zfill(4)
                    
                        # verifycode先轉換為大寫,然后轉換為bytes
                        verifycodeLen = hex(len(verifycode)).replace(r"0x","").zfill(4)
                        l = binascii.b2a_hex( verifycode.upper() )
                    
                        # verifycode length
                        c = self.hexToString( len(l)/2 )
                        c = c.zfill(4)
                    
                        # TEA: KEY:p, s+a+ TEA.strToBytes(e) + c +l
                        new_pwd = s + a + salt + c + l
                        saltpwd = base64.b64encode(
                                tea.encrypt( self.fromhex(new_pwd), self.fromhex(p) )
                        ).decode().replace('/', '-').replace('+', '*').replace("=", "_")
                    

                    代碼傳送門:
                    https://github.com/LeoHuang2015/qqloginjs/blob/master/autologin_account.py

                    0x02 QQ模擬登陸實現之草船借箭(客戶端快速登錄實現)


                    我們在對QQ進行爬取和掃描的時候,很多時候需要考慮到登錄的情況,如果使用用戶名密碼的方式,可能因為一些風控規則,當我們多次登陸時就要求圖片驗證碼,而使用快速登錄就能很好的規避這種情況。

                    流程梳理

                    客戶端快速登錄方式是用戶在PC端已經登錄了QQ客戶端軟件,如果用戶再打開Web頁面進行登陸,不用再輸入用戶名和密碼,只需要選擇已經登錄的帳號,點擊確認登錄即可。

                    p2

                    快速登錄的本質上是使用clientkey置換token。

                    QQ客戶端登陸后會生成一個長224的clientkey認證字符串,每次登陸都會變化,參考如下:

                    #!bash
                    000156DCEB4E0068663F53B8B402784291BB6E74C482BFB6367FF48FB970443E9B9682359E8F1F92D5A814B097D12D938B96B30742DDE5CDA8E453EB7CD31A5121416637D945615C661285F5306884D959184AB1E4F7CFA83BC9FAF069C1E5878320ECF79EF8751320763492752A1433
                    

                    早期快速登錄的實現方式是各個瀏覽器使用插件,如IE的 ActiveX控件支持的,firefox是插件,通過插件植入clientkey。

                    后續支持非插件的形式,每次動態的訪問QQ客戶端綁定的本地server(localhost.ptlogin2.qq.com)獲取clientkey,然后再用clientkey去置換token。

                    整體流程

                    客戶端快速登錄主要分為兩種情況:

                    一種是插件模式,直接使用clientkey置換token登陸;

                    另外一種是費插件模式,或者clientkey出現一些異常情況,通過請求server把clientkey設置到cookie,然后再置換token登陸。

                    p3

                    流程分析

                    clientkey存在且正常/插件模式

                    1.組件加載&獲取用戶頭像信息

                    同帳號和密碼登陸,只是這里獲取已經登陸QQ客戶端的用戶頭像和昵稱。

                    獲取登陸信息,用戶
                    http://ptlogin2.qq.com/getface

                    返回:帳號、頭像地址(有多個客戶端登陸的帳號,請求多個)

                    2.登陸

                    用戶點擊登陸,實際上是一個clientkey置換token的過程。

                    請求會帶上clientkey進行認證
                    http://ptlogin2.qq.com/jump

                    如果client不正確,則會登陸失敗,后續登陸不會信任原來的clientkey,會走clientkey不存在/異常的流程。

                    clientkey不存或者異常/沒有安裝插件

                    1.組件加載&獲取用戶信息&獲取用戶頭像信息

                    由于clientkey這里會多一步獲取用戶信息的流程

                    參考url:

                    #!bash
                    http://localhost.ptlogin2.qq.com:4300/pt_get_uins
                    ?callback=ptui_getuins_CB
                    &r=0.5314265366275367
                    &pt_local_tk=0.3291951622654449
                    

                    返回,賬號信息,客戶端類型,昵稱等信息

                    PS:這里如果獲取不到會進行重試,一共5次,比如http的端口依次是4300,4302,4304,4306,4308。

                    然后再獲取用戶頭像信息(同上)。

                    2.登陸

                    獲取clientkey

                    參考URL:

                    #!bash
                    http://localhost.ptlogin2.qq.com:4300/pt_get_st
                    ?clientuin=1802014971
                    &callback=ptui_getst_CB
                    &r=0.11057236711379814
                    &pt_local_tk=0.3291951622654449
                    

                    返回:設置clientkey到cookie,返回回調方法

                    #!js
                    var var_sso_get_st_uin={uin:"1802014971"};ptui_getst_CB(var_sso_get_st_uin);
                    

                    然后進行登陸置換
                    http://ptlogin2.qq.com/jump

                    返回:設置cookie

                    #!js
                    ptui_qlogin_CB('0', 'http://www.qq.com/qq2012/loginSuccess.htm', '');
                    

                    設置完cookie后,再請求ptlogin2.qq.com域的如下url來完成對ptlogin2.qq.com域和qq.com域的認證cookie的設置,同時刪除clientuin和clientkey這兩個cookie值。

                    模擬實現

                    我們需要模擬實現的快速登錄,需要走非插件模式的流程,流程本身比較簡單,也沒有比較復雜的算法,如下:

                    p4

                    這里有兩個安全點需要注意:

                    1. Token校驗:請求的pt_local_tk會和cookie中的pt_local_tk校驗;
                    2. Referrer驗證:referer限制了QQ域。

                    參考代碼:

                    #!python
                    def get_client_uins(self):
                        '''
                        get client unis info
                        need: token check & referer check
                        '''
                        tk =  "%s%s" %(random.random(), random.randint(1000, 10000) )
                        self.session.cookies['pt_local_token'] = tk
                        self.session.headers.update({'Referer':'http://ui.ptlogin2.qq.com/'})
                    

                    具體實現參考代碼:
                    https://github.com/LeoHuang2015/qqloginjs/blob/master/autologin_quick.py

                      <pre id="vvttv"><mark id="vvttv"><progress id="vvttv"></progress></mark></pre>
                      <pre id="vvttv"></pre>

                        <p id="vvttv"></p>

                            <p id="vvttv"></p>

                                  <p id="vvttv"></p>

                                  <pre id="vvttv"><cite id="vvttv"><progress id="vvttv"></progress></cite></pre>

                                    <output id="vvttv"><dfn id="vvttv"><th id="vvttv"></th></dfn></output>

                                      <p id="vvttv"></p>

                                      这里只有精品视频