记一次APP测试的爬坑经历

0x01 闪退

开始在接到任务的时候,由于是第一次接触APP测试,俺兴高采烈的将其装进咱的夜神模拟器里面,准备学习一番。结果,轻触APP,它轻轻地来了,却又轻轻地走了(对没错,它无脑闪退。。。),我当时的心情是这样的:!@#¥@%¥#@%&#%&🤮🤮。

随后各位大佬告诉我,可以更换真机、模拟器、安卓版本各种尝试。

在经过一系列的测试后,将夜神模拟器的版本换为 Android 7 成功解决闪退问题😃😃。

0x02 抓包

为了抓取APP的数据包,我将burp的证书装入我的模拟器中。但是,却一直抓不到,在做了一定功课了解后,知道了可以采用 Xposed + JustTrustMe 来突破 SSL PINNING

image

但是!但是!但是!模拟器在未装 JustTrustMe 时候无法抓包,而在装上以后,整个网络环境,不,可,用。打扰了~

image

还好,感谢雄哥给的 JustTrustMePlus (只令某一个APP强制信任证书)。

image
image

再用 Burp 进行抓包的时候,就能成功抓取到APP的数据包了。

嗯~ o( ̄▽ ̄)o,舒服了...

0x03 传输加解密

再次抓包时,发现传输过程存在加解密。

image

起初,使用阿雄教的方法,用 Xserver 去动态 hook 加解密(函数这个模块需要知道其加解密函数),可是却没有任何反应。

image

无能为力的我,便去求同事帮忙逆向一下,却是无果 /(ㄒoㄒ)/~~。

最后,在老大的指导下,请出了 Inspecakge

Inspeckage:是一个用于提供帮助Android应用程序动态分析的工具。通过对Android API的函数使用hook技术,帮助用户了解应用程序在运行时的行为。Gayhub地址:https://github.com/ac-pm/Inspeckage

操作如下:

1、勾选需要调试的APP

image

2、本机进行端口转发

adb connect 127.0.0.1:62001
adb forward tcp:8008 tcp:8008
image

Tips:
62001是夜神模拟器在本机的默认端口
而关于模拟器多开的问题,端口计算公式如下:

nox_i = 62024+i
例如:
nox_1 = 62025
nox_2 = 62026
nox_3 = 62027

当然,也可以通过命令行查看端口

image

3、打开Inspeckage

image

将上方的 OFF 勾选为 ON 就能成功 hook 函数了

4、配合Burp分析加解密

在抓取数据包后,我发现,POST的数据使用 0x1d 作为分隔符将其分为三段

image

多抓了几个数据包后,综合分析出,BurpSuite截获的POST数据包被 0x1d 字符分成了三段M1,M2M,M3,其中:

M2的动态生成过程对应 Inspeckage 中的 3,其使用了 AES CBC 模式进行加解密,且 IV 初始偏移量 为一个固定值(Inspecakge中能看到)。

M3可以看到通过X算法加密前,恰好为第二段的密钥(第三段的密钥是每次请求都会动态生成的,我并没有解密出第三段的加密方法,只能每次使用Inspeckage查看密钥,如果有大佬了解的,请不吝赐教)

image

而M1,使用了密钥+明文再经过一次 MD5 散列的处理,在后端做篡改的校验

image

流程图如下:

image.png
image.png

5、编写小脚本

分析出结果后,便能根据思路写一个脚本出来简化操作了

解密测试:

image

修改参数再加密的测试(此处进行越权测试):

image

可以顺利开始测试了,舒服了~~~~

0x04 后续思考

在后续进行测试的时候,由于每次请求都需要把密钥和密钥解密前的密文输入程序,导致整个测试流程较为繁琐,进度较慢。(由于不会写Burp插件,手法比较笨)

于是回想了下整个加解密流程,发现关键是:

1、密钥 Key1 是本地通过代码随机生成的,经过加密流程生成 PostData

2、服务器端经过代码解密出Key2 ,不和客户端生成的 Key1 做校验,只要能成功解密出数据就行。

也就是说,我们可以将 Key2 写为固定值,然后每次只需要输入需要加密的数据就行了。当然,解密过程还是需要我们输入 Key1 的。

于是我取了某次在 Inspeckage 中取到的 KeyX(Key) 写为定值,修改了原本的代码,简化了操作。

package com.encrypt;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

import sun.misc.BASE64Encoder;
import sun.misc.BASE64Decoder;
import org.apache.commons.codec.digest.DigestUtils;

import java.util.Arrays;
import java.util.List;
import java.util.Scanner;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

class AesCBC {

    //IV
    private static String InitV="XXXXXXXXXXXXXXXX";

    public void setKey(String key) {
        this.key = key;
    }
    public void setE(String e) {
        E = e;
    }
    public void setD(String d) {
        D = d;
    }
    public String getKey() {
        return key;
    }
    public String getE() {
        return E;
    }
    public String getD() {
        return D;
    }

    private String key="";
    private String E="";
    private String D="";

    /**
     * FUNCTION: ENCRYPT
     * */
    public String encrypt() throws Exception{
        Cipher cipher=Cipher.getInstance("AES/CBC/PKCS5Padding");
        SecretKeySpec sks=new SecretKeySpec(getKey().getBytes(),"AES");
        IvParameterSpec iv=new IvParameterSpec(InitV.getBytes());
        cipher.init(Cipher.ENCRYPT_MODE,sks,iv);
        byte[] ctext=cipher.doFinal(getE().getBytes());
        return new BASE64Encoder().encode(ctext);
    }

    /**
     * FUNCTION: DECRYPT
     * */
    public String decrypt() throws Exception{
        Cipher cipher=Cipher.getInstance("AES/CBC/PKCS5Padding");
        SecretKeySpec sks=new SecretKeySpec(getKey().getBytes(),"AES");
        IvParameterSpec iv=new IvParameterSpec(InitV.getBytes());
        cipher.init(Cipher.DECRYPT_MODE,sks,iv);
        byte[] ptext=cipher.doFinal(new BASE64Decoder().decodeBuffer(getD()));
        return new String(ptext);
    }


    public static void main(String[] args) throws Exception {
        //开始
        System.out.println("--------------------~Strat~--------------------");
        System.out.println("***********************************************");
        AesCBC ac=new AesCBC();
        Scanner sc = new Scanner(System.in);

        while(true){
            System.out.print("Please Choice Mode【D(ecrypt)/E(ncrpty)】: ");
            String mode=sc.nextLine().toUpperCase().trim();

            if(mode.equals("D")){
                //-------解密-------
                //输入key
                System.out.println("--------------------AESKey~--------------------");
                System.out.print("Key: ");
                String key1=sc.nextLine().trim();
                ac.setKey(key1);
                System.out.print("CipherText: ");
                String D=sc.nextLine().trim();
                ac.setD(D);
                System.out.println("\n--------------------Decrypt~--------------------");
                String ptext=ac.decrypt();
                System.out.println("Decrypt Result: "+ptext);
                System.out.println("\n--------------------Finished--------------------\n\n");
            }else if(mode.equals("E")){
                //-------加密-------
                //key为定值
                System.out.println("--------------------AESKey~--------------------");
                String K="(HqttsSdHpJHTwkF7 , WUy7AghAGVmCHcdC78jW+wTbCi2SvJ7n3Ig6Mmbi+Qdkn5TL79ISZ8XnIA03KRDhtZmPltbJSCQaIw8TbkkzY7wK/SpUmK0+wZV4feYDf+4RIAHCQyA+bWXx1dvdJT00toNyrQSCuORsCh2VMusmJ6XhyI1MrYNDY3m+1poNqes=)";
                System.out.println("HqttsSdHpJHTwkF7\n");
                String str="";
                //正则匹配括号里的,并用逗号分割
                Pattern pattern = Pattern.compile("(?<=\\()[^\\)]+");
                Matcher matcher = pattern.matcher(K);
                while(matcher.find()){
                    str=matcher.group();
                }
                List<String> keys = Arrays.asList(str.split(" , "));
                String key1=keys.get(0);
                String key2=keys.get(1);
                ac.setKey(key1);
                System.out.print("PlainText: ");
                String E=sc.nextLine().trim();
                ac.setE(E);
                System.out.println("\n--------------------Encrypt~--------------------");
                String ctext=ac.encrypt();
                String hashStr=ac.getKey()+ac.getE();
                String hashedStr=DigestUtils.md5Hex(hashStr);
                char sp=29;
                System.out.println("BURP Result: "+ hashedStr+sp+ctext+sp+key2);
                System.out.println("\n--------------------Finished--------------------\n\n");
            }else if(mode.equals("0")){
                System.out.println("\n***********************************************");
                System.out.println("--------------------the End--------------------");
                System.exit(0);
            }

        }
    }
}

0x05 总结

通过本次APP的测试,总的来说是艰辛却收获了很多,但最后就结果来讲有两点不太完美的地方:

1、对于M3并未成功破解,否则可以写出更加精简的代码

2、由于不会写Burp插件,所以操作过程还是比较繁琐(下次一定学着写💪💪)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

友情链接更多精彩内容