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参数。