A-A+

啃得起 APP 数据传输 内部加密签名signatureCode方法分析

2020年09月17日 12:17 漏洞安全 评论 19 条 共6026字 (阅读5,934 views次)
Image

【注意:此文章为博主原创文章!转载需注意,请带原文链接,至少也要是txt格式!】

因对方APP已经更换加密方式、证书,所以本文方法已失效,所以公布过程大家学习。

问题的重点在某APP进行了数据包签名加密的验证,然后就是没办法更改数据包,没办法重放数据包。那针对如何加密的过程分析如下:

 

首先针对此款APP进行数据抓包,当然了,正常你是没办法抓包的,所以想看抓包过程请参考:Frida  Burp抓包APP ,下面是抓取的数据包,请仔细看图,如图分析:

Image

burp 数据包对比分析

既然这样,那么就收集到了几个关键字

kbappkwle8K1Mhlc、kbcts、kbck、kbsv

既然这样已经收集这4个关键字,我们反编译APP,然后解包其中最重要的文件:classes.dex、classes2.dex、classes3.dex  解包之后我们搜索这几个关键词。

Image

关键词kbappkwle8K1Mhlc 搜索结果

通过这里呢,发现了APP的核心配置文件,里面有很多关键的东西呢。

路径是:com.hp.smartmobile.config.ServiceConfig

我们再次搜索其它关键词,就发现了重要的传输过程中的加密方法及函数。如下图,请仔细看图:

Image

关键词 kbcts 搜索结果详细分析 (所有图片点击可查看大图)

所有的加密函数,传输方法都在com.smartmobilevpay.android.http.HttpTools 这个路径下的文件中,全局的传输方法是doHttpGetPost、doHttpGetPost,而在这个传输方法中,使用了加密验证的方式,下面我们拿出重点的加密方法函数如下,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
//这里是Get方法的加密函数
    public static Map<String, String> getSignGetMap(final String s, JSONObject jsonObject) {
        final HashMap<String, String> hashMap = new HashMap<String, String>();
        JSONObject jsonObject2;
        final Object o = jsonObject2 = (JSONObject)"";
        if (jsonObject != null) {
            TreeMap<String, String> treeMap;
            try {
                final Iterator keys = jsonObject.keys();
                treeMap = new TreeMap<String, String>();
                while (keys.hasNext()) {
                    final String s2 = keys.next();
                    treeMap.put(s2, jsonObject.getString(s2));
                }
            }
            catch (Exception ex) {
                ex.printStackTrace();
                return hashMap;
            }
            final Iterator iterator = treeMap.keySet().iterator();
            jsonObject = (JSONObject)o;
            while (true) {
                jsonObject2 = jsonObject;
                if (!iterator.hasNext()) {
                    break;
                }
                final String s3 = iterator.next();
                try {
                    final String s4 = treeMap.get(s3);
                    if (((String)jsonObject).equals("")) {
                        jsonObject = (JSONObject)(s3 + "=" + s4);
                    }
                    else {
                        jsonObject = (JSONObject)((String)jsonObject + "&" + s3 + "=" + s4);
                    }
                }
                catch (Exception ex2) {
                    ex2.printStackTrace();
                }
            }
        }
        final long time = new Date().getTime();
        hashMap.put("kbck", SmartSdkManager.getInstance().getClientKey());
        hashMap.put("kbcts", time + "");
        hashMap.put("kbsv", EncryptUtils.stringToMD5(EncryptUtils.signatureGetCode(time, s, (String)jsonObject2)));
        return hashMap;
    }
 
    private void removeRequest(final long n) {
        synchronized (this) {
            if (this.mRequestIds == null) {
                this.mRequestIds = new HashSet();
            }
            this.mRequestIds.remove(n);
        }
    }
 
//这里是POST方式签名加密
    public Map<String, String> getSignPostMap(final String s, final JSONObject jsonObject) {
        final HashMap<String, String> hashMap = new HashMap<String, String>();
        try {
            final String string = jsonObject.toString();
            final long time = new Date().getTime();
            hashMap.put("kbck", SmartSdkManager.getInstance().getClientKey());
            hashMap.put("kbcts", time + "");
            hashMap.put("kbsv", EncryptUtils.stringToMD5(EncryptUtils.signaturePostCode(time, s, string)));
            return hashMap;
        }
        catch (Exception ex) {
            ex.printStackTrace();
            return hashMap;
        }
    }

到这里我们就明白了很多,首先kbck的值仅仅是一个配置文件的字符串,kbcts是当前时间的时间戳,重点的就是kbsv。
kbsv这里首先是执行 EncryptUtils.signaturePostCode(l, paramString, str) l是时间戳,其它两个还需要继续看。先看一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
//这里是Get方法的kbsv加密
public static String signatureGetCode(long paramLong, String paramString1, String paramString2) {
    paramString1 = SmartSdkManager.getInstance().getClientKey() + '\t' + SmartSdkManager.getInstance().getClientSec() + '\t' + paramLong + '\t' + paramString1 + '\t' + paramString2;
    if (SmartSdkManager.getInstance().isOpenLog())
      Log.i("applog", "------forMD5," + paramString1); 
    return paramString1;
  }
 
//这里是Post方法的kbsv加密  注意上面的函数调用的就是这里signaturePostCode
public static String signaturePostCode(long paramLong, String paramString1, String paramString2) {
    paramString1 = SmartSdkManager.getInstance().getClientKey() + '\t' + SmartSdkManager.getInstance().getClientSec() + '\t' + paramLong + '\t' + paramString1 + '\t' + '\t' + paramString2;
    if (SmartSdkManager.getInstance().isOpenLog())
      Log.i("applog", "------forMD5," + paramString1); 
    return paramString1;
  }
 
//这里是最外层的MD5加密EncryptUtils.stringToMD5(EncryptUtils.signaturePostCode(l, paramString, str)))
public static String stringToMD5(String paramString) {
    try {
      MessageDigest messageDigest = MessageDigest.getInstance("MD5");
      try {
        byte[] arrayOfByte = messageDigest.digest(paramString.getBytes("UTF-8"));
        StringBuffer stringBuffer = new StringBuffer();
        for (int i = 0; i < arrayOfByte.length; i++) {
          int j = arrayOfByte[i] & 0xFF;
          if (j < 16)
            stringBuffer.append("0"); 
          stringBuffer.append(Integer.toHexString(j));
        } 
        if (SmartSdkManager.getInstance().isOpenLog())
          Log.i("applog", "------MD5iS," + stringBuffer.toString()); 
        return stringBuffer.toString();
      } catch (Exception exception) {
        exception.printStackTrace();
        return "";
      } 
    } catch (Exception exception) {
      exception.printStackTrace();
      return "";
    } 
  }
}

 

其实走到这里就可以看出来了,最重要的就是:SmartSdkManager.getInstance().getClientKey() + '\t' + SmartSdkManager.getInstance().getClientSec() + '\t' + paramLong + '\t' + paramString1 + '\t' + paramString2;

通过刚刚的核心配置文件我们知道了

SmartSdkManager.getInstance().getClientKey() 值是 kbappkwle8K1Mhlc

SmartSdkManager.getInstance().getClientSec() 值是 WYjEbpFholuphDuO

paramLong 值是 当前时间戳

那么把上面的解密就是:

kbappkwle8K1Mhlc + '\t' + WYjEbpFholuphDuO + '\t' + 当前时间戳 + '\t' + paramString1 + '\t' + paramString2;

下一步,只要我们弄清楚paramString1paramString2就完全弄清楚这个传输方式过程中kbsv的加密方式。我们就可以通过修改数据包然后来重新加密,重新发包。

既然想弄清paramString1paramString2那么我们就要梳理整个加密解密逻辑关系,什么地方传入这两个参数。

我们先看下图,找到他们俩的来源

paramString1、paramString2参数来源

paramString1、paramString2参数来源

其实到这里基本就可以明白了。下图是更详细的 paramString1、paramString2参数来源

全局POST入口 参数详解

全局POST入口 参数详解

还有更多详细的分析需要梳理,这里就不梳理的那么仔细了。

 

paramString1 = 提交路径

例如  http://seo.baidu.com/Web/Page/Mainframe/Mainframe.aspx

paramString1 就是:/Web/Page/Mainframe/Mainframe.aspx   具体可能有所不同,我看到有的API接口是取部分值,有可能还是/Mainframe/Mainframe.aspx

 

paramString2 = 提交参数

例如 某GET请求是  https://woj.app/post.php?post=6731&action=edit

paramString2 就是 post=6731&action=edit

 

例如 某POST请求 https://seo.baidu.com/validOvertimeVoucher

DATA: {"customerId":null,"userUniqueId":"d020434d-0c0f-4357-b2b8-e834a4390009"}

paramString2 就是 {"customerId":null,"userUniqueId":"d020434d-0c0f-4357-b2b8-e834a4390009"}

 

那所有的全部都梳理出来拉,整体的就是:

1
2
3
4
5
6
def do_POST_signatureCode(n, s, s2):
    return 'kbappkwle8K1Mhlc' + '\t' + 'WYjEbpFholuphDuO' + '\t' + str(时间戳) + '\t' + paramString1 + '\t' + '\t' + paramString2 
 
 
def do_GET_signatureCode(n, s, s2):
    return 'kbappkwle8K1Mhlc' + '\t' + 'WYjEbpFholuphDuO' + '\t' + str(时间戳) + '\t' + paramString1 + '\t' + paramString2

就这么简单哦。
最后赠送一个神秘接口https://authlogin.baidu.com.cn/api/user/token?token=10232d845aa0fd70_1123123123a_1749ae5894a_w123123w 就可以查询这个用户的信息。

布施恩德可便相知重

微信扫一扫打赏

Image

支付宝扫一扫打赏

Image
×
Image

19 条留言  访客:12 条  博主:7 条

  1. Image 明月

    我试了一下,md5结果不对.能不能有偿帮我破解KFC APP的API?

    • Image gdd

      KFC 每次加密都要对应路径的。 而且不同的路径还要用不同的加密key 较为繁琐。

  2. Image shaonian

    刚刚小打赏,望回复一下

    • Image gdd

      什么情况???

      • Image shaonian

        有问题想咨询下,想私聊一下

      • Image shaonian

        哥们希望帮忙一下有偿

        • Image gdd

          你这边需要做什么事情呢? 可以直接给我发邮件聊。

        • Image gdd

          我这边只做技术研究、分享,不做软件破解、破坏,不做黑灰产。

      • Image shaonian

        这边不知道您的邮箱0.0

  3. Image shaonian

    哥们收到回一下下,谢谢

    • Image gdd

      你需要把[email protected] 加入白名单,不然我回你也收不到,还有 请问有什么事情吗?

  4. Image shaonian

    其实我有一个接口卡壳了,我朋友用易语言能访问通,但是我java链路和他一样参数也是一样,我这边就是返回verify failed,我只是换了个UA就不行了

    • Image gdd

      你这个问题很简单,只需要抓包,对比一下你朋友易语言发出的https请求数据包与你java请求的数据包有什么区别即可,因为语言不同,https请求包也可能有不同。 (之前朋友用go、python同一个功能,一个能实现,一个不能实现)

  5. Image shaonian

    就是拿抓包工具对比工具对比了还是有问题

  6. Image shaonian

    对比了协议头和发送的东西,一样的还是有问题

  7. Image shaonian

    所以想您帮我这边分析一下

  8. Image shaonian

    老哥

  9. Image shaonian

    呜呜呜,老哥help

给我留言

Image Image Image Image Image Image Image Image Image Image Image Image Image Image Image Image Image Image Image Image Image Image