@author : Dlivephp
在對Android應用進行Web漏洞測試時,常常遇到一種狀況:HTTP傳輸的數據帶有簽名字段html
處理這種狀況的方法一般是逆向簽名算法,可是若是算法在so中,並且so加殼了,想要逆向出算法也要花很大一番功夫java
還有就是能夠本身編寫app調用so裏的簽名算法,而後對HTTP傳輸的數據進行測試python
這兩種方法都挺麻煩的,而且若是一個app中多處使用了不一樣的簽名/加密算法就更麻煩了android
曾經想寫一個Android上的代理軟件,在Android手機上開啓HTTP/HTTPS代理,在PC端將HTTP/HTTPS流量交給代理,代理軟件調用so裏的加密/簽名算法,git
最後代理軟件將經簽名/加密後的數據提交給服務端。這樣的話就能夠直接用SQLmap之類的工具進行測試了。github
可是在我準備着手寫這麼個東西以前,我發現了個更方便的東西:Frida,使用Frida咱們能夠較簡便地解決上面所說的問題。算法
這裏以一些Demo爲例講解Frida如何簡化Android端應用的安全測試。sql
同時2017 TSCTF的一道Web+Android APP題目爲例,講解如何使用Firda簡化移動端的Web安全測試。(畢竟Web🐶,關注點主要在Web...chrome
Frida是一個動態代碼插樁工具,它可讓你向多種平臺(Windows, Linux, macOS, IOS, Android, QUX)的App插入自定義Javascript代碼片斷
它能夠作什麼
以Mac爲例,其餘系統請自行查看Firda官方文檔
環境:Python3 ,Root過的Nexus 4(Android 4.4)
Frida官方文檔說須要Python3.x的環境,由於Firda最開始是基於Android 4.4開發的,因此建議使用4.4或4.4以上版本的系統
sudo pip3 install frida
驗證是否安裝成功
➜ ~ frida --version 9.1.20
➜ ~ python3 Python 3.6.0 (default, Dec 24 2016, 08:01:42) [GCC 4.2.1 Compatible Apple LLVM 8.0.0 (clang-800.0.42.1)] on darwin Type "help", "copyright", "credits" or "license" for more information. >>> import frida >>>
下載和frida對應版本的frida-server (https://github.com/frida/frida/releases ),解壓後將frida-server push到Android中
➜ ~ adb push ~/0Android/frida/frida-server-9.1.20-android-arm /data/local/tmp/frida-server /Users/dlive/0Android/frida/frida-serv...d. 3.5 MB/s (21555488 bytes in 5.948s)
在adb shell中運行frida-server
root@android:/data/local/tmp # chmod 755 frida-server root@android:/data/local/tmp # ./frida-server &
經測試發現不少狀況下frida很是不穩定,能夠選擇重啓frida-server後從新執行命令
開啓端口轉發
adb forward tcp:27042 tcp:27042 adb forward tcp:27043 tcp:27043
查看正在運行的進程
# Connect Frida to an iPad over USB and list running processes # -U connect to USB device $ frida-ps -U # List running applications $ frida-ps -Ua # List installed applications $ frida-ps -Uai
# 顯示open()函數的調用狀況 frida-trace -i "open" -U com.android.chrome # -f 讓Frida啓動chrome app (let Frida spawn the process) frida-trace -i "open" -U -f com.android.chrome
frida交互式命令行界面(詳細請參考官方文檔)
# Unfortunately, in my case it always lead to getting the app killed automatically after 2 seconds. # This is not what we want. You can either use these 2 seconds to type %resume frida -U -f com.android.chrome # better command # --no-pause automatically start main thread after startup frida -U --no-pause -f com.android.chrome # pass the -f option to Frida to let it spawn the process itself frida -U --no-pause -f com.android.chrome # 向app注入JS frida -U -l example.js com.example.dlive
注入的JS和以後Python中用到的JS的用法相同
console.log("[*] Starting script"); Java.perform(function() { var Activity = Java.use("android.app.Activity"); Activity.onResume.implementation = function () { console.log("[*] onResume() got called!"); this.onResume(); }; });
import frida import sys rdev = frida.get_remote_device() session = rdev.attach("com.tencent.mm") scr = """ Interceptor.attach(Module.findExportByName("libc.so" , "open"), { onEnter: function(args) { send("open("+Memory.readCString(args[0])+","+args[1]+")"); }, onLeave:function(retval){ } }); """ script = session.create_script(scr) def on_message(message ,data): print message script.on("message" , on_message) script.load() sys.stdin.read()
import frida import sys rdev = frida.get_remote_device() session = rdev.attach("com.tencent.mm") scr = """ Java.perform(function () { var ay = Java.use("com.tencent.mm.sdk.platformtools.ay"); ay.pu.implementation = function(){ var type = arguments[0]; send("type="+type); if (type == 2) { return this.pu(type); } else { return 5; } }; }); """ script = session.create_script(scr) def on_message(message ,data): print message script.on("message" , on_message) script.load() sys.stdin.read()
apk只有一個登錄功能,該功能的username字段存在注入
apk中調用了native方法對username和password簽名, 簽名以後獲得的sign和username, password一塊兒發往服務端
使用Flask寫以下中轉腳本,接收username和password, 計算sign
經測試Frida頻繁向app進程注入JS容易掛掉。。。若是掛掉重啓app就好,而後讓sqlmap繼續原來的session
from flask import Flask from flask import request import frida import hashlib import requests import time app = Flask(__name__) sign_result = '' jscode = """ Java.perform(function () { var sign = Java.use("com.example.dlive.tsctf2017.Sign"); var result = sign.sign("%s", "%s"); send(result); }); """ frida_session = frida.get_device_manager().enumerate_devices()[-1].attach("com.example.dlive.tsctf2017") def on_message(message, data): global sign_result sign_result = hashlib.md5(message['payload']).hexdigest() # print sign_result def sign(username, password): global frida_session global jscode username = username.replace('"', '\\"') script = frida_session.create_script(jscode % (username, password)) script.on('message', on_message) script.load() @app.route('/forward', methods=['POST']) def login(): url = 'http://10.101.162.128/sign.php' username = request.form['username'] password = request.form['password'] session = requests.session() sign(username, password) http = session.post(url, data={'username': username, 'password': password, 'sign': sign_result}, timeout=5, allow_redirects=False) return http.content if __name__ == "__main__": app.run()
使用sqlmap注入,目標url是該中轉腳本
中轉註入是稍微方便了很多,可是在注入的時候frida特別容易掛掉QAQ
https://sec.xiaomi.com/article/23 https://github.com/dweinstein/awesome-frida https://www.notsosecure.com/pentesting-android-apps-using-frida/ https://www.codemetrix.net/hacking-android-apps-with-frida-1/ https://www.codemetrix.net/hacking-android-apps-with-frida-2/ https://www.frida.re/ http://www.jianshu.com/p/ca8381d3e094 http://www.voidcn.com/blog/asmcvc/article/p-6240248.html http://wooyun.jozxing.cc/static/drops/tools-5602.html