# Liferay revisited: A tale of 20k$

Chained jkstatus info leak, Apache LoadBalancer wildcard routing, and Akamai WAF encoding bypass to reach Liferay and exploit CVE-2020-7961 for pre-auth RCE.

- Payout: $20,000
- Severity: critical
- Program: -
- Credited: VNG Security Response Center
- Disclosed: 2022-08-06
- CVE: CVE-2020-7961
- Write-up quality: 8/10
- Original: https://vsrc.vng.com.vn/blog/liferay-revisited-a-tale-of-20k/
- Library page: https://bugbountylibrary.com/r/blog%3Avsrc-vng-com-vn-blog-liferay-revisited-a-tale-of-20k
- Tags: domain:web impact:rce kind:bounty platform:blog sev:critical tech:java technique:chained technique:parser-differential technique:source-review vuln:deserialization vuln:info-disclosure vuln:path-traversal vuln:rce

**Target:** Liferay Portal behind Akamai WAF and Apache LoadBalancer
**Root cause:** Apache LoadBalancer wildcard rule /group/control_panel* routes to Liferay; Akamai doesn't URL-decode so %2e%2e%3b bypasses WAF, then Tomcat strips ';' and normalizes ../ to reach /api/jsonws. Liferay's JSON Web Service allows typed parameter instantiation leading to C3P0 deserialization.
**Steps:**
1. Recon finds `https://sub.foo.bar/jkstatus;` leaking internal routing maps
2. Identify `/web/aviva` routes to Liferay; need to traverse to `/api/jsonws`
3. Akamai blocks `..;` and `;` so encode as `%2e%2e%3b`
4. Send `GET /group/control_panel/%2e%2e%3b/%2e%2e%3b/api/jsonws/...` — Akamai passes, Apache decodes and routes via `/group/control_panel*` wildcard, Tomcat normalizes to `/api/jsonws/`
5. Akamai blocks `{ } ; :` in POST body, so use GET-style JSONWS invocation with typed params in URL
6. Invoke `expandocolumn/add-column` with `-defaultData:com.mchange.v2.c3p0.WrapperConnectionPoolDataSource`
7. POST body sets `defaultData.userOverridesAsString` to hex-encoded serialized payload triggering C3P0 deserialization
8. Use deserialization gadget to execute commands and write output to Liferay webroot (no outbound connection)
**How found:** Noticed `;` on jkstatus endpoint (Orange Tsai trick), reviewed 84 Apache LoadBalancer routing maps, spotted `/group/control_panel*` wildcard, hypothesized URL-encoding `..;` would bypass Akamai (no decode) but be decoded by Apache, then Tomcat-normalized to reach JSONWS.
**Exploitation:** Pre-auth: chain WAF bypass to reach Liferay JSONWS, then abuse CVE-2020-7961 by instantiating WrapperConnectionPoolDataSource via typed URL parameter and supplying a hex-encoded serialized payload in POST body's userOverridesAsString, triggering deserialization RCE. Output written to webroot since no egress.
**Escalation:** Escalated low-severity jkstatus info leak into full pre-auth RCE by chaining proxy bypass, load balancer wildcard routing, and deserialization.
**Insight:** When a WAF and backend disagree on URL decoding, encode traversal (`%2e%2e%3b`) so the WAF sees a benign prefix matching a load-balancer wildcard rule while the backend decodes and normalizes to the real path. Also, JSONWS GET-style typed params (`-param:class`) can substitute for blocked JSON bodies.
