Blogs
Using marketplace override
The Apache Log4j project is now saying that setting -Dlog4j2.formatMsgNoLookups=true
is not a 100% guarantee that you are protected from exploits. I think that currently no one has found a way to exploit the vulnerability on Liferay with -Dlog4j2.formatMsgNoLookups=true
set but many prefer to be extra safe.
As it has been stated before, you're likely to find log4j2 in DXP 7.4 or in some marketplace apps in previous versions, elasticsearch connector being often quoted.
Among the mitigations, the log4j project quotes another option:
remove the JndiLookup class from the classpath: zip -q -d log4j-core-*.jar org/apache/logging/log4j/core/lookup/JndiLookup.class
https://logging.apache.org/log4j/2.x/security.html
Liferay has a feature that allows you to override any LPKG you may find under osgi/marketplace:
https://help.liferay.com/hc/en-us/articles/360018159991-Overriding-lpkg-files
I've tested this procedure with Liferay DXP 7.0 and the Elasticsearch 6 connector ans already shared it with customers.
I'll provide you with the detailed steps of the procedure and how I demonstrated that it is getting the job done.
First, I've run the lb
command in the gogo shell to identify the bundle ID of the elasticsearch connector:
642|Active | 10|Liferay Portal Search Elasticsearch 6 Implementation (1.0.62)
And I've written this groovy script to test the classloading of the JndiLookup class:
import com.liferay.portal.scripting.groovy.internal.GroovyExecutor;
def bundle = org.osgi.framework.FrameworkUtil.getBundle(GroovyExecutor.class);
def esbundle = bundle.getBundleContext().getBundle(642);
def clazz = esbundle.loadClass("org.apache.logging.log4j.core.lookup.JndiLookup");
out.println(clazz);
The output of this script is the following:
class org.apache.logging.log4j.core.lookup.JndiLookup
We know that the class can be currently loaded in the bundle.
Using 7zip, I've opened the Liferay Connector to Elasticsearch 6.lpkg
file in my osgi/marketplace folder and extracted the com.liferay.portal.search.elasticsearch6.impl-1.0.62.jar
which I have copied to osgi/marketplace/override. I then renamed it as com.liferay.portal.search.elasticsearch6.impl.jar
Using 7zip again, I've browsed that jar until I've found log4j-core.jar and browsed down to the place where the JndiLookup class is located:
I've removed it and saved the archive with 7zip.
Note: Theoretically, you should use the jar command line (https://docs.oracle.com/en/java/javase/14/docs/specs/man/jar.html#:~:text=The%20jar%20command%20is%20a,command%20to%20create%20modular%20JARs.) to fix your jar but in my case 7zip did not corrupt the jar. If you run into issues, consider using the jar tool instead.
Then, I've restarted Liferay and discovered that it was useless unless you clean osgi/state. So please do it! :)
During Liferay's restart, you'll see this kind of log entries which is explaining that the original jar contained in the LPKG is ignored and replaced by the override:
2021-12-16 21:25:20.683 INFO [main][ModuleFrameworkImpl:1604] Starting initial bundles
2021-12-16 21:25:21.529 INFO [ModuleFramework-Static-Bundles-1][LPKGBundleTrackerCustomizer:626] Disabled Liferay Foundation - Liferay Connector to Elasticsearch 6 - Impl:com.liferay.portal.search.elasticsearch6.impl.jar
2021-12-16 21:25:22.737 INFO [ModuleFramework-Static-Bundles-1][DefaultLPKGDeployer:426] Installed override JAR bundle LPKG-Override::D:\wk\cardif\bundles\osgi\marketplace\override\com.liferay.portal.search.elasticsearch6.impl.jar
2021-12-16 21:25:23.536 INFO [main][ModuleFrameworkImpl:1888] Started initial bundles
I've run the lb
command in the gogo shell gain and discovered that the bundle ID had changed:
624|Active | 10|Liferay Portal Search Elasticsearch 6 Implementation (1.0.62)
And I've run the same script but with this new bundle ID:
import com.liferay.portal.scripting.groovy.internal.GroovyExecutor;
def bundle = org.osgi.framework.FrameworkUtil.getBundle(GroovyExecutor.class);
def esbundle = bundle.getBundleContext().getBundle(624);
def clazz = esbundle.loadClass("org.apache.logging.log4j.core.lookup.JndiLookup");
out.println(clazz);
It failed and I could see this log on my Liferay server:
org.apache.logging.log4j.core.lookup.JndiLookup cannot be found by com.liferay.portal.search.elasticsearch6.impl_1.0.62
This is exactly what I wanted to achieve. If you browse the osgi/state directory looking for the log4j-core jar, you'll find it but it will not have the JndiLookup.class
file.
Now, in order to make sure that I just don't have broken everything, I've tried to load a sister class:
import com.liferay.portal.scripting.groovy.internal.GroovyExecutor; def bundle = org.osgi.framework.FrameworkUtil.getBundle(GroovyExecutor.class); def esbundle = bundle.getBundleContext().getBundle(624); def clazz = esbundle.loadClass("org.apache.logging.log4j.core.lookup.JavaLookup"); out.println(clazz);
And I got that output:
class org.apache.logging.log4j.core.lookup.JavaLookup
Works as expected!
If you want to be 100% sure to remove any attacking surface before being able to apply a Liferay fix, you may use this procedure.
The nice thing about it is that it is easy to reverse: just remove the jar in the osgi/marketplace/override directory, restart Liferay with a clean osgi/state folder and you've reversed that manual fix of yours.
Of course, I encourage you to test this procedure in QA environment to practice the procedure before going live.