Apache Software FoundationサーブレットコンテナのApache Tomcatの全てのバージョン(9.x/8.x/7.x/6.x)でAJP(PORT:8009)を利用して、/web/ROOTディレクトリ下位のファイルを読み込める「潜在的(Potential)」なリモートコード実行脆弱性(Ghostcat, CVE-2020-1938)が発見された。ウェブアプリケーションプログラム内にファイルアップロード及び保存の許可が必要であるため、「潜在的」と表現した。
2020年1月3日初めに中国のセキュリティ業者のChaitin Techにて発見されました、Ghostcatと名前を付けた。脆弱性が公開された後、2020年2月EOSされた6.xバージョンを除外した全てのバージョンのセキュリティパッチが行われていたが、2020年3月現在まで、GitHubに20個のPOCが公開されて攻撃頻度が急激に増加することになった。
AJPはTomcatをインストール際にポートが基本的に有効化するため、下記の図のようにインターネットにつながっている機器の情報を提供しているFOFA(FOFA.SO)によると、日本内でも基本ポートでサービス使用しているサーバが45,680件(2020年3月12月基準)で、脆弱性に漏出されている。その為、今回はリモートコード実行可能性があって、攻撃の危険度が高いApache Tomcat-AJP脆弱性、CVE-2020-1938について調べてみよう。
【▲ インターネット上に公開されているAJPポートの使用数値(参考:FOFA)】
CVE | 脆弱なバージョン | 危険度 |
---|---|---|
CVE-2020-1938 | Apache Tomcat 6.x | High |
Apache Tomcat 7.0.0 ~ 7.0.99 | ||
Apache Tomcat 8.5.0 ~ 8.5.50 | ||
Apache Tomcat 9.0.0 M1 ~ 9.0.30 |
AJP(Apache Jserv Protocol)はApache ServeとJAVA EEサーバ間の連携のためのプロトコルで、様々なウェブサーバからアプリケーションサーバへのロードバランスを具現するための目的で使用される。セッションはそれぞれのアプリケーションサーバのインスタンス名を持つルーティングメカニズムを使用してアプリケーションサーバにリダイレクトされた場合、サーバのためのリバースプロキシでウェブサーバが動作されることになる。
【▲ Apache Tomcatの環境設定ファイルの基本設定】
AJP RequestがApacheからTomcatに送信される際、ユーザーの入力値を検証なく、実行されると、任意でファイルを読み込んだり実行ができるようになる。前もって説明していた潜在的なリモートコード実行条件である、ファイルのアップロードができる環境であれば任意のファイルがアップロードされた後、AJP脆弱性を利用して攻撃コードを実行できるようになる構造である。
CVE-2020-1938はAJP RequestメッセージがTomcatに送信される際、発生する。AJP Requestメッセージを処理する際にTomcatからはorg.apache.coyote.ajp.AjpProcessor.javaを呼び出し、prepareRequest()を通じてAJPメッセージを取り出してRequestオブジェクトをAtrribute(属性)で設定する。
Requestメッセージヘッダーではファイルの拡張子によって、*.JSPファイルはJspServlet.javaで処理されてそれ以外のファイルはDefaultServlet.javaから処理される。任意のファイルが実行されるためにはDefaultServlet.javaが処理すべきファイルもJspServlet.javaから処理させるのが脆弱性の核心である。
【▲ AJP Request送信過程】
上記の図の流れの中でAJP Requestから使用するクラスメンバーであるAttribute(属性)を調べてみると事前に定義されていない属性を使用する場合「SC_A_REQ_ATTRIBUTE」に設定される。「SC_A_REQ_ATTRIBUTE」を処理するためのプロセスに脆弱性があるため、攻撃を実施する際にAJP RequestのAttribute値を「SC_A_REQ_ATTRIBUTE」に設定して送信する。
【▲ AJP Request Attributeの定義】
【▲ AjpProcessor.javaクラスからの脆弱性発生コード】
下記のAJP Headerパケットからattribute_name(javax.sevlet.include.request_uri, javax.sevlet.include.servlet_path)とattribute_value(index, /attack.txt) 値を上記のコードからそれぞれ変数nとvに保存してAttributeの要請ではないかと確認後、Requestメッセージとしてカプセル化してgetadapter()関数を通じて、servlet過程に送る。この過程で入力されたn, v値に対して別途の検証は行わなく、攻撃ができるようになる。
【▲ AJP RequestのパケットHeader情報】
一般ファイルの場合、上記の過程から作られたRequest値がDefaultServlet.javaクラスに送信され、service()関数を呼び出した後、GET Methodに送信される。service()関数はdoget()関数を呼び出し、doget()関数はserveResource()関数を通じて必要なソースコードが得られる。
serveResource()関数はHttpServeltRequest値をgetRelativePath関数を通じてRequestDispatcher.INCLUDE_PATH_INFOとRequestDispatcher.INCLUDE_SERVLET_PATHをpathInfoと serveretPathに保存するともらえる戻り値を通じてgetResource()関数に保存する変数を作成する。getResource()関数からは当該パスのファイルの実行結果を持ってくる。
【▲ http servletコード】
【▲ jspServlet.javaからreuqest処理】
JSPファイルの場合、defaultServlet.javaクラスではなく、jspServlet.javaクラスを呼び出してRequestメッセージを送信することになる。
メッセージの処理方法は前もって調べていたdefaultServlet.javaと同じく、Requestメッセージに保存されているinclude_path_info attributeをjspUriに入れて、Requestメッセージに追加した後、serviceJspFileから当該のパスのリソースを持ってきてコンパイラに送信されて、サーバに転送される。この過程で変更されたAttributeであれば、変更されたURIパス上のファイルが実行される。
【▲ 攻撃に使用されるAttribute属性値】
CVE-2020-1938を実際に攻撃コードとして作成するためには、前に説明した3つのAttribute設定の中でjavax.servlet.include.path_infoを除いた2つの属性値を変更して送信しなければならない。Githubに公開されたPOCの中で1つを分析してTomcat-AJP脆弱性をより詳細に確認してみよう。
【▲ POCコード内に「SC_A_REQ_ATTRIBUTE」値を設定した場合】
POCコードを確認してみると、脆弱性が存在している「SC_A_REQ_ATTRIBUTE」を実行するために、「SC_A_REQ_ATTRIBUTE」値に10(0A)を入力して送信している。
【▲ POCコード(ajpshooter.py)のshoot()関数】
ファイルがアップロードできる環境でjspServlet.javaを通じてリモートコード実行脆弱性が実際できるかPOCコードを利用して下記の環境でテストをやってみた。
区分 | 環境とバージョン |
---|---|
Server OS | CentOS 7 (192.168.0.138) |
Client OS | Windows 7 (192.168.0.131) |
Server | Apache 2.4.41 |
サーブレットコンテナ | Tomcat 8.5.49 |
AJP Module | mod_jk |
① Wgetコマンドを利用してWebshellファイルをダウンロードする攻撃コードが含まれているファイルを作成
【▲ attack.txtファイルの内容】
② 作成した攻撃コードを/webapps/ROOTパスにアップロード
【▲ サーバにアップロードされたattack.txtファイル】
【▲ attack.txt実行結果(1)】
【▲ attack.txt実行結果(2)】
脆弱性なバージョン | セキュリティパッチ | パッチ日付 |
---|---|---|
Apache Tomcat 6.x | EOS | EOS |
Apache Tomcat 7.0.0 ~ 7.0.99 | Apache Tomcat 7.0.100 | 2020. 02. 14 |
Apache Tomcat 8.5.0 ~ 8.5.50 | Apache Tomcat 8.5.51 | 2020. 02. 11 |
Apache Tomcat 9.0.0 M1 ~ 9.0.30 | Apache Tomcat 9.0.31 | 2020. 02. 11 |
Apache Tomcatのバージョンの確認方法は2つがある。
① version.shで確認する:「インストールパス」/bin/version.sh実行
【▲ ./version.sh実行結果】
下図のセキュリティパッチ結果の通りjava/org/apache/coyote/ajp/AjpProcessor.javaのprepareRequest()に既存では定義していないattributeコードに対して、req_attributから別途検証なして実行していた脆弱性はgetAllowedArbitraryRequestAttributesPattern()を通じて検証する機能が追加された。
【▲ 脆弱性セキュリティパッチ結果】
1) 「インストールパス」/conf/server.xml
2) AJPを有効有無を決定する設定値変更(注釈処理)
<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
3) 保存及び再起動
【▲ AJPの無効化方法】
AJPが必ず使用されていてアップデートが困難な場合、requiredSecretオプションを使用して資格証明を設定する。
1) 「インストールパス」/conf/server.xml
2) AJPを有効有無を決定する設定値変更(注釈処理)
<Connector protocol="AJP/1.3"
address="[TomcatサーバIP]"
port="8009"
redirectPort="8443"
requiredSecret="[AJP認証属性]" />
3) 保存及び再起動
【▲ AJPの資格証明の設定方法】
区分 | 検知ポリシー |
---|---|
1 | alert tcp any any -> $HOME_NET 8009 (msg:”IGRSS.2.04201 Apache, Tomcat, CVE-2020-1938, Attempted User Privilege Gain”; flow:to_server,established; content:” |
【▲ CVE-2020-1938 Snort】
今回はTomcat-AJP脆弱性分析及び対応方法:Ghostcat(CVE-2020-1938)について調べてみた。
Tomcatはウェブアプリケーションで日本国内でも幅広く使用されているため、この分析レポートを参考し、適切な対処を行うことを推奨する。
[blogcard url=” https://cyberfortress.jp/contact/ “]
[1] Apache Tomcat脆弱性セキュリティアップデート推奨
FaceBook、Twitterで、サイバーセキュリティに関する情報、注意喚起、そのほかITお役立ち情報を配信しています。
是非、フォローいただき情報をご確認ください。
Written by CYBERFORTRESS, INC.
Tweet