0x00前言
有技術交流或滲透測試培訓需求的朋友歡迎聯系QQ/VX-547006660,需要代碼審計、滲透測試、紅藍對抗網絡安全相關業務可以看置頂博文
2000人網絡安全交流群,歡迎大佬們來玩
群號820783253
本文首發奇安信攻防社區,作者J0o1ey,大家可以多多支持奇安信攻防社區哦,技術氛圍濃厚,作者待遇優厚~
本期登場的目標雖不是SRC,但是整個漏洞的利用手法很有學習意義。目前在很多大廠的http數據包中都會添加sign值對數據包是否被篡改進行校驗,而sign算法的破解往往是我們漏洞測試的關鍵所在~
本人在一些漏洞挖掘實戰中經常發現在破解sign值后,在測試各類越權,重放等漏洞時一馬平川
今天特此為大家帶來這樣一個精彩的實戰案例。
0x01 背景
學校每學期的體育成績中會有10%來源於某跑步APP的打卡數據,本人作為一個體測只能勉強及格的廢物,自然得想辦法拿到這10分,以防掛科
無奈這個app后台設置的是每學期男生總共要求跑120公里才能完整地拿到10分,非常惡心。
一學期跑120公里,你還不如鯊了我,於是便有了此文對於其app的crack
0x02 初步測試
手機裝好https證書,掛好Burpsuite的代理,隨后我們直接去抓取該APP上傳校園跑步數據時的請求
請求如下
我們發現單次跑步距離對應的POST參數為FormatSportRange,自然二話不說對其進行修改,將0.000修改為5.000
此時悲劇發生了,直接提示認證失敗~
定神細看,發現POST數據末尾有sign簽名....
此時老開發或老安全肯定就知道發生甚么事了,為了讓本文對新手友好一些,下面簡單講一下sign校驗手法
0x03 何為Sign簽名校驗?
Sign簽名校驗法本質上是對客戶端傳輸數據合法性的一種校驗手段
其常用手法為,在用戶客戶端傳輸的數據中額外加上時間戳以及特殊字符,隨后一起得出整體數據的加密值(常用MD5,SHA1等加密算法)
這就導致了用戶在不知曉程序的原始數據生成sign值的方法情況下,若對傳輸到服務端的數據進行篡改,在后端都會鑒權失敗,導致用戶篡改后的數據無效。
0x04 sign值的安全對抗方法
針對有sign簽名值的數據包,本人常用的測試手法有兩個:
1.檢測sign值是否為弱憑據
2.檢測sign值是否可以置空
第一種類型通常有兩種情況
①看sign值是否采用了一些弱加密/編碼方法(例如base64),我們解碼后可以直接看到sign的原始數據。
②測試sign值是否為時間戳/隨機數加密后的密文值,在一些實戰情況中,很多廠商安全開發意識不足,會將sign值的算法直接暴露在前端未加密的js中,或者直接將用戶進行某操作的時間戳進行md5加密后作為sign憑據,導致sign憑據在一段時間內可以通過遍歷時間戳進行猜解
第二種類型就比較好理解,我們直接將sign參數以及值刪掉,看后端程序是否可以不校驗sign直接執行我們傳輸的數據
上述概念可能看起來比較抽象,下面我們繼續來看本案例
0x05 二次測試
我們先嘗試第一種方法,上方添加跑步記錄獲取到的sign值為5ded7f11f080fb5a9d403c9d0963ebaa
拿眼一看,大概率sign值是使用md5加密的,我們隨后對其進行解密
GG了,看樣廠商的安全意識不算太差~沒有使用時間戳或者隨機數加密后的值作為sign,導致sign可以被無腦遍歷猜解
隨后我們嘗試第二種方法,置空sign值
發現依然鑒權認證失敗,gg了。
看樣兩種常規的對抗sign的方法已經廢了,我們只能從app下手了,逆向嘗試去尋找其sign的算法
0x06 逆向apk文件取得其sign值算法
拿到程序apk直接查殼,運氣不錯,apk沒加殼,省了不少功夫
直接將apk文件拖到jadx中,對其進行逆向分析
全局搜素sign,在仔細挨個查看后,成功定位到了其sign生成的關鍵算法
代碼過長,關鍵部分代碼如下
可以看到,其sign值的簽名算法為
創建一個鏈表, 將全部已有的參數加入進去, 然后再加上一些鍵值對(其中timestamp時間戳我們已知,appID,appSecret兩個鍵值對我們均未知)
之后再將全部的鍵值對根據鍵的字母順序進行排序, 之后使用 querystring
方式對鍵值對進行編碼.
最后再對編碼后的字符串求 MD5
值, 就是最終的簽名了, 麻煩的一比
0x07 繼續逆向apk文件獲取未知鍵值對
我們繼續來找appID,appSecret兩個我們未知的鍵值對
發現其獲取方法如下
①appID鍵值對的獲取方法:
如果請求的url是 https://m.xxxxx.com 則為move,否則調用getAppId方法
后面我搜索了一下getAppId方法,發現其本質上是獲取某接口openId的參數值,隨后賦值給AppID
我去burpsuite走了一遍這個apk的業務,然后去http history搜索了一下openId,直接獲取到了我們學校的openId參數值,也就是說我們獲取到了AppID
②appSceret鍵值對獲取方法
在jadx中提示appSecre鍵t對應的值來源於g.b,我們在import的包中成功找到了g.b(即appSecret)
0x08 Nodejs編寫計算sign的Exploit
sign的算法已經有了,未知的鍵值對我們也拿到了,下面就是直接編寫計算sign的exploit的時刻啦~
我們選擇用nodejs來還原整個sign的加密算法(注意,我們將formatSportRange跑步距離改為了5.003)
var parseQueryString = function( url ){
var reg_url =/^[^\?]+\?([\w\W]+)$/,
reg_para=/([^&=]+)=([\w\W]*?)(&|$)/g, //g is very important
arr_url = reg_url.exec( url ),
ret = {};
if( arr_url && arr_url[1] ){
var str_para = arr_url[1],result;
while((result = reg_para.exec(str_para)) != null){
ret[result[1]] = result[2];
}
}
return ret;
}
var url ="www.xxx.com/index.php?userId=34263&runType=2&startTime=1635573372448&endTime=1635573425940&gitudeLatitude=%5B%7B%22latitude%22%3A34.383468%2C%22locationType%22%3A4%2C%22longitude%22%3A108.976148%2C%22puase%22%3Afalse%2C%22speed%22%3A0.0%2C%22time%22%3A1635573372480%7D%2C%7B%22latitude%22%3A34.383484%2C%22locationType%22%3A4%2C%22longitude%22%3A108.976155%2C%22puase%22%3Atrue%2C%22speed%22%3A0.0%2C%22time%22%3A1635573373314%7D%2C%7B%22latitude%22%3A34.383489%2C%22locationType%22%3A2%2C%22longitude%22%3A108.976155%2C%22puase%22%3Atrue%2C%22speed%22%3A1.55%2C%22time%22%3A1635573378344%7D%2C%7B%22latitude%22%3A34.383408%2C%22locationType%22%3A2%2C%22longitude%22%3A108.976193%2C%22puase%22%3Atrue%2C%22speed%22%3A1.55%2C%22time%22%3A1635573398400%7D%2C%7B%22latitude%22%3A34.383479%2C%22locationType%22%3A2%2C%22longitude%22%3A108.976152%2C%22puase%22%3Atrue%2C%22speed%22%3A1.55%2C%22time%22%3A1635573418403%7D%5D&identify=12cc1557-f226-4d19-a01f-58f492620818&formatSportTime=00%3A00%3A01&formatSportRange=5.003&avgspeed=0.0&speed=0%270%22&okPointList=%5B%5D&brand=Xiaomi&model=Mi%2010&system=Android&version=10&appVersion=1.5.73&stepNumbers=%5B0%5D&isFaceStatus=0&points=%5B%5D&uploadType=0×tamp=1635573451940";
var obj = parseQueryString(url);
//console.log(obj) //querystring序列化
const crypto = require('crypto')
const APP_ID = "ec74df4f7ea14f1fb585bbc9f936fc23"
const data = obj
console.log(data)
const timestamp = '1635573451940'
function ff(data, timestamp, appId = APP_ID){
const d = { ...data, appId, timestamp: '1634356066432',appSecret: 'e8167ef026cbc5e456ab837d9d6d9254' }
const ans = crypto.createHash('md5').update(Object.keys(d).sort().map(k => k + '=' + d[k]).join('&')).digest('hex')
console.log("sign is",ans)
}
ff(data, timestamp, APP_ID)
大功告成,我們成功序列化queryString后計算出了sign值,我們現在可以篡改任意數據並根據算法生成偽造的sign值
0x09 測試
我們將原來的formatSportRange跑步距離改為了從0.000修改為5.003,並使用程序生成的sign值
如圖,大功告成,跑步記錄保存成功,我們成功使用偽造的sign簽名增加了一條5.003 km的跑步記錄
返回app查看~
nice!
0x10 后言
遇到可能的漏洞點莫要輕言放棄,再堅持一下,曙光就在前方~
大家如果對“重生之我是賞金獵人”專欄感興趣,歡迎大家多多關注奇安信攻防社區以及vx公眾號-M78安全團隊