宣告
本文章中所有內容僅供學習交流,抓包內容、敏感網址、資料介面均已做脫敏處理,嚴禁用於商業用途和非法用途,否則由此產生的一切後果均與作者無關,若有侵權,請聯絡我立即刪除!
逆向目標
- 目標:某政務服務網登入介面
- 主頁:
aHR0cHM6Ly9sb2dpbi5obnp3ZncuZ292LmNuL3RhY3MtdWMvbG9naW4vaW5kZXg=
- 介面:
aHR0cHM6Ly9sb2dpbi5obnp3ZncuZ292LmNuL3RhY3MtdWMvbmF0dXJhbE1hbi9sb2dpbk5v
逆向引數:
Form Data:loginNo、loginPwd、code、requestUUID
Request Headers:token
抓包分析
本次逆向目標來源於某位粉絲的求助:
隨便輸入賬號密碼點選登陸,抓包發現介面的 Request Headers 有個加密引數 token,Form Data 裡 loginNo、loginPwd、code、requestUUID 都是經過加密處理了的,loginNo 和 loginPwd 應該就是使用者名稱密碼了,由於登入前需要過一下滑動驗證碼,因此可以猜測另外兩個引數與驗證碼有關,不過僅從抓包來看,另外兩個引數類似於 uuid 的格式,不太像驗證碼的引數。
另外可以注意到登陸前,有兩次 csrfSave 和一次 verCode 的請求,正常請求成功就會返回一個 JSON,裡面有個 data 引數,後面應該是會用到的。
引數逆向
Form Data
先看 Form Data,搜尋任意一個引數,比如 loginNo,很容易在 login.js 裡找到加密的地方,使用者名稱和密碼都經過了 encrypt 這個函式進行加密,backUrl 這個值,是利用 localStorage 屬性,從瀏覽器儲存的鍵值對的資料裡取的,為空也不影響。
跟進 encrypt,可以看到用到了 JSEncrypt,標準的 RSA 加密:
再看看 loginCode,直接搜尋這個值,可以看到是 verCode 這個請求返回的:
然後再看看 requestUUID,其值就是個 UUID,直接在當前檔案(login.js)裡搜尋,可以看到定義的地方,有個 uploadUUID()
方法,就是在設定 UUID 的值,方法裡面是向一個 uploadIdentifier 的介面傳送了 post 請求:
這裡注意,如果你直接全域性搜尋 UUID 的話,還可以在 common.js 裡搜尋到一個方法,經過測試,直接使用這個方法生成一個 uuid 也是可以請求通過的,這網站可能不嚴謹,不會嚴格檢測這個值。
Request Headers
Form Data 解決了,再來看看 Request Headers 裡的 token 引數,由於它存在於請求頭裡,所以我們可以通過 Hook 的方式來查詢其生成的地方:
(function () {
var org = window.XMLHttpRequest.prototype.setRequestHeader;
window.XMLHttpRequest.prototype.setRequestHeader = function (key, value) {
if (key == 'token') {
debugger;
}
return org.apply(this, arguments);
};
})();
這裡我們也可以直接搜尋 token、setRequestHeader 之類的關鍵字,很容易在 common.js 裡找到,當我們點選登陸,會有一個 csrfSave 的請求,返回的 data 值,經過 encrypt 方法加密後就是登陸請求頭的 token 了。
這個 token 引數在很多請求中都會用到,生成方法是一樣的,都是拿 csrfSave 請求返回的 data 經過 RSA 加密後得到的:
另外注意一點的就是,以上所有涉及到網路請求的,Cookie 都需要一個 SESSION 值,這個可以在首次訪問頁面獲取到:
登陸流程
這裡我們理一下登陸的流程:
- 訪問首頁拿 Cookie 中的 SESSION 值;
- 訪問 csrfSave,拿到一個 data 值,經過 RSA 加密得到 token,攜帶 token 訪問 uploadIdentifier,拿到 uuid;
- 訪問 csrfSave,拿到一個 data 值,經過 RSA 加密得到 token,攜帶 token 訪問 verCode,拿到 code;
- 訪問 csrfSave,拿到一個 data 值,經過 RSA 加密得到 token,攜帶 token、uuid、code 和加密後的賬號密碼,訪問 loginNo 登入。
這裡第2步,也可以直接用 Python 或者 JS 生成一個 uuid,網站校驗不嚴格,也可以通過,另外可以看出這個滑塊是假的,通過程式碼可以無視滑塊進行登入。
完整程式碼
GitHub 關注 K 哥爬蟲,持續分享爬蟲相關程式碼!歡迎 star !https://github.com/kgepachong/
以下只演示部分關鍵程式碼,不能直接執行! 完整程式碼倉庫地址:https://github.com/kgepachong...
JavaScript 加密程式碼
/* ==================================
# @Time : 2022-01-11
# @Author : 微信公眾號:K哥爬蟲
# @FileName: encrypt.js
# @Software: PyCharm
# ================================== */
JSEncrypt = require("jsencrypt")
function encrypt(pwd){
var key = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsgDq4OqxuEisnk2F0EJFmw4xKa5IrcqEYHvqxPs2CHEg2kolhfWA2SjNuGAHxyDDE5MLtOvzuXjBx/5YJtc9zj2xR/0moesS+Vi/xtG1tkVaTCba+TV+Y5C61iyr3FGqr+KOD4/XECu0Xky1W9ZmmaFADmZi7+6gO9wjgVpU9aLcBcw/loHOeJrCqjp7pA98hRJRY+MML8MK15mnC4ebooOva+mJlstW6t/1lghR8WNV8cocxgcHHuXBxgns2MlACQbSdJ8c6Z3RQeRZBzyjfey6JCCfbEKouVrWIUuPphBL3OANfgp0B+QG31bapvePTfXU48TYK0M5kE+8LgbbWQIDAQAB";
var encrypt = new JSEncrypt();
encrypt.setPublicKey(key);
var encrypted = encrypt.encrypt(pwd);
return encrypted;
}
// 測試樣例
// console.log(encrypt("15555555555"))
Python 登入程式碼
# ==================================
# @Time : 2022-01-11
# @Author : 微信公眾號:K哥爬蟲
# @FileName: hnzww_login.py
# @Software: PyCharm
# ==================================
import execjs
import requests
cookies = {}
UA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36"
with open("encrypt.js", encoding="utf-8") as f:
js = execjs.compile(f.read())
def csrf_save():
url = "脫敏處理,完整程式碼關注 GitHub:https://github.com/kgepachong/crawler"
headers = {"User-Agent": UA}
response = requests.post(url=url, headers=headers, cookies=cookies).json()
data = response["data"]
return data
def get_session():
url = "脫敏處理,完整程式碼關注 GitHub:https://github.com/kgepachong/crawler"
headers = {"User-Agent": UA}
response = requests.get(url=url, headers=headers)
cookies.update(response.cookies.get_dict())
def get_uuid():
url = "脫敏處理,完整程式碼關注 GitHub:https://github.com/kgepachong/crawler"
headers = {
"User-Agent": UA,
"token": js.call("encrypt", csrf_save())
}
response = requests.post(url=url, headers=headers, cookies=cookies).json()
uuid = response["data"]
return uuid
def ver_code():
url = "脫敏處理,完整程式碼關注 GitHub:https://github.com/kgepachong/crawler"
headers = {
"User-Agent": UA,
"token": js.call("encrypt", csrf_save())
}
response = requests.post(url=url, headers=headers, cookies=cookies).json()
data = response["data"]
return data
def login(phone, pwd, code, uuid):
url = "脫敏處理,完整程式碼關注 GitHub:https://github.com/kgepachong/crawler"
headers = {
"User-Agent": UA,
"token": js.call("encrypt", csrf_save())
}
data = {
"backUrl": "",
"loginNo": js.call("encrypt", phone),
"loginPwd": js.call("encrypt", pwd),
"code": code,
"requestUUID": uuid,
"guoBanAuthCode": ""
}
response = requests.post(url=url, headers=headers, cookies=cookies, data=data)
print(response.json())
def main():
phone = input("請輸入賬號:")
pwd = input("請輸入密碼:")
get_session()
uuid = get_uuid()
code = ver_code()
login(phone, pwd, code, uuid)
if __name__ == '__main__':
main()