您的位置:首页 > Web前端 > JavaScript

【JS逆向】破解B站登录加密策略

2021-01-04 21:50 417 查看
昨天还立flag说今天不摸鱼了,结果又摸了半天,稍微总结一下。GitHub上有一个叫做「哔哩哔哩-API收集整理」[1]的仓库,这个仓库是之前破解第三方B站视频下载方式的时候明一gg发给我的,当时没有看的很仔细,今天又找出来看了看里面的文档,解决了很多之前的疑惑。

登录有什么用——获取cookie

上一篇推文里,随口提了一句登录的作用。
登录以后可以获得更多的权限,比如发帖、收藏等功能,此外有些内容只有登录以后才能访问。
对于B站来说,注册成为正式会员以后可以点赞、投币、收藏喜欢的视频,也算对up主小小的鼓励。此外,不管通过什么途径(插件、客户端等等),想要下载720P及以上的视频,都需要保持登录状态,如果想要下载的是大会员的专属视频,那么需要的就是大会员的cookie。

关于视频流会员鉴权:

  • 获取720P及以上清晰度视频时需要登录(Cookie)
  • 获取高帧率(1080P60)/高码率(1080P+)视频时需要有大会员的账号登录(Cookie)
  • 获取会员专属视频时需要登录(Cookie)
而那些第三方的下载网站,原理无非是网站的管理员持有多个账号,然后用这些账号登录后的cookie来获取视频的下载链接。而显然,一个个账号手动登录再把cookie保存下来是不现实的,所以才需要用爬虫程序来完成登录。

找齐所需参数

首先按照之前提到的套路,用错误的密码进行登录。 登录B站的Form Data需要的参数如下:
  • captchaType
    keep
    goUrl
    为固定参数
  • username
    :用户名,这里填的是手机号
  • password
    :加密过后的密码,看到最后的=可以猜测是加密以后再用Base64编码
  • key
    challenge
    validate
    seccode
    :加密参数,来源暂时未知
再次查阅一下「哔哩哔哩-API收集整理」[1],可以知道这里的
key
challenge
都是从B站的API获取的,而
validate
seccode
是从「极验」获取的。
参数名类型内容必要性备注
captchaTypenum6必要必须为
6
usernamestr用户登录账号必要手机号或邮箱地址
passwordstr加密后的带盐密码必要base64格式
keepbooltrue必要必须为
true
keystr登录秘钥必要从B站API获取
challengestr极验challenge必要从B站API获取
validatestr极验结果必要从极验获取
seccodestr极验结果必要从极验获取
返回Network请求列表,仔细查找,果然在登录前面找到这样一个请求: 获取key和challenge的API)这次请求的响应包含了
key
challenge
,此外还有一个参数
gt
。响应中的key和challenge那么剩下的
validate
seccode
是怎么来的呢?在请求列表中继续往下找,可以找到这样一个请求: 获取验证码的POST请求它的响应包含了刚才登录时弹出的验证码: 响应中包含验证码地址将
image_servers
中的任意一个字符串和
pic
字符串拼接后就得到了验证码的图片地址: 验证码图示按照顺序点击“越”、“橘”、“红”后生成一个
w
参数,再次发起请求得到
validate
seccode
seccode
就是
validate
后面拼接一个
|jordan
): 响应中的validate到这里我们已经知道所有参数的来源了,我画了一张图来表示这个过程: 参数生成流程图

破解加密策略

全局搜索
password
关键字,可以找到11个文件,注意到这个login开头的js文件,相当可疑。 全局搜索password在文件中再次搜索
password
,找到这个地方,不出意外应该就是
password
赋值的地方,打上一个断点: 定位到加密处打上断点以后,重新点击登录, 确认是在这里加密可以看出密码是在
s = this.encryptPassword(this.password)
这行代码中被加密的,加密函数为
this.encryptPassword

进入函数内部

加密函数可以看到这里用到了
JSEncrypt
库,这个库是专门用来做
RSA
加密的,
hash
key
包含在另一个响应中: 响应中的公钥现在分析一下密码加密的逻辑:
  1. 通过GET https://passport.bilibili.com/login?act=getkey&r=随机数这个API来获取
    RSA
    加密的参数
    hash
    key
  2. key
    设置为公钥
  3. 将字符串
    hash
    和原始密码拼接,然后进行RSA加密
由于逻辑比较清晰,可以直接用Python把这个加密流程复现出来。
import random
import base64
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_v1_5 as Cipher_pkcs1_v1_5

password = 'xxxxxx'
r = requests.get('https://passport.bilibili.com/login?act=getkey&r={}'.format(random.random()))
key, _hash = r.json()['key'], r.json()['hash']

rsa_key = RSA.importKey(key)
cipher = Cipher_pkcs1_v1_5.new(rsa_key)  
crypted_password = base64.b64encode(cipher.encrypt((_hash + password).encode("utf-8"))).decode('utf8')  

“破解”验证码

之所以加了引号是因为我走了个后门,并没有实际解决验证码的加密流程——如何获取参数
w
。经过一番网上冲浪,我查到有一个叫「2captcha」[2]的网站专门处理各种各样的验证码,其中就有「极验」。 2captcha提供的服务收费是每1000次请求2.99美元,用支付宝就可以支付,于是我为了试一试这个服务就付了3美元。 优埃斯刀了

超时——hash是有寿命的

我把刚才的想法实现完以后,得到的响应是这样的: 提交超时这个「提交超时」是咋回事呢?我测试了一下输入错误的密码和错误的验证信息都会得到不同的报错信息。 账号密码错误 验证码参数错误于是只能再次求助「哔哩哔哩-API收集整理」[1]了,一番查阅之后查到了如下内容: hash是有寿命的到这里我终于搞明白我之前失败的原因了——每次验证都需要30秒左右的时间,
hash
早就过期了。于是调整一下顺序,先进行验证,再对密码进行加密,然后就成功啦。 成功登录之后整理一下代码,会发在原文链接指向的GitHub仓库中。
旁边的师兄看到我又在不务正业,在写这些确实没什么卵用的代码,教育我说道:“霏霏你这样每天不科研是没法毕业的,你看看人家大师兄顶会一篇接一篇地发,你不惭愧吗?”。我留下了不务正业的眼泪,泪水打湿了板烧鸡腿堡[3]。




内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: