一、知识点:
单向认证:服务端向客户端发送CA证书,并用证书中的公钥对随机数加密使其成为通信秘钥
双向认证:服务端和客户端都有CA证书,并向对方发送,服务端选择加密方案让客户端进行加密密钥。
以上认证可以使用中间人抓取数据。为了防止中间人抓取又出现了:SSL-pinning
SSL-pinning有两种方式
证书锁定(Certificate Pinning):客户端代码内置仅接受指定域名的证书.
公钥锁定(Public Key Pinning):提取证书中的公钥并内置到客户端中,通过与服务器对比公钥值来验证连接的正确性.
二、实战:
打开charles抓包:
返回信息是 SSL握手失败,那么最大的可能就是使用了SLL-pinning。
那么我们尝试把证书down下来。frida代码如下:
function hook_KeyStore_load() {
Java.perform(function () {
var ByteString = Java.use("com.android.okhttp.okio.ByteString");
var myArray=new Array(1024);
var i = 0
for (i = 0; i < myArray.length; i++) {
myArray[i]= 0x0;
}
var buffer = Java.array('byte',myArray);
var StringClass = Java.use("java.lang.String");
var KeyStore = Java.use("java.security.KeyStore");
KeyStore.load.overload('java.security.KeyStore$LoadStoreParameter').implementation = function (arg0) {
console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()));
console.log("KeyStore.load1:", arg0);
this.load(arg0);
};
KeyStore.load.overload('java.io.InputStream', '[C').implementation = function (arg0, arg1) {
console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()));
console.log("KeyStore.load2:", arg0, arg1 ? StringClass.$new(arg1) : null);
if (arg0){
var file = Java.use("java.io.File").$new("/sdcard/Download/"+ String(arg0)+".p12");
var out = Java.use("java.io.FileOutputStream").$new(file);
var r;
while( (r = arg0.read(buffer)) > 0){
out.write(buffer,0,r)
}
console.log("save success!")
out.close()
}
this.load(arg0, arg1);
};
console.log("hook_KeyStore_load...");
});
}
把证书pull到电脑,使用KeyStore Explorer工具转换为.p12文件,此处保存的虽然也是.p12但真实文件可能并不是.p12。所以此处需要转换一下。
此处转换后色.p12文件导入charles一直报密码错误。但是密码真的没有错。但是就是无法导入,没有找到具体原因,所以我们转换为了PEM证书,并成功导入。
但是我们添加完证书之后还是抓取不到数据
此处我们猜测是将服务器证书与APP预埋证书做对比,来完成强校验。
那么我们直接hook它会用到的API
function hook_ssl() {
Java.perform(function() {
var ClassName = "com.android.org.conscrypt.Platform";
var Platform = Java.use(ClassName);
var targetMethod = "checkServerTrusted";
var len = Platform[targetMethod].overloads.length;
console.log(len);
for(var i = 0; i < len; ++i) {
Platform[targetMethod].overloads[i].implementation = function () {
console.log("class:", ClassName, "target:", targetMethod, " i:", i, arguments);
//printStack(ClassName + "." + targetMethod);
}
}
});
}
再次抓包:
成功抓取请求。
让我们来循环出flag吧
import requests
from requests.packages.urllib3.exceptions import InsecureRequestWarning
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)#跳过警告信息
def main():
head = {
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': '6',
'Host': '180.76.60.244:18443',
'Connection': 'Keep-Alive',
'Accept-Encoding': 'gzip',
'User-Agent': 'okhttp/3.14.9',
}
count = 0
for i in range(1,101):
payload = {
'page': str(i),
}
print(i)
r = requests.post("https://180.76.60.244:18443/api/app5", data=payload,cert="1.pem",headers=head,verify=False,allow_redirects=False )
# print(r.status_code,r.url,i)
data = r.json()['data']
for v in data:
# print("v:",v['value'])
count += int(v['value'])
print("count:",count)
if __name__ == '__main__':
main()