CyberFix Note
防御・ハードニング

Webシェルの仕組みと検知と対策、設置の経路からログとプロセスでの見つけ方、発見時の初動まで

対象の目安: Webサーバーを運用するエンジニア / Webアプリ開発者 / 実務

リク・編集長 / セキュリティ全般・戦略
・ 約22分で読めます
Webシェルの仕組みと検知と対策、設置の経路からログとプロセスでの見つけ方、発見時の初動まで

Webアプリの脆弱性が悪用されたとき、攻撃者がまず残していくものの代表がWebシェルです。Webシェルは、HTTPのリクエストを受けてサーバー上でコマンドを実行させる不正なスクリプトで、多くはWebサーバーの公開ディレクトリにファイルとして置かれます。脆弱性を修正した後も、置かれたWebシェルが残っていれば、攻撃者は同じサーバーへ何度でも戻ってこられます。

2026年10月27日には、Adobe CommerceとMagento Open Sourceで危険な種類のファイルのアップロードを許すCVE-2026-48356(CWE-434)が、CISAのKEV(悪用が確認された脆弱性の一覧)に追加されました。NVDの説明は、任意のコードの実行につながりうるとしています。アップロードの不備のように、サーバーへファイルを書き込める脆弱性は、そのままWebシェルの設置につながります。この記事は、Webサーバーを運用するエンジニアに向けて、Webシェルの設置の経路、検知の方法、置かせない設計、見つけたときの初動を、MITRE ATT&CK、NSAとオーストラリアのASDのガイダンス、CISAの注意喚起をもとに整理します。

この記事のコマンドと設定の例は、自分が管理するサーバーでの検知と予防のためのものです。他者のサーバーに対する調査は、許可を得た範囲に限って行います。許可のないサーバーへの操作は、不正アクセス禁止法などの法令に触れるおそれがあります。

Webシェルが攻撃者にとって果たす役割

MITRE ATT&CKは、Webシェルを「公開されたWebサーバーに置かれ、ネットワークへの入口として使われるスクリプト」と説明し、永続化(Persistence)の手法T1505.003に分類しています。脆弱性を突いた侵入は一度きりの操作ですが、Webシェルを置けば、その後はふつうのHTTPのリクエストとしてサーバーを操作し続けられます。

Webシェルには大きく2つの形があります。ATT&CKの説明では、ブラウザから使える機能の一覧やコマンドラインの画面を持つものと、サーバー側には小さなスクリプトだけを置き、専用のクライアントのプログラムから操作するもの(例としてChina Chopperが挙げられています)があります。後者のサーバー側のスクリプトはごく短いため、ファイルを目で見ても気づきにくくなります。

CISAの注意喚起TA15-314Aは、Webシェルはサーバーが対応する言語なら何でも書けるとし、よく使われる言語としてPHPとASPを挙げ、Perl、Ruby、Python、Unixのシェルスクリプトも使われるとしています。Javaのアプリケーションサーバーでは、JSPのファイルとして置かれます。攻撃者はWebシェルを使って、次のことを行います。

  • サーバー上のファイルの閲覧、追加、削除と、コマンドの実行
  • 設定ファイルに書かれたデータベースのパスワードやAPIのキーの持ち出し
  • 追加のマルウェアの設置
  • 内部のネットワークへの中継と、ほかのサーバーへの横展開

Webシェルが設置される経路

TA15-314Aは、Webシェルの設置によく使われる経路として、次のものを挙げています。

経路設置の機構
ファイルアップロードの不備拡張子や保存先の検証が甘く、スクリプトのファイルをそのまま公開ディレクトリへ置ける(CWE-434)
CMSやプラグインの脆弱性WordPressなどのCMSや拡張機能の脆弱性を突き、ファイルを書き込む
ファイルインクルード(RFI、LFI)外部や別の場所のファイルを、スクリプトとして読み込ませる
露出した管理画面初期のパスワードや推測しやすいパスワードの管理画面に入り、テーマの編集やアップロードの機能でファイルを置く
SQLインジェクション、XSSほかの手法と組み合わせて、管理者の権限やファイルの書き込みにつなげる

TA15-314Aは、これらの手法が組み合わせて使われることも多いと書いています。たとえば露出した管理画面に入れても、ファイルを置くにはアップロードの機能が必要になります。ここに加えて、任意のコードを実行できる脆弱性(RCE)を突かれた場合は、最初の一回のコマンドでWebシェルのファイルを書き込まれます。新しいファイルを置く代わりに、既存の正規のPHPファイルへ数行を書き足したり、アップロード先へ設定ファイル(.htaccessや.user.ini)を置いてスクリプトの扱いを変えたりする形もあるため、新しいファイルだけを探しても見落とします。

アップロードの機能で、Webシェルを受け付けないための検証と保存の設計は、次の記事で解説しています。

あわせて読みたい

ファイルアップロード機能の脆弱性と対策。Webシェルを置かせないための検証と保存の設計

Webシェルが見つけにくい理由

Webシェルは、既存のWebアプリに紛れ込むように作られます。NSAのツール群の解説(NSAとASDのガイダンス「Detect and Prevent Web Shell Malware」の付属資料)は、見つけにくさの理由として次の点を挙げています。

  • 改変しても機能を失わないため、ファイルのシグネチャを簡単に避けられる
  • 通信を暗号化や符号化して、ネットワークの検知を避ける
  • まれに、ファイルを残さずメモリの上だけで動き、ファイルを探す方法では見つけられない

TA15-314Aも、単純なWebシェルでも影響は大きく、痕跡は最小限にとどまることが多いと書いています。そのため、1つの方法に頼らず、ファイル、ログ、プロセス、ネットワークの複数の観点を重ねて検知します。

既知の正常な版とファイルを比べる

NSAの解説は、多くのWebシェルを見つける最も効果的な方法として、本番のWebサーバーのファイルを既知の正常な版と比べることを挙げています。既知の正常な版とは、同じアプリを同じ版で新しく展開し、更新を当てたものです。比べた結果に出てくる追加と変更のファイルを、1つずつ確かめます。

# 既知の正常な版(同じ版を新しく展開したもの)と本番を比べ、追加と変更のあったファイルを一覧にする
diff -r -q /srv/known-good/example/ /var/www/example/

# アップロード先や画像のディレクトリに、スクリプトや設定ファイルがないかを確かめる
find /var/www/example/public/uploads -type f \( -iname '*.php*' -o -iname '*.phtml' -o -iname '*.phar' -o -name '.htaccess' -o -name '.user.ini' \) -ls

# 調査の起点の日付より後に作られたか変更されたスクリプトを探す
find /var/www/example -type f \( -name '*.php' -o -name '*.phtml' \) -newermt '2026-10-20' -ls

ファイルの更新日時は攻撃者が書き換えられるため、日時での絞り込みは補助として使い、比較の主役はファイルの中身(ハッシュ)にします。WordPressでは、WP-CLIのwp core verify-checksumsとwp plugin verify-checksums --allで、本体とプラグインのファイルを公式のチェックサムと照合できます。

この比較を継続的に行うのが、ファイル整合性監視(FIM)です。TA15-314Aは、公開するコンテンツをファイル整合性の仕組みと変更管理の方針で監視するよう勧めています。Linuxでは、auditdでWebのディレクトリへの書き込みと属性の変更を記録できます。

# /etc/audit/rules.d/webroot.rules
# Webのディレクトリ以下への書き込み(w)と属性の変更(a)を記録する
-a always,exit -F arch=b64 -F dir=/var/www/example -F perm=wa -k webroot_change
-a always,exit -F arch=b32 -F dir=/var/www/example -F perm=wa -k webroot_change
# rules.dのルールを読み込み、登録されたことを確かめる
augenrules --load
auditctl -l

auditctlのマニュアルは、-wで書く古い形式は互換性のために残されたもので、性能の面から非推奨としているため、ここでは-F dirを使う形式で書いています。記録はausearch -k webroot_change -iで確かめます。デプロイのたびに正規の変更も記録されるため、デプロイの作業と時刻が一致しない書き込みや、Webサーバーのユーザーによるスクリプトの作成を通知の対象にします。

Webサーバーのログで異常なアクセスを探す

Webシェルは、置いた攻撃者だけがアクセスするURIです。NSAの解説は、手がかりとして次の3点を挙げています。

  • User-Agent: 侵入した直後の攻撃者は、そのサーバーでふつうに使われるUser-Agentを知らないため、まれなUser-Agentでアクセスしがち
  • Referer: 多くのページへのリクエストにはRefererが付くが、Webシェルへのアクセスでは付け忘れられやすい
  • IPアドレス: 社内からしか使わないアプリなのに、ふだんと違うネットワークからアクセスがある

TA15-314Aは、JPGのファイルへPOSTのリクエストが送られているような、本来ありえない通信に注目するよう勧めています。Apacheのcombined形式のアクセスログなら、次のように確かめられます。

# 画像やCSSなど、本来GETで読むファイルへのPOSTを探す
awk '$6=="\"POST" && $7 ~ /\.(jpe?g|png|gif|ico|css|txt)(\?|$)/ {print $1, $4, $7, $9}' /var/log/apache2/access.log

# POSTを受けたURIを、アクセス元のIPアドレスの数が少ない順に並べる
awk '$6=="\"POST" {split($7,u,"?"); print u[1], $1}' /var/log/apache2/access.log \
  | sort -u | awk '{c[$1]++} END {for (k in c) print c[k], k}' | sort -n | head -30

NSAの解説は、この方法は多くの環境で誤検知が多く、手法を知る攻撃者には簡単に避けられるとも書いています。ログの分析は、ほかの検知と組み合わせる前提で使います。NSAのリポジトリには、IISのログ向けのPowerShellのスクリプト、Apacheのログ向けのPythonのスクリプト、Splunkの検索例が置かれています。

Webサーバーが起動する子プロセスを監視する

Webシェルでコマンドを実行すると、Webサーバーのプロセスの子として、ふだんは起動しないプログラムが動きます。NSAの解説は、Webシェルはふつう、Webサーバーのプロセスにいつもと違う振る舞いをさせると書いています。ATT&CKの検知の戦略(DET0394)も、Webのディレクトリでの予期しないファイルの作成と、その後にWebサーバーのプロセスがシェルやスクリプトのインタプリタを起動する流れを、検知の対象に挙げています。

環境親のプロセス疑わしい子プロセスの例(NSAの一覧から抜粋)
Windows(IIS)w3wp.execmd.exe、powershell.exe、whoami.exe、net.exe、ipconfig.exe、certutil.exe、nltest.exe
Linux(Apache)httpd、apache2whoami、uname、hostname、ls、cat、netstat、ifconfig、ip、crontab

PHP-FPMを使う構成では、PHPのコードはWebサーバーではなくphp-fpmのプロセスで動くため、php-fpmの子プロセスとプールのユーザーも監視の対象にします。JavaのアプリケーションサーバーではJavaのプロセスが親になります。

Windowsでは、SysmonのイベントID 1(プロセスの作成)を記録し、親のプロセス(ParentImage)がC:\Windows\System32\inetsrv\w3wp.exeのものを抽出します。イベントID 11(ファイルの作成)を組み合わせると、Webのディレクトリにファイルが作られた直後のプロセスの起動も追えます。Linuxでは、NSAの解説に沿って、Webサーバーのユーザーのuidで実行されたexecveをauditdで記録します。

# /etc/audit/rules.d/webserver-exec.rules
# uid=33はDebian系のwww-dataの例。apachectl -Sやidコマンドで実際のuidを確かめる
-a always,exit -F arch=b64 -F uid=33 -S execve -k webserver_exec
-a always,exit -F arch=b32 -F uid=33 -S execve -k webserver_exec
# Webサーバーのユーザーが起動したプログラムを一覧にする
ausearch -k webserver_exec -i | grep -o 'exe=[^ ]*' | sort | uniq -c | sort -n

正規のアプリが画像の変換やメールの送信で外部のコマンドを呼ぶこともあるため、まず一定期間の記録をとって、ふだん起動するプログラムの一覧を作ります。そのうえで、一覧にないプログラムの起動を通知します。EDRを導入している場合は、Webサーバーのプロセスを親とする調査用のコマンドの起動を検知するルールが用意されていることが多いため、Webサーバーにも忘れずにEDRを入れ、通知の宛先を決めておきます。正規のツールを使う攻撃の見分け方は、次の記事で解説しています。

あわせて読みたい

環境寄生型攻撃(LOLBins)の仕組みと振る舞いで見つける検知設計

シグネチャとネットワークの検知を補助に使う

ファイルのシグネチャは、手を加えていない一般的なWebシェルを見つけるのに役立ちます。NSAのリポジトリにはYARAのルールが2組あります。coreは一般的なWebシェルに特有の特徴だけを含み、誤検知が少ない代わりに変種を見逃しやすいものです。extendedは難読化や動的な実行などの技法も含むため、検出の幅が広い一方で誤検知が多くなります。NSAの解説は、coreの結果から先に確かめるよう勧めています。

ネットワークの面では、通信の流れの異常が手がかりになります。Webシェルを中継に使われたWebサーバーは、内部のネットワークのサーバーへ自分からリクエストを送り始めます。Webサーバーから内部への接続、Webサーバーから外部の見慣れない宛先への接続を、ファイアウォールやプロキシのログで監視し、必要な宛先以外への通信は許可しない設定にしておくと、検知と抑止の両方に効きます。

アップロード先でスクリプトを実行させない

予防の第一歩は、ファイルを置かれても実行させないことです。アップロードしたファイルは、公開ディレクトリの外に保存してアプリ経由で返す設計が安全です。公開ディレクトリの中に置く必要がある場合は、Webサーバーの設定で、そのディレクトリのスクリプトを実行させないようにします。

Apacheでは、アップロード先のディレクトリで.htaccessによる設定の上書きを禁止し、スクリプトの拡張子へのアクセスを拒否します。攻撃者が.htaccessを置いてPHPの扱いを変える手口を防ぐため、AllowOverride NoneとAllowOverrideList Noneを指定します。Apacheのマニュアルでは、この2つがともにNoneのとき.htaccessは読まれません。

<Directory "/var/www/example/public/uploads">
    # .htaccessによる設定の上書きを禁止する
    AllowOverride None
    AllowOverrideList None
    Options -ExecCGI -Includes
    # スクリプトの拡張子へのアクセスを拒否する(大文字と小文字を区別せず、shell.php.jpgのような複数の拡張子も対象にする)
    <FilesMatch "(?i)\.(php\d*|phar|phtml|pht)(\.|$)">
        Require all denied
    </FilesMatch>
</Directory>

Apacheは1つのファイル名に複数の拡張子を認めるため、AddHandlerでPHPを割り当てている構成では、shell.php.jpgのような名前のファイルもPHPとして扱われることがあります。末尾の拡張子だけを見る正規表現では、この名前を拒否できません。

nginxでは、アップロード先を^~の接頭辞のlocationで受けると、PHPのファイルをFastCGIへ渡すための正規表現のlocationが、そのパスには使われなくなります。中では、スクリプトの拡張子へのリクエストを拒否します。

location ^~ /uploads/ {
    # PHPのハンドラへ渡さず、静的なファイルとしてだけ返す
    location ~* \.(php|phar|phtml|pht)$ {
        return 403;
    }
}

設定の後は、無害なテキストを.phpや.php.jpgの名前でアップロード先に置き、ブラウザからのアクセスが403になること、中身が実行されないことを確かめます。PHP-FPMの設定やPHPのハンドラの割り当ては構成ごとに異なるため、自分の構成で実際に試して確かめます。

アプリのコードをWebサーバーから書き込めないようにする

Webシェルの多くは、Webのディレクトリにファイルを新しく作るか、既存のファイルを書き換えて置かれます。NSAの解説も、Webシェルはほぼ必ず、既存のファイルの変更か、Webからアクセスできるディレクトリへの新しいファイルの作成に頼ると書いています。Webサーバーのユーザーがアプリのコードに書き込めなければ、ファイルを置ける場所は大きく減ります。

  • アプリのコードは、デプロイ用のユーザーが所有し、Webサーバーのユーザーには読み取りだけを許す
  • Webサーバーのユーザーが書き込めるのは、アップロード先、キャッシュ、セッションなど、必要なディレクトリだけに限る
  • 書き込めるディレクトリでは、スクリプトを実行させない(前の章の設定)
  • CMSの管理画面からテーマやプラグインのファイルを編集する機能は、使わないなら無効にする(WordPressならDISALLOW_FILE_EDIT)
# アプリのコードはデプロイ用のユーザー(deploy)が所有し、Webサーバーのグループには読み取りだけを許す
chown -R deploy:www-data /var/www/example
find /var/www/example -type d -exec chmod 750 {} +
find /var/www/example -type f -exec chmod 640 {} +

# 書き込みを許すのは、アップロード先とキャッシュのディレクトリだけにする
chown -R www-data:www-data /var/www/example/storage /var/www/example/public/uploads

この例は、PHPのソースのように実行権限のいらないファイルを前提にしています。一括で640にすると、アプリに含まれるCLIのスクリプトや補助のプログラムの実行権限も外れるため、実行が必要なファイルは個別に権限を戻し、適用の前に現在の権限を控えて、適用の後にアプリの動作を確かめます。

TA15-314Aも、Webサーバーで最小権限の設定を使い、特定のディレクトリでのファイルの作成と実行を制御するよう勧めています。MITRE ATT&CKの緩和策(M1018)も、Webのディレクトリを変更できるアカウントを、認められたものだけに限ることを挙げています。権限を絞る考え方の全体像は、次の記事で解説しています。

あわせて読みたい

最小権限の原則(Least Privilege)。なぜ権限を絞ることが最強の防御の一つなのか

PHPでは、disable_functionsでsystemやexec、shell_exec、passthru、proc_open、popenなどの関数を無効にすると、Webシェルからコマンドを実行しにくくなります。ただし、PHPのマニュアルは、この設定は回避されうるため、共有ホスティングの環境で十分な対策とみなすべきではないと警告しています。またevalは関数ではなく言語の構文(language construct)のため、disable_functionsの対象になりません。関数の無効化は、書き込みと実行の制限の上に重ねる対策として使います。

このほか、TA15-314Aは次の対策を挙げています。

  • アプリとOSの更新を定期的に当てる。設置の経路の多くは既知の脆弱性です
  • 管理画面へのアクセス元を絞り、初期のパスワードを使わない
  • リバースプロキシやmod_securityなどで、受け付けるURLのパスを既知の正規のものに限る
  • 既知の正常なサーバーの版を、オフラインのバックアップとして残しておく
  • 公開するサーバーと社内のネットワークの間にDMZを置き、その間の通信を記録する

Webシェルを見つけたときの初動

Webシェルを見つけたら、すぐに削除したくなりますが、削除すると侵入の時期と経路を調べる手がかりが消えます。Webシェルが1つ見つかったときは、ほかにも置かれていることや、すでに認証情報を持ち出されていることを前提に動きます。

  1. 1

    ファイルとログを保全する

    Webシェルのファイルを、ハッシュ、所有者、更新日時とあわせて別の場所へ複製します。Webサーバーのアクセスログ、エラーログ、auditdやSysmonの記録も、ローテーションで消える前に保全します。サーバーはすぐに電源を落とさず、通信の遮断などで隔離してから調べます。

  2. 2

    Webシェルへのアクセスを洗い出す

    アクセスログで、Webシェルのパスへの最初のアクセスの時刻と、アクセス元のIPアドレス、User-Agentを確かめます。同じIPアドレスやUser-Agentがほかにどのパスへアクセスしたかをたどると、別のWebシェルが見つかることがあります。POSTの本文はふつうログに残らないため、実行されたコマンドはauditd、Sysmon、EDRの記録で確かめます。

  3. 3

    侵入の経路を特定する

    Webシェルのファイルが作られた時刻の直前のリクエストを調べ、アップロードの機能、CMSやプラグインの脆弱性、管理画面のログインなど、どの経路で書き込まれたかを特定します。経路を塞がないまま作り直すと、同じ手口で再び置かれます。

  4. 4

    ほかのWebシェルと永続化を探す

    既知の正常な版との比較をサーバー全体で行い、追加と変更のファイルをすべて確かめます。書き換えられた正規のファイル、.htaccessや.user.ini(auto_prepend_fileで別のファイルを読み込ませる設定)、cronの登録、新しいユーザーやSSHの公開鍵の追加も調べます。

  5. 5

    認証情報を変える

    Webサーバーのユーザーが読めた設定ファイルの中身は、持ち出されたものとして扱います。データベースのパスワード、APIのキー、クラウドの認証情報、セッションの署名に使う鍵を変更し、古い値を無効にします。

  6. 6

    既知の正常な状態から作り直す

    Webシェルを消すだけで済ませず、既知の正常な版からアプリを展開し直し、侵入の経路の脆弱性を修正します。cronやユーザー、SSHの公開鍵の追加、権限の昇格の痕跡など、アプリの外にまで手が入っていた場合は、アプリの再展開だけでは足りないため、OSやコンテナごと信頼できるイメージから作り直します。作り直した後は、前の章のファイル整合性監視とプロセスの監視を入れて、再設置に気づける状態にします。

インシデントの初動全体の進め方、組織の連絡体制と証拠保全の順序は、次の記事で解説しています。

あわせて読みたい

インシデント発生時の初動対応。最初の1時間で何をするか

対応チェックリスト

  • 本番のWebのディレクトリを、既知の正常な版と定期的に比べているか
  • Webのディレクトリへの書き込みを、ファイル整合性監視やauditdで記録し、デプロイ以外の変更を通知しているか
  • アクセスログで、画像ファイルへのPOSTや、少数のアクセス元からしか使われないURIを確かめているか
  • Webサーバーやphp-fpmのプロセスが起動する子プロセスを、EDR、Sysmon、auditdで監視しているか
  • アップロード先で、スクリプトの実行と.htaccessによる設定の上書きを禁止しているか
  • アプリのコードをWebサーバーのユーザーから書き込めないようにし、書き込めるディレクトリを限っているか
  • Webサーバーから内部や外部への、必要のない通信を許可していないか
  • Webシェルを見つけたときに、削除の前に保全と経路の調査を行う手順を決めているか

Webシェル対策は書き込みと実行の分離から始まる

Webシェルは、脆弱性が悪用された後に残る結果です。脆弱性の修正を急ぐことに加えて、Webサーバーが書き込める場所と、スクリプトを実行できる場所を重ねない設計にしておくと、脆弱性を突かれてもWebシェルが動く余地は小さくなります。そのうえで、ファイルの比較、ログ、プロセスの監視を重ね、置かれたときに早く気づける状態を保ちます。ログの収集と保管の設計は、次の記事にまとめています。

あわせて読みたい

ログ管理の基本。何を・どこまで・どれだけ残すか

出典・参考

この記事をシェア

関連する記事

セキュアコーディング

ファイルアップロード機能の脆弱性と対策。Webシェルを置かせないための検証と保存の設計

ファイルアップロード機能の脆弱性(CWE-434)を、Webシェルによるリモートコード実行や保存型XSSといった悪用の機構から整理します。拡張子の許可リストに内容の実体検証を重ね、Content-Typeとファイル名を信用せず、公開ディレクトリの外へ保存してスクリプト実行を無効化する、という検証と保存の設計をWebアプリ開発者向けに解説します。

脆弱性・CVE解説

Adobe CommerceとMagentoのCVE-2026-48356とは、ゲストカートからのファイルアップロードとKEV追加

Adobe CommerceとMagento Open SourceのCVE-2026-48356は、カートの商品オプションを通じて危険なファイルをアップロードできる脆弱性(CWE-434)です。CISAは10月27日にKEVへ追加しました。影響版とAPSB26-73の個別パッチ、pub/mediaの実行禁止などの暫定策、侵害の点検手順を一次情報で整理します。