AJP File Read/Inclusion in Apache Tomcat (CVE-2020-1938) and Undertow (CVE-2020-1745)
Environment
- Red Hat JBoss Web Server (JWS)
- 5.2.0
- 3.1.7
- Red Hat JBoss Enterprise Application Platform (EAP)
- 5.x
- 6.x
- 7.x
- Red Hat Enterprise Linux
- 5.x ELS
- 6.x
- 7.x
- 8.x (as
pki-servlet-container
,pki-servlet-engine
inpki-deps
module)
Issue
-
CVE-2020-1938 is a file read/inclusion using the AJP connector in Apache Tomcat. The AJP protocol is enabled by default, with the AJP connector listening in TCP port
8009
and bond to IP address0.0.0.0
. A remote, unauthenticated/untrusted attacker could exploit this AJP configuration to read web application files from a server exposing the AJP port to untrusted clients. In instances where a poorly configured server allows file uploads, an attacker could upload malicious JavaServer Pages (JSP) code within a variety of file types to gain remote code execution (RCE). -
CVE-2020-1745 is a file read/inclusion using the AJP connector in Undertow and very similar to CVE-2020-1938.
Resolution
This is a configuration issue with AJP protocol in Tomcat/Undertow. AJP is a highly trusted protocol and should never be exposed to untrusted clients. It is insecure (clear text transmission) and assumes that your network is safe. The preventive measures should be taken by using the configuration that will not allow AJP to be exposed.
In order of preference, one of the following mitigations should be applied:
- Disable AJP altogether in Tomcat, and instead use HTTP or HTTPS for incoming proxy connections. HTTP and HTTPS do not contain the same trust issues as AJP.
- Protect the AJP connection with a secret, as well as carefully reviewing network binding and firewall configuration to ensure incoming connections are only allowed from trusted hosts.
- Use only network binding and firewall configuration to ensure incoming connections are only allowed from trusted hosts.
The first option, disabling AJP, is the most secure and robust recommended solution. Protecting AJP with a secret may be less disruptive, but requires using either mod_jk
or a version of httpd
that supports the secret
parameter. This parameter is supported by current versions of httpd
in Red Hat Enterprise Linux 7 and 8, but the version included in Red Hat Software Collections do not support this parameter, so another mitigation strategy must be employed.
Configuration showing how to disable AJP and how to protect it with a secret is shown below, for various Red Hat products.
JWS (Tomcat)
-
If your site is not using the AJP Connector, disable it by commenting it out from the
<TOMCAT_HOME>/conf/server.xml
file as:<!-- <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" /> -->
-
If AJP connector is required and cannot be commented/deactivated, then we recommend to set a secret password for the AJP conduit - Only requests from workers with the same secret keyword will be accepted. At the Tomcat side, edit
conf/server.xml
:<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" address="YOUR_TOMCAT_IP_ADDRESS" secret="YOUR_AJP_SECRET" />
Note that
YOUR_AJP_SECRET
must be changed to a value that is highly secure and cannot be easily guessed.
JWS (Tomcat) OpenShift container images
- JWS OpenShift container images do not expose AJP connector port 8009 by default. Hence they are not affected by this vulnerability.
JBoss EAP 5.2 (JBossWeb 2.1.x)
-
If your site is not using the AJP Connector, disable it by commenting it out from the
<EAP52_HOME>/server/$PROFILE/deploy/jbossweb.sar/server.xml
file as:<!-- <Connector protocol="AJP/1.3" port="8009" address="${jboss.bind.address}" redirectPort="8443" /> -->
-
If AJP connector is required and cannot be commented/deactivated, then we recommend to set a secret password for the AJP conduit - Only requests from workers with the same secret keyword will be accepted. At the EAP 5.2 side, edit
<EAP52_HOME>/server/$PROFILE/deploy/jbossweb.sar/server.xml
:<Connector protocol="AJP/1.3" port="8009" address="${jboss.bind.address}" redirectPort="8443" requiredSecret="YOUR_AJP_SECRET"/>
Note that
YOUR_AJP_SECRET
must be changed to a value that is highly secure and cannot be easily guessed.
JBoss EAP 6.4 (JBossWeb 7.x)
The AJP connector is enabled by default only in standalone-full-ha.xml
, standalone-ha.xml
and full-ha
, ha
profiles in domain.xml
. AJP connector can be secured as follows:
-
In JBoss EAP 6.4 Update 23+ or after applying the One off Patch to EAP 6.4 Update 22, the vulnerability is fixed and custom AJP request attributes are blocked by default. If using custom AJP and request attributes, see How to allow custom AJP request attributes after applying the CVE-2020-1938 AJP File Read/Inclusion Vulnerability fix in JBoss EAP 6.4 Update 23+ or with the Security Patch applied to top of Update 22 , as they will not be allowed by default after the CVE fix.
-
If you do not use AJP, you can disable the AJP port configuration in your
standalone-*.xml
and/ordomain.xml
file by settingenabled="false"
as shown below or comment out the whole<connector name="AJP" .../>
clause:<connector name="AJP" protocol="AJP/1.3" scheme="http" socket-binding="ajp" enabled="false"/>
-
If AJP connector is a requirement and cannot be commented or deactivated, then, it is recommended to add credential to AJP connector by configuring the following system property. See also this knowledge article about adding system properties:
<system-properties> <property name="org.apache.coyote.ajp.DEFAULT_REQUIRED_SECRET" value="YOUR_AJP_SECRET"/> </system-properties>
Note that
YOUR_AJP_SECRET
must be changed to a value that is highly secure and cannot be easily guessed.
JBoss EAP 7 (Undertow)
The AJP connector is enabled by default only in standalone-full-ha.xml
, standalone-ha.xml
and full-ha
, ha
profiles in domain.xml
. AJP connector can be secured as follows:
-
In EAP 7.2.8+ / EAP 7.3.1+ (or after applying the One off Patch to EAP 7.2.7 / EAP 7.3.0), the vulnerability is fixed and custom AJP request attributes are blocked by default. If using custom AJP and request attributes, see How to allow AJP request attributes after applying the CVE-2020-1745 AJP File Read/Inclusion Vulnerability fix in JBoss EAP 7.2 Update 8+ as they will not be allowed by default after the CVE fix.
-
If you do not use AJP, you can disable the AJP port configuration in your
standalone-*.xml
and/ordomain.xml
file by settingenabled="false"
as shown below or comment out the whole<ajp-listener .../>
clause:<ajp-listener name="ajp" socket-binding="ajp" enabled="false"/>
Important: notice that
mod_cluster
uses AJP by default as a conduit. You need to configuremod_cluster
to use HTTP or HTTPS instead of AJP before disabling the AJP connector. -
If AJP connector is a requirement and cannot be commented or deactivated, configure the following configuration inside undertow subsystem to check secure request attribute on AJP request.
<subsystem xmlns="urn:jboss:domain:undertow:7.0" ...> ... <server name="default-server"> <http-listener name="default" socket-binding="http" redirect-socket="https" enable-http2="true"/> <ajp-listener name="ajp" socket-binding="ajp"/> <https-listener name="https" socket-binding="https" security-realm="ApplicationRealm" enable-http2="true"/> <host name="default-host" alias="localhost"> ... <!-- add the following with your AJP port (8009) --> <filter-ref name="secret-checker" predicate="equals(%p, 8009)"/> </host> </server> ... <filters> <!-- add the following with your credential (YOUR_AJP_SECRET) --> <expression-filter name="secret-checker" expression="not equals(%{r,secret}, 'YOUR_AJP_SECRET') -> response-code(403)"/> </filters> </subsystem>
The above configuration can be added by the following two command in JBoss-CLI1:
/subsystem=undertow/configuration=filter/expression-filter=secret-checker:add(expression="not equals(%{r,secret}, 'YOUR_AJP_SECRET') -> response-code(403)") /subsystem=undertow/server=default-server/host=default-host/filter-ref=secret-checker:add(predicate="equals(%p,8009)")
Note that
YOUR_AJP_SECRET
must be changed to a value that is highly secure and cannot be easily guessed.
If the above configuration is not possible, then consider binding AJP port to the loopback interface, or having a firewall that only allows access from trusted hosts would considerably reduce the attack surface to local, or trusted users.
Apache httpd (httpd in JBCS or RHEL)
When the above "secret" setting is configured on Tomcat/JBoss side, the same secret value (YOUR_AJP_SECRET
in the above example) will be required to be configured on the front-end proxy (mod_proxy_ajp or mod_jk).
httpd
in Red Hat Software Collections (RHSCL) does not support the secret
parameter. In this case we recommend using HTTP or HTTPS and disabling AJP in Tomcat as shown above.
mod_proxy (mod_proxy_ajp / mod_proxy_balancer using ajp)
- The
secret
property is supported since the following versions: JBCS httpd 2.4.37; RHEL 7 httpd-2.4.6-672; RHEL 8 httpd-2.4.6-62.- For
mod_proxy_ajp
, thesecret
property can be added toProxyPass
setting. - For
mod_proxy_balancer
, thesecret
property can be added to eachBalancerMember
setting.
- For
-
For example, add
secret=YOUR_AJP_SECRET
in your configuration (e.g.<HTTPD_HOME>/conf/httpd.conf
or<HTTPD_HOME>/conf.d/proxy_ajp.conf
) like the following:-
mod_proxy_ajp
:ProxyPass /example/ ajp://localhost:8009/example/ secret=YOUR_AJP_SECRET
-
mod_proxy_balancer
:<Proxy balancer://mycluster> BalancerMember ajp://node1:8009 route=node1 secret=YOUR_AJP_SECRET BalancerMember ajp://node2:8009 route=node2 secret=YOUR_AJP_SECRET </Proxy> ProxyPass /example/ balancer://mycluster/example/ stickysession=JSESSIONID|jsessionid
-
mod_jk
mod_jk
-secret
can be specified to a worker or a load balancer inworkers.properties
. If you set a secret on a load balancer, all its members will inherit this secret. Thissecret
property support was added in mod_jk 1.2.12 onwards.-
For example, add the following in your
workers.properties
:worker.<WORKER_NAME>.secret=YOUR_AJP_SECRET
Ensure that WORKER_NAME must be replaced with the appropriate name.
mod_cluster
mod_cluster
does not support thesecret
property. 3 in JBCS httpd 2.4.37 SP1 or earlier. With JBCS httpd 2.4.37 SP2, you can set theAJPsecret
directive in your httpd mod_cluster config to match your AJP connector secret- If mod_cluster is required with earlier versions, you need to change your configuration to use
http
orhttps
instead ofajp
because you can not connect without a correct secret value whensecret
is configured on the AJP connector in Tomcat/JBoss side.
Tomcat on RHEL
- Red Hat Enterprise Linux 8 makes Tomcat available in the
pki-deps
module. This module is only intended for use to support the Dogtag Certificate System as shipped in thepki-core
module, and not for hosting custom or third-party applications. In this configuration, Tomcat is only available locally and therefore remote attacks are not considered possible. As a result, the severity is Moderate. - Red Hat Enterprise Linux 7 and 6 are affected. Please follow the CVE page for further updates.
- Red Hat Enterprise Linux 5 is in the Extended Life Phase of the Support and Maintenance Life Cycle. As this vulnerability is rated Moderate, it is not in support scope for Extended Life Phase, and no updates are planned to be released for Red Hat Enterprise Linux 5. The Mitigation strategies described above should be used.
- Red Hat Software Collections are not affected. The binary RPMs produced from the
rh-java-common-tomcat
source RPM do not contain the Apache Coyote code which is affected by this vulnerability unless it is manually recompiled. A future update may fix the code. - Red Hat Satellite 6 makes use of Red Hat Enterprise Linux 7's tomcat. Satellite versions 6.5 and older enable AJP. Users of these versions should install RHSA-2020:0855 to get the fixed version of the tomcat component.
Spring Boot with Embedded Tomcat
If we have a Spring boot application with an embedded Tomcat we need to define a bean that handle the embedded application container creation. This bean must make a connector using AJP to connect Apache to Tomcat. We can define this in a @Configuration annotated class as follows:
@Configuration
public class TomcatConfig {
@Bean
public TomcatServletWebServerFactory servletContainer() {
TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory();
Connector ajpConnector = new Connector("org.apache.coyote.ajp.AjpNioProtocol");
AjpNioProtocol protocol= (AjpNioProtocol)ajpConnector.getProtocolHandler();
protocol.setSecret("myapjsecret");
ajpConnector.setPort(9090);
ajpConnector.setSecure(true);
tomcat.addAdditionalTomcatConnectors(ajpConnector);
return tomcat;
}
}
Restart the app and you should see messages that Tomcat is now listening on both port 8080 and 9090.
[ restartedMain] org.apache.coyote.ajp.AjpNioProtocol : Initializing ProtocolHandler ["ajp-nio-127.0.0.1-9090"]
[ restartedMain] org.apache.coyote.ajp.AjpNioProtocol : Starting ProtocolHandler ["ajp-nio-127.0.0.1-9090"]
For further updates please follow up on CVE-2020-1938 and CVE-2020-1745 pages.
Root Cause
This vulnerability leverages a AJP protocol functionality to get access to files at the server side and it is not a code failure.
The AJP protocol, if used, must be properly isolated with proper firewall rules and secret keys to accept only valid content.
This solution is part of Red Hat’s fast-track publication program, providing a huge library of solutions that Red Hat engineers have created while supporting our customers. To give you the knowledge you need the instant it becomes available, these articles may be presented in a raw and unedited form.
Comments