Apache Tomcat (CVE-2020-1938) および Undertow (CVE-2020-1745) での AJP ファイルの読み取り / インクルージョン

Solution Verified - Updated -

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 (pki-deps モジュールの pki-servlet-containerpki-servlet-engine として)

Issue

  • CVE-2020-1938 は、Apache Tomcat の AJP コネクターを使用したファイルの読み取り / インクルージョンです。AJP プロトコルはデフォルトで有効になっており、AJP コネクターは TCP ポート 8009 をリッスンし、IP アドレス 0.0.0.0 に結びつけられます。認証 / 信頼されていないリモート攻撃者は、この AJP 設定を悪用してサーバーから Web アプリケーションファイルを読み取り、信頼されていないクライアントに AJP ポートを公開する可能性があります。適切に設定されていないサーバーでファイルのアップロードが許可されている場合、攻撃者は悪意のある JavaServer Pages (JSP) コードをさまざまなファイルタイプ内にアップロードして、リモートコード実行 (RCE) を行う可能性があります。

  • CVE-2020-1745 は、Undertow の AJP コネクターを使用したファイルの読み取り / インクルージョンで、CVE-2020-1938 に非常に似ています。

Resolution

これは、Tomcat / Undertow の AJP プロトコルの設定に関する問題です。AJP は信頼性の高いプロトコルですが、信頼できないクライアントには公開しないでください。これは安全ではなく (クリアテキスト転送)、お客様のネットワークが安全であると想定しています。予防策として、AJP が公開されない設定を使用する必要があります。

優先順に示した以下の緩和策のいずれかを適用する必要があります。

  • Tomcat で AJP を完全に無効にし、代わりに着信プロキシー接続に HTTP または HTTPS を使用します。 HTTP および HTTPS には、AJP と同じ信頼問題は含まれていません。
  • AJP 接続をシークレットで保護し、ネットワークバインディングとファイアウォール設定を慎重に確認して、信頼できるホストからの受信接続のみが許可されるようにします。
  • 信頼できるホストからの受信接続のみが許可されるようにするには、ネットワークバインディングとファイアウォール設定のみを使用してください。

AJP を無効にする最初のオプションは、最も安全で堅牢な推奨ソリューションです。シークレットを使用して AJP を保護すると、混乱が少なくなる可能性がありますが、secret パラメーターをサポートする mod_jk または httpd のバージョンの 1 つを使用する必要があります。 Red Hat Software Collections の現行バージョンの httpd は、このパラメーターをサポートしていないため、別の軽減策を採用する必要があります。

さまざまな Red Hat 製品について、AJP を無効にする方法と AJP をシークレットで保護する方法に関する設定を以下に示します。

JWS (Tomcat)

  • お客様のサイトで AJP コネクターを 使用していない 場合は、<TOMCAT_HOME>/conf/server.xml ファイルから以下のようにコメントアウトして無効にします。

    <!-- <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" /> -->
    
  • AJP コネクターが必要で、コメント / 非アクティブ化ができない場合は、AJP コンジットにシークレットパスワードを設定することをお勧めします - 同じシークレットキーワードを持つワーカーからのリクエストのみが受け入れられます。 Tomcat 側で conf/server.xml を編集します。

    <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" address="YOUR_TOMCAT_IP_ADDRESS" requiredSecret="YOUR_AJP_SECRET" />
    

    YOUR_AJP_SECRET は、安全性が高く簡単に推測できない値に変更する必要があることに留意してください。

JWS (Tomcat) OpenShift コンテナーイメージ

  • JWS OpenShift コンテナーイメージ は、デフォルトでは AJP コネクターポート 8009 を公開しません。したがって、これらはこの脆弱性の影響を受けません。

JBoss EAP 5.2 (JBossWeb 2.1.x)

  • お客様のサイトで AJP コネクターを 使用していない 場合は、<EAP52_HOME>/server/$PROFILE/deploy/jbossweb.sar/server.xml ファイルから以下のようにコメントアウトして無効にします。

    <!-- <Connector protocol="AJP/1.3" port="8009" address="${jboss.bind.address}"
         redirectPort="8443" /> -->
    
  • AJP コネクターが必要で、コメント / 非アクティブ化ができない場合は、AJP コンジットにシークレットパスワードを設定することをお勧めします - 同じシークレットキーワードを持つワーカーからのリクエストのみが受け入れられます。 EAP 5.2 側で <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"/>
    

    YOUR_AJP_SECRET は、安全性が高く簡単に推測できない値に変更する必要があることに留意してください。

JBoss EAP 6.4 (JBossWeb 7.x)

AJP コネクターは、[ domain.xmlstandalone-full-ha.xmlstandalone-ha.xml、および full-haha プロファイルでのみデフォルトで有効になっています。 AJP コネクターは、以下のように安全性を確保できます。

  • AJP を使用しない場合は、以下のように enabled="false" を設定するか、<connector name="AJP" .../> 部分全体をコメントアウトして、standalone-*.xmldomain.xml ファイルで AJP ポートの設定を無効にすることができます。

    <connector name="AJP" protocol="AJP/1.3" scheme="http" socket-binding="ajp" enabled="false"/>
    
  • AJP コネクターが必須要件で、コメント化または非アクティブ化できない場合は、以下のシステムプロパティーを設定して、認証情報を AJP コネクターに追加することをお勧めします。システムプロパティーの追加に関する詳細は、こちらのナレッジ記事 も参照してください。

    <system-properties>
        <property name="org.apache.coyote.ajp.DEFAULT_REQUIRED_SECRET" value="YOUR_AJP_SECRET"/>
    </system-properties>
    

    YOUR_AJP_SECRET は、安全性が高く簡単に推測できない値に変更する必要があることに留意してください。

JBoss EAP 7 (Undertow)

AJP は、 domain.xmlstandalone-full-ha.xmlstandalone-ha.xml および full-haha プロファイルでのみデフォルトで有効になっています。AJP コネクターは、以下のように安全性を確保できます。

  • JBoss EAP 7.2 Update 8+ で、または EAP 7.2 Update 7 / EAP 7.3 に One off Patch を適用した後に、カスタム AJP およびリクエスト属性を使用する場合は、CVE 修正後のデフォルトでは許可されないので、「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+」を参照してください。

  • AJP を使用しない場合は、以下のように enabled="false" を設定するか、<ajp-listener .../> 部分全体をコメントアウトして、standalone-*.xmldomain.xml ファイルで AJP ポートの設定を無効にすることができます。

    <ajp-listener name="ajp" socket-binding="ajp" enabled="false"/>
    

    重要: mod_cluster は、デフォルトでコンジットとして AJP を使用する点に留意してください。AJP コネクターを無効化する前に、AJP の代わりに HTTP または HTTPS を使用するように mod_cluster を設定する必要があります。

  • AJP コネクターが必須要件であり、コメント化または非アクティブ化できない場合は、Undertow サブシステム内で以下の設定を構成して、AJP リクエストの安全なリクエスト属性を確認します。

    <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>
    

    上記の設定は、JBoss-CLI で以下の 2 つのコマンドを使用して追加できます。1

    /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)")
    

    YOUR_AJP_SECRET は、安全性が高く簡単に推測できない値に変更する必要があることに留意してください。

上記の設定が不可能な場合は、AJP ポートをループバックインターフェースにバインドするか、信頼できるホストからのアクセスのみを許可するファイアウォールを設定することで、ローカルユーザーまたは信頼できるユーザーへの攻撃対象領域を大幅に減らすことができます。


Apache httpd (JBCS または RHEL の httpd)

上記の「シークレット」設定が Tomcat / JBoss 側で設定されている場合、同じシークレット値 (上記の例では YOUR_AJP_SECRET) をフロントエンドプロキシー (mod_proxy_ajp or mod_jk) で設定する必要があります。

Red Hat Software Collections (RHSCL) の httpd は、secret パラメーターをサポートしません。この場合、上記のように HTTP または HTTPS を使用し、Tomcat で AJP を無効化することを推奨します。

mod_proxy (ajp 使用の mod_proxy_ajp / mod_proxy_balancer)

  • secret プロパティーのサポートは、JBCS httpd 2.4.37 以降、または RHEL 7 httpd-2.4.6-67.el7.x86_64 以降に追加されました。2.
    • mod_proxy_ajp の場合、secret プロパティーを ProxyPass 設定に追加できます。
    • mod_proxy_balancer の場合、secret プロパティーを各 BalancerMember 設定に追加できます。
  • たとえば、secret=YOUR_AJP_SECRET を以下のように設定に追加します (例: <HTTPD_HOME>/conf/httpd.conf または <HTTPD_HOME>/conf.d/proxy_ajp.conf)。

    • 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 は、workers.properties で、ワーカーまたはロードバランサーに指定できます。ロードバランサーにシークレットを設定すると、そのすべてのメンバーがこのシークレットを継承します。この secret プロパティーのサポートは、mod_jk 1.2.12 以降で追加されました。
  • たとえば、workers.properties に以下を追加します。

    worker.<WORKER_NAME>.secret=YOUR_AJP_SECRET
    

    WORKER_NAME を適切な名前に置き換える必要があることを確認します。

mod_cluster

  • mod_cluster は、secret プロパティーをサポートしません。3
  • mod_cluster が必要な場合は、ajp の代わりに http または httpsを使用するよう設定を変更する必要があります。これは、Tomcat / JBoss 側の AJP コネクターで secret が設定されている場合は、正しいシークレット値がないと接続できないからです。

RHEL 上の Tomcat

  • Red Hat Enterprise Linux 8 では、Tomcat を pki-deps モジュールで利用できます。このモジュールは、pki-core モジュールに同梱されている Dogtag Certificate System へのサポートに対してのみ使用することを目的としており、カスタムまたはサードパーティーのアプリケーションをホストするためのものではありません。この設定では、Tomcat はローカルでのみ使用できるため、リモート攻撃は不可能だと考えられています。そのため、重大度は中程度になります。
  • Red Hat Enterprise Linux 7 および 6 が影響を受けます。 今後のアップデートについては、CVE ページを参照してください。
  • Red Hat Enterprise Linux 5 は、サポートおよびメンテナンスライフサイクルの 延長ライフフェーズ に入っています。この脆弱性は中程度と評価されているため、延長ライフフェーズのサポート範囲ではなく、Red Hat Enterprise Linux 5 用の更新がリリースされる予定はありません。上記の軽減策を使用する必要があります。
  • Red Hat Software Collections への影響はありません。rh-java-common-tomcat ソース RPM から生成されたバイナリーRPMには、手動で再コンパイルしない限り、この脆弱性の影響を受ける Apache Coyote コードは含まれていません。今後のアップデートでコードが修正される可能性があります。
  • Red Hat Satellite 6 は、Red Hat Enterprise Linux 7 の tomcat を利用しています。ただし、AJP を有効化しないため、Satellite 6 はこの問題の影響を受けません。

Tomcat が組み込まれた Spring Boot

Tomcat が組み込まれた Spring Boot アプリケーションがある場合、組み込みアプリケーションコンテナーの作成を処理する Bean を定義する必要があります。この Bean は、AJP を使用して Apache を Tomcat に接続するコネクターを作成する必要があります。これを @Configuration 注釈付きクラスで以下のように定義できます。

@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;
    }
}

アプリを再起動すると、Tomcat がポート 8080 と 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"]

詳細なアップデートに関しては、CVE-2020-1938 および CVE-2020-1745 のページを参照してください。

Root Cause

この脆弱性は AJP プロトコル機能を利用してサーバー側のファイルにアクセスするものであり、コードエラーではありません。
AJP プロトコルを使用する場合は、有効なコンテンツのみを受け入れるために、適切なファイアウォールルールと秘密キーで正常に分離する必要があります。

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.