微博登錄頁面分析
目標網站: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,需要自己去看並且手動輸入的。