psf-yootheme-check(8)
YOOtheme Pro 5 unauthenticated remote code execution: checker (v1)
CVE-2026-102785
If this software is helpful, consider a donation to our 501c3 nonprofit:
https://privacysafe.foundation
YOOtheme Pro is a widely used commercial theme and page builder for WordPress
and Joomla. Its publicly reachable image handler (the route that creates and
caches resized and cropped images) does not fully bind the normalized image
operations and the output destination to the request signature. An
unauthenticated attacker can reuse a signature disclosed in an ordinary public
image URL, inject extra image operations, traverse out of the intended cache
directory, and write an attacker-controlled PNG/PHP polyglot file into an
executable public media directory. That file is then requested to run code.
CVSS 10.0. Disclosed 21 to 22 October 2026. It is not listed as known
exploited, but a CVSS 10 pre-authentication flaw with a published
mechanism should be treated as likely to be exploited quickly.
Affected: YOOtheme Pro 5.0.0 through 5.0.52
Fixed: 5.0.53 (released 21 October 2026)
Not affected: YOOtheme Pro 4 and earlier (vendor statement)
Sources disagree slightly: the CVE record is filed against the Joomla extension
and one aggregator title says "5.0.0-5.0.55". The vendor's own notice, the
changelog and the CVE description text say 5.0.52 and earlier, fixed in 5.0.53,
for YOOtheme Pro 5 in general, so this script applies the rule to the WordPress
theme as well as the Joomla template.
This script is read-only. "check" finds YOOtheme Pro installs and reads their
version. "hunt" looks for what the attack leaves behind: PHP files inside
media, upload and cache directories, PHP code inside image files, and web log
requests that run such files. It sends nothing to your site and exercises no
part of the flaw.
Usage:
./yootheme-check.sh [command] [DIR ...]
check (default) scan DIR (default /var/www /srv /opt /home) for YOOtheme.
Exit 0 = nothing affected, 1 = an affected version found.
hunt check, then search for signs of the attack. Exit 0 = nothing
found, 1 = an indicator or an affected version was found.
mitigate [options] (root) changes the host. PrivacySafe protection
documented: refuses PHP files under uploads, media, images and
cache paths with a 403, so a file an attacker managed to write
cannot run. It does NOT stop the write: update to 5.0.53. It
only acts when an affected or unreadable YOOtheme is found
(--force overrides). Options:
--web auto|apache|nginx which web server (default auto: every
one found)
--dir DIR where to look for YOOtheme (repeat;
default /var/www /srv /opt /home)
--dry-run show the changes, change nothing
--reload graceful reload afterwards (systemd)
--force apply even if no affected install is found
Apache: writes psf-yootheme-deny-php.conf (conf-available and
a2enconf on Debian, conf.d on RHEL) and checks it with a
configtest. nginx: writes a snippet and adds an include as the
FIRST line of every server block that serves PHP (the first
matching regex location wins), then checks with nginx -t
(NGINX_MAIN_CONF=FILE checks a non-default main file). If the
web server rejects the result everything is undone at once.
The rule applies to EVERY PHP virtual host on the machine, and
it also blocks a legitimate PHP file requested directly under a
directory named cache, images, media or uploads.
rollback [--dry-run] [--reload] remove what mitigate added and restore the files
it edited.
version print the script version.
Environment: HUNT_WEB_LOGS (extra web access log paths for the hunt). STATE_DIR
(default /var/lib/psf-security-patches/yootheme) holds the backups and manifest.
Version reading: the script reads the standard WordPress theme header
(style.css, Version:) and the Joomla template manifest (templateDetails.xml,
<version>). If it cannot read a version it says so; the YOOtheme dashboard also
shows the version.
Containers: with no argument the check also looks in running docker and podman containers
(SCAN_CONTAINERS=1, the default; 0 skips them). A read-only probe copies only the first few KB of
each theme's style.css or templateDetails.xml, where the version is, into a private temporary
directory, and this script runs once per container in isolation against that copy. The hunt
command does not look inside containers. Paths shown as .../docker_NAME/... are inside the
container NAME.
PSF_LOCK_WAIT seconds a run that changes the host waits for another one that is already
changing it (default 30), then stops with exit 2; the lock is STATE_DIR/.lockThe Security Patches software and this documentation are dedicated to the public domain under CC0 1.0 Universal. Other pages on privacysafe.app/docs are CC BY-SA 4.0 unless the page says otherwise.