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