記一次APP測試的爬坑經(jīng)歷

0x01 閃退

開始在接到任務(wù)的時(shí)候,由于是第一次接觸APP測試,俺興高采烈的將其裝進(jìn)咱的夜神模擬器里面,準(zhǔn)備學(xué)習(xí)一番。結(jié)果,輕觸APP,它輕輕地來了,卻又輕輕地走了(對沒錯(cuò),它無腦閃退。。。),我當(dāng)時(shí)的心情是這樣的:!@#¥@%¥#@%&#%&????。

隨后各位大佬告訴我,可以更換真機(jī)、模擬器、安卓版本各種嘗試。

在經(jīng)過一系列的測試后,將夜神模擬器的版本換為 Android 7 成功解決閃退問題????。

0x02 抓包

為了抓取APP的數(shù)據(jù)包,我將burp的證書裝入我的模擬器中。但是,卻一直抓不到,在做了一定功課了解后,知道了可以采用 Xposed + JustTrustMe 來突破 SSL PINNING 。

image

但是!但是!但是!模擬器在未裝 JustTrustMe 時(shí)候無法抓包,而在裝上以后,整個(gè)網(wǎng)絡(luò)環(huán)境,不,可,用。打擾了~

image

還好,感謝雄哥給的 JustTrustMePlus (只令某一個(gè)APP強(qiáng)制信任證書)。

image
image

再用 Burp 進(jìn)行抓包的時(shí)候,就能成功抓取到APP的數(shù)據(jù)包了。

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

0x03 傳輸加解密

再次抓包時(shí),發(fā)現(xiàn)傳輸過程存在加解密。

image

起初,使用阿雄教的方法,用 Xserver 去動態(tài) hook 加解密(函數(shù)這個(gè)模塊需要知道其加解密函數(shù)),可是卻沒有任何反應(yīng)。

image

無能為力的我,便去求同事幫忙逆向一下,卻是無果 /(ㄒoㄒ)/~~。

最后,在老大的指導(dǎo)下,請出了 Inspecakge 。

Inspeckage:是一個(gè)用于提供幫助Android應(yīng)用程序動態(tài)分析的工具。通過對Android API的函數(shù)使用hook技術(shù),幫助用戶了解應(yīng)用程序在運(yùn)行時(shí)的行為。Gayhub地址:https://github.com/ac-pm/Inspeckage

操作如下:

1、勾選需要調(diào)試的APP

image

2、本機(jī)進(jìn)行端口轉(zhuǎn)發(fā)

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

Tips:
62001是夜神模擬器在本機(jī)的默認(rèn)端口
而關(guān)于模擬器多開的問題,端口計(jì)算公式如下:

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

當(dāng)然,也可以通過命令行查看端口

image

3、打開Inspeckage

image

將上方的 OFF 勾選為 ON 就能成功 hook 函數(shù)了

4、配合Burp分析加解密

在抓取數(shù)據(jù)包后,我發(fā)現(xiàn),POST的數(shù)據(jù)使用 0x1d 作為分隔符將其分為三段

image

多抓了幾個(gè)數(shù)據(jù)包后,綜合分析出,BurpSuite截獲的POST數(shù)據(jù)包被 0x1d 字符分成了三段M1,M2M,M3,其中:

M2的動態(tài)生成過程對應(yīng) Inspeckage 中的 3,其使用了 AES CBC 模式進(jìn)行加解密,且 IV 初始偏移量 為一個(gè)固定值(Inspecakge中能看到)。

M3可以看到通過X算法加密前,恰好為第二段的密鑰(第三段的密鑰是每次請求都會動態(tài)生成的,我并沒有解密出第三段的加密方法,只能每次使用Inspeckage查看密鑰,如果有大佬了解的,請不吝賜教)

image

而M1,使用了密鑰+明文再經(jīng)過一次 MD5 散列的處理,在后端做篡改的校驗(yàn)

image

流程圖如下:

image.png
image.png

5、編寫小腳本

分析出結(jié)果后,便能根據(jù)思路寫一個(gè)腳本出來簡化操作了

解密測試:

image

修改參數(shù)再加密的測試(此處進(jìn)行越權(quán)測試):

image

可以順利開始測試了,舒服了~~~~

0x04 后續(xù)思考

在后續(xù)進(jìn)行測試的時(shí)候,由于每次請求都需要把密鑰和密鑰解密前的密文輸入程序,導(dǎo)致整個(gè)測試流程較為繁瑣,進(jìn)度較慢。(由于不會寫B(tài)urp插件,手法比較笨)

于是回想了下整個(gè)加解密流程,發(fā)現(xiàn)關(guān)鍵是:

1、密鑰 Key1 是本地通過代碼隨機(jī)生成的,經(jīng)過加密流程生成 PostData 。

2、服務(wù)器端經(jīng)過代碼解密出Key2 ,不和客戶端生成的 Key1 做校驗(yàn),只要能成功解密出數(shù)據(jù)就行。

也就是說,我們可以將 Key2 寫為固定值,然后每次只需要輸入需要加密的數(shù)據(jù)就行了。當(dāng)然,解密過程還是需要我們輸入 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 總結(jié)

通過本次APP的測試,總的來說是艱辛卻收獲了很多,但最后就結(jié)果來講有兩點(diǎn)不太完美的地方:

1、對于M3并未成功破解,否則可以寫出更加精簡的代碼

2、由于不會寫B(tài)urp插件,所以操作過程還是比較繁瑣(下次一定學(xué)著寫????)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容