竹笋

注册

 

发新话题 回复该主题

原创浅谈Shiro反序列化获取Key的几 [复制链接]

1#
北京治疗白癜风的医院有哪些 https://yyk.39.net/bj/zhuanke/89ac7.html

关于ApacheShiro反序列化

在shiro≤..4版本,默认使CookieRememberMeManager,由于AES使用的key泄露,导致反序化的cookie可控,从而引发反序化攻击。(理论上只要AES加密钥泄,都会导致反序化)

利用的两个关键条件是key和可用gadget。..4版本默认key为kPH+bIxk5DdeZiIxcaaaA==,当然也可以通过下面的方式自定义key:

privatestaticfinalStringENCRYPTION_KEY="3AvVhmFLUs0KTA3Kprsdag==";publicCookieRememberMeManagerrememberMeManager(){CookieRememberMeManagercookieRememberMeManager=newCookieRememberMeManager();cookieRememberMeManager.setCookie(rememMeCookie());//remeberMecookie加密的密钥各个项目不一样默认AES算法密钥长度()cookieRememberMeManager.setCipherKey(Base64.decode(ENCRYPTION_KEY));returncookieRememberMeManager;}

下面结合实战以及shiro的CookieRememberMeManaer的调用过程,浅谈获取Key的几种方式。

Shirokey的获取方式

结合Dnslog与URLDNS

在进行漏洞探测的时候,一般会使用ysoserial-URLDNS-gadget结合dnslog进检测,其受JDK版本和相关的安全策略影响,除非存在网络限制DNS不能出网。

通过判断dnslog是否收到对应的请求,判断漏洞是否存在。这是获取key比较实用方法,通过在dnslog域名前加对应key的randomNum,结合对应的dnslog记录,即可获取到应用对应的Shirokey了。

例如下图通过结合Dnslog与URLDNS成果枚举出当前应用的key为kPH+bIxk5DdeZiIxcaaaA==:

利用时间延迟或报错

结合Dnslog与URLDNS方法有一个前提是DNS能出网。那么在不出网的情况下就需要找一个替代的方案了。结合SQL盲注的思路,可以考虑执行如下代码结合时间延迟进行判断,若系统是linux系统,则睡眠0s:

try{if(!(System.getProperty("os.name").toLowerCase().contains("win"))){Thread.currentThread().sleep(L);}}catch(Exceptione){}

同理,可以考虑结合触发Java异常进判断,若系统返回对应的报错系统,或者返回通用的报错提示,说明当前的key和gadget组合是成功的:

Stringresult="shiro-Vul-Discover";thrownewNoClassDefFoundError(newString(result));

上述的思路是通过执行相关的恶意代码来进行判断的,那么就需要在有相关的gadget的前提下才能进行key的枚举了。例如下面的案例,使用CommonsBeanutils结合时间延迟的方式成功枚举出当前key为4AvVhmFLUs0KTA3Kprsdag==:

同理,也可以使用报错的方式进行key的枚举:

这种方法的话存在一个比较棘手的点:枚举的次数多,耗时长。因为要结合可用gadget执行相关代码进行判断,那么假设字典的key个数为00个,那么枚举的次数就是gadget与key的笛卡尔积(0个gadget就耀枚举次),以下是一些常用的gadget:

URLDNSCommonsBeanutilsCommonsCollections*JRMPClientJRMPListenerC3P0Spring......

自动化不稳定:例如部分场景报错时会统一返回登陆页面,在实际利用中很多情况下也仅仅是在登陆页面的接口进行检测,那么就可能会出现漏报误报的情况。所以在DNS不出网的情况下,这种方式比较繁琐。

3结合CookieRememberMeManaer

shiro提供了记住我(RememberMe)的功能,关闭了浏览器下次再打开时还是能保存身份信息,使得无需再登录即可访问。在登陆成功时,如果启用了RememberMe功能,shiro会在CookieRememberMeManaer类中将cookie中rememberMe字段内容进行序列化、AES加密、Base64编码操作。然后保存在cookie中。在关闭浏览器后,重新访问对应的业务接口,此时就是反过来的操作,解码,解密,然后序列化。最后获取到当前用户的身份信息。简单看看具体的代码实现,看看能不能找到相关的思路来解决枚举key的问题。在获取到rememberMe后,会调用getRememberedPrincipals方法解密反序列化,得到用户凭证组信息:

protectedPrincipalCollectiongetRememberedIdentity(SubjectContextsubjectContext){RememberMeManagerrmm=getRememberMeManager();if(rmm!=null){try{returnrmm.getRememberedPrincipals(subjectContext);}......}

getRememberedPrincipals的具体实现:

publicPrincipalCollectiongetRememberedPrincipals(SubjectContextsubjectContext){PrincipalCollectionprincipals=null;try{byte[]bytes=getRememberedSerializedIdentity(subjectContext);if((bytes!=null)(bytes.length0)){principals=convertBytesToPrincipals(bytes,subjectContext);}}catch(RuntimeExceptionre){principals=onRememberedPrincipalFailure(re,subjectContext);}returnprincipals;}

在getRememberedSerializedIdentity方法里主要是对cookie里的相关内容进行base64解码,然后调用convertBytesToPrincipals方法进行解密操作:

protectedPrincipalCollectionconvertBytesToPrincipals(byte[]bytes,SubjectContextsubjectContext){if(getCipherService()!=null){bytes=decrypt(bytes);}returndeserialize(bytes);}

解密后就是对应的反序列化以及生成对应的用户凭证组的信息了。在调用上述方式时,如果抛出异常,则会调用onRememberedPrincipalFailure方法:

principals=onRememberedPrincipalFailure(re,subjectContext);

查看onRememberedPrincipalFailure的具体实现:

protectedPrincipalCollectiononRememberedPrincipalFailure(RuntimeExceptione,SubjectContextcontext){if(log.isDebugEnabled()){log.debug("Therewasafailurewhiletryingtoretrieverememberedprincipals.Thiscouldbeduetoaconfigurationproblemorcorruptedprincipals.Thiscouldalsobeduetoarecentlychangedencryptionkey.Therememberedidentitywillbeforgottenandnotusedforthisrequest.",e);}forgetIdentity(context);throwe;}

里面调用的是CookieRememberMeManager

类的forgetIdentity方法/p>

publicvoidforgetIdentity(SubjectContextsubjectContext){if(WebUtils.isHttp(subjectContext)){HttpServletRequestrequest=WebUtils.getHttpRequest(subjectContext);HttpServletResponseresponse=WebUtils.getHttpResponse(subjectContext);forgetIdentity(request,response);}}privatevoidforgetIdentity(HttpServletRequestrequest,HttpServletResponseresponse){getCookie().removeFrom(request,response);}

然后调用removeFrom方法,这里具体是设置对应的

responseheader,也就是常见的

rememberMe=deleteMe/p>

publicvoidremoveFrom(HttpServletRequestrequest,HttpServletResponseresponse){Stringname=getName();Stringvalue="deleteMe";String

分享 转发
TOP
发新话题 回复该主题