Frida主动调用爆破密码
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 密码爆破效果
~欢迎各位师傅关注我的公众号哟~