微博登录页面分析
目标网站:https://weibo.com/
微博密码加密使用的是rsa算法
微博登陆成功总共涉及到三个步骤:
1、向https://login.sina.com.cn/sso/prelogin.php发送请求来获取密码加密所需要的公钥及一系列下次发送请求所需要的参数
2、向https://login.sina.com.cn/sso/login.php?client=ssologin.js(v1.4.19)这个地址发送请求获取微博通行证
3、向https://passport.weibo.com/wbsso/login地址发请求,实现真正的登录。
具体分析过程
一、获取公钥
抓包:
响应数据:很明显有我们想要的pubkey
需要携带的参数:
js逆向加密参数:su
这里需要注意:有些参数的变量名起的实在是太狗了,就比如这个su,一搜必然是一大堆的结果,一个一个找的话会话费大量的时间,这里我们可以去搜索其他的参数来进行代码的定位,如:这里我们可以搜索rsakt这个参数。
如上图,我们已经定位到了su这个参数的生成代码,分析可以知道这里的su是通过使用bs64对username进行加密得到的
可以打断点测试一下:
用python改写一下:
如此:su就得到了,接下来发请求
结果:pubkey就是下一步需要的公钥,servertime以及nonce等参数是登录时需要携带的参数
{'retcode': 0, 'servertime': 1593777451, 'pcid': 'yf-0b4eb0a3d407b2a5eeffd958aed6345e9507', 'nonce': 'AU561F', 'pubkey': 'EB2A38568661887FA180BDDB5CABD5F21C7BFD59C090CB2D245A87AC253062882729293E5506350508E7F9AA3BB77F4333231490F915F6D63C55FE2F08A49B353F444AD3993CACC02DB784ABBB8E42A9B1BBFFFB38BE18D78E87A0E41B9B8F73A928EE0CCEE1F6739884B9777E4FE9E88A1BBE495927AC4A799B3181D6442443', 'rsakv': '1330428213', 'exectime': 19}
如此我们便完成了第一步,获得了公钥
二、对密码进行加密
接上述步骤,现在我们只是输入了用户名得到了这一系列的数据,那么下一步就是输入密码,输入验证码,然后点击登录了。
抓包分析:
完成上述中的步骤后你会发现这样的两个包,而这两个包的关系实质是:先向第一个包的地址发送请求获取通行证数据,然后带着通行证再向第二个包的地址发送请求获得登录的cookie,我这边第一个包的响应(正常响应是可以看到一个新浪通行证的html页面)是看不到的,可以尝试使用chales抓包看一下。
破解参数:上图中可以看到我们需要破解的参数只有sp,下面我们进行js逆向
同样的我们不搜索sp,上面说到了这一步是通过获得的pubkey进行加密的,所以我们直接搜索pubkey
打断点调试:
上图中可以看到,使用的是rsa加密,知道了加密的参数,通过python进行改写:
sp参数拿到了,看一下参数:
data = { 'entry': 'weibo', 'gateway': '1', 'from': "", 'savestate': '7', 'qrcode_flag': 'false', 'useticket': '1', 'pcid': pcid, 'door': input("code>>>").strip(), # 验证码 'vsnf': '1', 'su': , # 加密的用户名 'service': 'miniblog', 'servertime': servertime, 'nonce': nonce, 'pwencode': 'rsa2', 'rsakv': rsakv, 'sp': , # 加密的密码 'sr': '1920*1080', 'encoding': 'UTF-8', 'prelt': '207', 'url': 'https://weibo.com/ajaxlogin.php?framelogin=1&callback=parent.sinaSSOController.feedBackUrlCallBack', 'returntype': 'TEXT', # 让接口返回json的数据 }
我们现在就缺验证码了,下面来解决验证码的问题:
分析img的url:
https://login.sina.com.cn/cgi/pin.php?r=80069743&s=0&p=yf-a15624b7cb52abefca89f7b96602c931a7d8
解析一下:
url:https://login.sina.com.cn/cgi/pin.php 参数: r:不知道 s:0 p:不知道
找js解析一下:
看到这三个参数是不是很难受,这要是搜索参数,那得找到什么时候去,这时候我们可以思考一下,验证码既然是点一下就换一张图,那是不是说明它向后台发送了ajax请求呢?因此我们以url后面的字段作为搜索条件去搜索.
以变量名进行搜索:
分析上图中js可以发现,r是取得是随机数,且生成r的代码在python中同样适用,而p就是我们第一次请求得到的pcid
发请求获取验证码图片
验证码有了,所有参数都具备了,接下来发请求获取通行证
最后一步:携带通行证,发请求登录
分析参数:
ticket: ST-NzQ1MzYwNzIyOQ==-1593783515-yf-1BA44751BC46AE66190449675C08F7DA-1 # 通行证 ssosavestate: 1625319514 # 10位的时间戳 js分析步骤省略 callback: sinaSSOController.doCrossDomainCallBack scriptId: ssoscript0 client: ssologin.js(v1.4.19) _: 1593783515374 # 13位的时间戳
发请求登录:
完成!
看到这里是不是在想,这写的什么几把玩意啊,这写作水平也太烂了吧,哈哈,没错,上面只是我做的一次记录,方便以后自己查看的,下面我将会给你们最想要的东西:代码(交流学习,禁止商用!!!!!)
import requests import time import rsa import binascii import math import random from urllib import parse from base64 import b64encode class Login(object): def __init__(self, username, password): self.sess = requests.session() self.username = username self.password = password self.headers = { 'Referer': 'https://weibo.com/', 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36' } @property def get_username(self): su = b64encode(parse.quote(self.username).encode('utf8')).decode('utf8') return su def get_pubkey(self): url = 'https://login.sina.com.cn/sso/prelogin.php' params = { 'entry': 'weibo', # 'callback':'sinaSSOController.preloginCallBack', 'su': self.get_username, 'rsakt': 'mod', 'checkpin': '1', 'client': 'ssologin.js(v1.4.19)', '_': round(time.time() * 1000) } try: response = self.sess.get(url=url, headers=self.headers, params=params).json() return response except Exception: print("获取公钥失败!") def get_password(self, public_key_json): pubkey = public_key_json['pubkey'] servertime = public_key_json['servertime'] nonce = public_key_json['nonce'] public_key = rsa.PublicKey(int(pubkey, 16), int('10001', 16)) password_str = str(servertime) + '\t' + str(nonce) + '\n' + self.password password = binascii.b2a_hex(rsa.encrypt(password_str.encode('utf8'), public_key)).decode('utf8') return password def pre_login(self, public_key_json, password): """ 获取通行证 :param public_key: get_pubkey()函数获得的json数据 :param password: 加密后的密码 :return: """ url = 'https://login.sina.com.cn/sso/login.php?client=ssologin.js(v1.4.19)' servertime = public_key_json['servertime'] pcid = public_key_json['pcid'] nonce = public_key_json['nonce'] rsakv = public_key_json['rsakv'] # 验证码 img_code = self.sess.get( url=f'https://login.sina.com.cn/cgi/pin.php?r={math.floor(random.random() * 1e8)}&s=0&p={pcid}', headers=self.headers).content with open('./a.png', 'wb') as fp: fp.write(img_code) data = { 'entry': 'weibo', 'gateway': '1', 'from': "", 'savestate': '7', 'qrcode_flag': 'false', 'useticket': '1', 'pcid': pcid, 'door': input("code>>>").strip(), 'vsnf': '1', 'su': self.get_username, 'service': 'miniblog', 'servertime': servertime, 'nonce': nonce, 'pwencode': 'rsa2', 'rsakv': rsakv, 'sp': password, 'sr': '1920*1080', 'encoding': 'UTF-8', 'prelt': '207', 'url': 'https://weibo.com/ajaxlogin.php?framelogin=1&callback=parent.sinaSSOController.feedBackUrlCallBack', 'returntype': 'TEXT', } response = self.sess.post(url=url, headers=self.headers, data=data).json() return response def login(self, ticket_json): """ :param ticket_json: 获得通行证的json数据 :return: """ url = 'https://passport.weibo.com/wbsso/login' try: tket = ticket_json['ticket'] except KeyError: raise KeyError("验证码输错啦") data = { 'ticket': tket, 'ssosavestate': round(time.time()), 'callback': 'sinaSSOController.doCrossDomainCallBack', 'scriptId': 'ssoscript0', 'client': 'ssologin.js(v1.4.19)', '_': round(time.time() * 1000) } response = self.sess.post(url=url, headers=self.headers, data=data) print(response.text) def main(self): """ 主函数 :return: """ public_key_json = self.get_pubkey() # 第一次发请求,检测账号,获取公钥等相关数据 password = self.get_password(public_key_json) # 对密码进行加密 ticket = self.pre_login(public_key_json, password) # 发请求获取通行证 self.login(ticket) # response = self.sess.get('https://weibo.com/u/7453607229/home').text # 携带登录的数据去测试能否得到正确的页面 user = Login('账号', '密码') user.main()
注:验证码图片是当前目录下的a.png,需要自己去看并且手动输入的。