Frida主動調用爆破密碼
0x00 案例下載地址
asktaosec/AndroidApplicationSecurity
0x01 案例分析
1 APP功能分析
安裝運行目標APP,經分析,該APP啟動后展現的是一個登錄界面,用戶在輸入框中輸入PIN,再點擊VERIFY PIN進行驗證,並提示驗證結果:

2 需要達到的目的
在平時的APP滲透測試過程當中,如果遇到一個登錄入口,首先想到的可能是通過抓取登錄操作向服務器傳輸的數據包,通過對數據包中的密碼字段進行爆破。但在這個案例中,遺憾的是APP並未向服務器發起網絡請求,而是通過客戶端本身對用戶的輸入進行判斷並返回正確的結果。因此,接下來就是學習如何在這種情況通過frida對密碼進行暴力破解,得到正確的返回結果。
3 反編譯分析目標APP
3.1 使用jadx反編譯目標APP

3.2 搜索特定字符串
當在app中點擊按鈕的時候,app會彈出響應的驗證結果提示:“Unfortunately,not the right PIN 😦” ,在jadx反編譯代碼中搜索字符串,目的是盡可能定位到離關鍵位置近的地方,沒有結果。
換個工具,使用androidkiller反編譯apk,並搜索提示的字符串,如下:

復制字符串資源對應的name:dialog_failure 到jadx反編譯得到的源碼中進行檢索,結果如下:


3.3 靜態代碼分析
以下為通過提示字符傳定位到的一個函數:
public void verifyPasswordClick(View view) {
if (!Verifier.verifyPassword(this, this.txPassword.getText().toString())) {
Toast.makeText(this, R.string.dialog_failure, 1).show();
} else {
showSuccessDialog();
}
}
閱讀代碼可知,函數的內部是一個if...else的條件判斷語句,當if條件為真時,表示我們輸入的PIN錯誤,返回驗證失敗的提示。反之,則表示驗證成功。
因此,關鍵的驗證邏輯可以判斷出應該在Verifier.verifyPassword()方法中,跟蹤此方法,查看內部實現的代碼邏輯,如下:
package org.teamsik.ahe17.qualification;
import android.content.Context;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public class Verifier {
private Verifier() {
}
public static boolean verifyPassword(Context context, String input) {
if (input.length() != 4) {
return false;
}
byte[] v = encodePassword(input);
byte[] p = "09042ec2c2c08c4cbece042681caf1d13984f24a".getBytes();
if (v.length != p.length) {
return false;
}
for (int i = 0; i < v.length; i++) {
if (v[i] != p[i]) {
return false;
}
}
return true;
}
private static byte[] encodePassword(String input) {
byte[] SALT = {95, 35, 83, 73, 75, 35, 95};
try {
StringBuilder sb = new StringBuilder();
sb.append((char) SALT[0]);
sb.append((char) SALT[1]);
for (int i = 0; i < input.length(); i++) {
sb.append((char) input.getBytes("iso-8859-1")[i]);
sb.append((char) SALT[i + 2]);
}
sb.append((char) SALT[6]);
byte[] bArr = new byte[0];
return SHA1(sb.toString()).getBytes("iso-8859-1");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
return null;
}
}
private static String convertToHex(byte[] data) {
StringBuffer buf = new StringBuffer();
for (int i = 0; i < data.length; i++) {
int halfbyte = (data[i] >>> 4) & 15;
int two_halfs = 0;
while (true) {
if (halfbyte < 0 || halfbyte > 9) {
buf.append((char) ((halfbyte - 10) + 97));
} else {
buf.append((char) (halfbyte + 48));
}
halfbyte = data[i] & 15;
int two_halfs2 = two_halfs + 1;
if (two_halfs >= 1) {
break;
}
two_halfs = two_halfs2;
}
}
return buf.toString();
}
private static String SHA1(String text) {
try {
MessageDigest md = MessageDigest.getInstance("SHA-1");
byte[] bArr = new byte[40];
md.update(text.getBytes("iso-8859-1"), 0, text.length());
return convertToHex(md.digest());
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e2) {
e2.printStackTrace();
}
return null;
}
}
通過分析,我們可以知道verifyPassword的實現功能如下:
- 判斷PIN碼的長度是否為4位,不等於4位則驗證失敗。
- 將用戶輸入的4位PIN碼經encodePassword()函數轉換后與“09042ec2c2c08c4cbece042681caf1d13984f24a”進行比對,如果相同,則表示輸入正確。
0x03 Frida主動調用實現密碼爆破
既然我們知道了verifyPassword()函數的實現功能,也不難得出,當我們如果輸入正確的PIN的話,經encodePassword()處理過后得到的結果也一定是和“09042ec2c2c08c4cbece042681caf1d13984f24a”相同的字符串,因此,接下來就直接通過frida 主動調用encodePassword()來暴力破解得到結果並與“09042ec2c2c08c4cbece042681caf1d13984f24a”進行比較。
1 實現代碼
frida -U org.teamsik.ahe17.qualification.easy -l brutefroce.js
function main(){
console.log("Entering the Script!"); /*打印日志,用來確定是否進入main函數開始執行*/
Java.perform(function x(){
console.log("Inside java perform");
var Verifier = Java.use("org.teamsik.ahe17.qualification.Verifier");
var stringClass = Java.use("java.lang.String");
var p = stringClass.$new("09042ec2c2c08c4cbece042681caf1d13984f24a");
var pSign = p.getBytes();
for (var i = 0; i < 10000; i++){
var v = "";
if (i < 10){
v = "000" + stringClass.$new(String(i));
}else if (i>=10&&i<100) {
v = "00" + stringClass.$new(String(i));
}else if (i>+100&&i<1000) {
v = "0" + stringClass.$new(String(i));
}else{
v = stringClass.$new(String(i));
}
var vSign = Verifier.encodePassword(v);
if (parseInt(stringClass.$new(pSign))== parseInt(stringClass.$new(vSign)) ) {
console.log("yes: " + v)
break
}
console.log("not :" + v)
}
})
}
setImmediate(main)
2 密碼爆破效果


