记一次app测试过程

1.签名分析

模拟器里抓包发现带了sign参数,抓包的时候发现修改参数时将导致签名校验失败。

1.1 静态分析

先拖到GDA里看看,没发现有壳。

搜索一下sign=关键字

在一个函数中发现了一个拼接sign的代码。

       String[] stringArray = (p0.split("[?]")[1]).split("&");
       int len = stringArray.length;
       int i = 0;
       while (i < len) {
          String[] stringArray1 = (stringArray[i]).split("=");
          if (2 == stringArray1.length) {
             hashMap.put(stringArray1[0], URLDecoder.decode(stringArray1[1]));
          }else {
             hashMap.put(stringArray1[0], null);
          }
          i = i + 1;
       }
    //url取出来,把?后面的截出来,然后按&分割,然后把参数按键值对取出来,参数URLDecoder.decode
    a(map,1)

    public static String a(Map p0,int p1){
       StringBuilder str = "";
       Iterator iterator = i.a(p0).iterator();
       String str1 = "=";
       while (iterator.hasNext()) {
          i$a uoa = iterator.next();
          str = str+uoa.a()+str1+uoa.b()+"&";
       }
       //再从map里再拼回去
       //str name=13112213311&v=4.16.1&device=android&ttid=1&
       str = str+"key"+str1;  //name=13112213311&v=4.16.1&device=android&ttid=1&key=
       String str2 = "db426a9829e4b49a0dcac7b4162da6b6";
       StringBuilder str3 = (p1 == 1)? str2: "219F223CB59B183D3A80708E8553AFA71b49"; //
       str = str+str3;  //name=13112213311&v=4.16.1&device=android&ttid=1&key=db426a9829e4b49a0dcac7b4162da6b6
       Log.d("http", "keyType "+p1);
       str3 = "signParams PARK_SECRET : ";
       if (p1 != 1) {
          str2 = "219F223CB59B183D3A80708E8553AFA71b49";
       }
       Log.d("http", str3+str2);
       return i.d((str).getBytes());  //name=13112213311&v=4.16.1&device=android&ttid=1&key=db426a9829e4b49a0dcac7b4162da6b6
    }

追入i.a方法,代码如下:

    public static String a(Map p0,int p1){
       StringBuilder str = "";
       Iterator iterator = i.a(p0).iterator();
       String str1 = "=";
       while (iterator.hasNext()) {
          i$a uoa = iterator.next();
          str = str+uoa.a()+str1+uoa.b()+"&";
       }
       //再从map里再拼回去
       //str name=13112213311&v=4.16.1&device=android&ttid=1&
       str = str+"key"+str1;  //name=13112213311&v=4.16.1&device=android&ttid=1&key=
       String str2 = "db426a9829e4b49a0dcac7b4162da6b6";
       StringBuilder str3 = (p1 == 1)? str2: "219F223CB59B183D3A80708E8553AFA71b49"; //
       str = str+str3;  //name=13112213311&v=4.16.1&device=android&ttid=1&key=db426a9829e4b49a0dcac7b4162da6b6
       Log.d("http", "keyType "+p1);
       str3 = "signParams PARK_SECRET : ";
       if (p1 != 1) {
          str2 = "219F223CB59B183D3A80708E8553AFA71b49";
       }
       Log.d("http", str3+str2);
       return i.d((str).getBytes());  //name=13112213311&v=4.16.1&device=android&ttid=1&key=db426a9829e4b49a0dcac7b4162da6b6
    }

可以看到,就是把hashmap里存储的方法再取出来,然后拼成uri的形式,并且在后面拼接了一个key参数,key参数是两个固定值,这里调用的时候传入的第二个参数是1,所以固定key为219F223CB59B183D3A80708E8553AFA71b49。 拼接完成后,在最后调用了i.d方法,追进去代码如下:

    public static String d(byte[] p0){
       char[] uocharArray = new char[16]{'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
       try{
          p0 = MessageDigest.getInstance("MD5").digest(p0);
          char[] uocharArray1 = new char[(p0.length * 2)];
          int len = p0.length;
          int i1 = 0;
          for (int i = 0; i < len; i = i + 1) {
             int b = p0[i];
             int i2 = i1 + 1;
             int i3 = b >> 4;
             i3 = i3 & 0x0f;
             uocharArray1[i1] = uocharArray[i3];
             i1 = i2 + 1;
             b = b & 0x0f;
             uocharArray1[i2] = uocharArray[b];
          }
          return new String(uocharArray1);
       }catch(java.lang.Exception e0){
          return "";
       }
    }

调用md5,然后又for循环做了些操作,最后返回一个结果作为Sign,所以sign值长度是个固定的串。

1.2 动态分析

知道了大概的加密流程,可以用hook辅助分析确认一下。开启adb,连接后使用frida查看包名。

使用objection,hook一下md5 digest方法,看看传进去参数和返回结果,验证一下猜想。

android hooking watch class_method java.security.MessageDigest.digest --dump-args --dump-backtrace --dump-return

app界面上发一个短信验证码,burp里抓到了这个包。

可以看到确实抓到了一个这个方法的调用。

打印一下参数字符串,可以看到md5传入的参数如下:

猜想得到印证,key也和之前分析的一样,至此获得加签代码如下:

import java.security.MessageDigest;

public class Test {
    public static String d(byte[] p0){
        char[] uocharArray = new char[]{'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
        try{
            p0 = MessageDigest.getInstance("MD5").digest(p0);
            char[] uocharArray1 = new char[(p0.length * 2)];
            int len = p0.length;
            int i1 = 0;
            for (int i = 0; i < len; i = i + 1) {
                int b = p0[i];
                int i2 = i1 + 1;
                int i3 = b >> 4;
                i3 = i3 & 0x0f;
                uocharArray1[i1] = uocharArray[i3];
                i1 = i2 + 1;
                b = b & 0x0f;
                uocharArray1[i2] = uocharArray[b];
            }
            return new String(uocharArray1);
        }catch(java.lang.Exception e0){
            return "";
        }
    }
    public static void main(String[] args) {
        String s2 = "device=android&phone=11111111119&ttid=1&v=4.16.1&key=db426a9829e4b49a0dcac7b4162da6b6";
        //byte[] bytes  =new byte[]{100,101,118,105,99,101,61,97,110,100,114,111,105,100,38,112,104,111,110,101,61,49,49,49,49,49,49,49,49,49,49,57,38,116,116,105,100,61,49,38,118,61,52,46,49,54,46,49,38,107,101,121,61,100,98,52,50,54,97,57,56,50,57,101,52,98,52,57,97,48,100,99,97,99,55,98,52,49,54,50,100,97,54,98,54};
        //String s = new String(bytes);
        //System.out.println(s.toString());
        System.out.println(d(s2.getBytes()));
    }
}

运行测试,签名与burp中所见一致,分析完成。

2.漏洞测试

测试了所有的相关接口,感觉应该存在越权,无奈userid参数太长了,32位随机哈希,没办法预测,app里也找不到短id对应长id的生成代码。无奈之时,三十年的安全测试经验告诉我,测试一下路径。

Spring boot Actuator 一发入魂。

直接目录扫描是扫不出来的,因为有前置网关在校验sign参数。