
Joomla website hacked via YOOtheme Pro, just before security patch was released
On 21 October I wrote about two critical security updates on one day: PHP (PHP2Shell) and YOOtheme Pro. What I did not know at that moment: one of my own websites was already hacked that same morning, via exactly this vulnerability in YOOtheme Pro. This website was completely up to date. Joomla 6.2 and all extensions were updated. But still it was hacked.
In this blog you can read how the hack worked, how I investigated and fixed it, and what I will do differently from now on. At the bottom you will find a checklist to check your own websites.
1. My first scare: was it PHP2Shell?
I discovered the hack on Wednesday 22 October, during maintenance work on the website. That was one day after both the PHP vulnerability (PHP2Shell) and the YOOtheme vulnerability became public. So my first thought was: this can be the PHP2Shell issue. That vulnerability is in PHP itself, so it affects every PHP application on the server. In the worst case not only this website, but the complete VPS server where the website runs on, was compromised. My fear became bigger when I could not log in via SSH anymore. That felt like the scenario that every administrator is most afraid of: an attacker who has taken over the complete server and then locks you out.
After more investigation the cause was much more innocent. On the server runs fail2ban, which blocks suspicious behaviour on the server. I first analysed the website via FTP and made a lot of connections. Fail2ban saw that as suspicious behaviour and blocked my own IP address. So fail2ban did its job well.
When I had access again, I first checked the server itself:
- In the log files I could see no other SSH logins than my own.
- There were no new cronjobs created, which can start certain programs in the background at certain times (for example "sleeping" backdoor scripts).
- In
~/.ssh/authorized_keysthere were no unknown keys that can be used to log in to the server remotely. - The other websites on the same server are well separated from each other and were not affected. Only this one website was hacked.
So the server was not taken over. The attacker came in via another way, but not via the PHP2Shell issue.
2. Timeline of the hack
I copied the hacked website and all log files of the last year to my own computer. By analysing it there locally, I could reconstruct the course of the hack quite precisely (times in Dutch time):
| When | What |
|---|---|
| 1 – 19 October | The website is continuously visited by automated scans for known vulnerabilities in WordPress and Joomla extensions (JCE, Astroid, upload tricks). All of them failed. |
| 21 Oct, 08:44 | The vulnerability in YOOtheme Pro is exploited. A backdoor (webshell) is placed in /images/. |
| 21 Oct, 12:12 | The vulnerability is exploited again. |
| 21 Oct, ± 15:00 | YOOtheme releases the patch (version 5.0.53), one hour earlier than the planned release at 16:00. |
| 21 Oct, 15:11 | I updated YOOtheme Pro to 5.0.53. The vulnerability is closed and later attempts are refused. But the backdoor script of the hacker was already on the server. |
| 21 Oct, 23:34 | The backdoor is used from all kinds of IP addresses. |
| 22 Oct, 00:17 | The first extra webshells and test files appear in the root. |
| 22 Oct, 06:48 | Hidden payloads in fake folders and fake images. Also about 5,600 .htaccess files appear in all folders. |
| 22 Oct, 09:25 | Google gets to see spam pages: index.php is replaced and robots.txt is modified. |
| 22 Oct, 14:31 | A second group places its own webshell. |
| 22 Oct, 17:41 | A third group removes the shells of the others and places its own shells. In the process they also empty some real library files. |
| 22 Oct, 17:55 | I discovered the hack during maintenance work. I immediately took the website offline, investigated and restored it. |
The most important point in this timeline: the break-in happened in the morning, more than six hours before the security update of YOOtheme was available. Even the fastest update procedure could not have prevented this. The release was planned for 16:00, but at 15:10 I heard from international Joomla colleagues that the patch was already released around 15:00. I had updated the website at 15:11, about ten minutes after the release. But an update does not remove a backdoor that is already placed.
3. How the hack worked
3.1 The vulnerability in the image processing of YOOtheme Pro
YOOtheme Pro has a function that edits images and stores them in a cache. In the vulnerable versions (5.0.0 up to and including 5.0.52) an attacker could use a specially crafted URL to do two things at the same time:
- let the website download a file from an external server (parameter
FROMFILE); - save that file on a location and with an extension of their own choice (parameter
SAVE).
The attacker let the website fetch an "image" with hidden PHP code in it. This was saved as a .php file in /images/. On the server PHP was allowed to run in that folder. With that the attacker immediately had a working backdoor, with access to all files and, via configuration.php, also to the database.
3.2 The backdoor was shared or sold
After the first break-in it did not stay with one attacker. In the log files I saw at least three different groups that each placed their own webshells. They were fighting with each other for the website. One group emptied the shells of the other groups and placed a .htaccess file in every folder that blocked PHP, except for their own files. A .htaccess file contains settings for Apache webservers that determine what is allowed and what is not allowed within a folder on the webserver. But this server runs nginx, and nginx ignores .htaccess. So those more than 5,600 files had no effect at all.
3.3 The goal: SEO spam via cloaking
The final goal was to make money with search engine spam. With this, search engines like Google are misled to lure visitors to unwanted or misleading pages. The index.php of Joomla was replaced by a script that first checks who requests the page:
- Normal visitors saw the normal website. Because of that the hack was not noticed immediately.
- Googlebot and Bingbot got to see Japanese spam pages of a fake webshop, fetched live from the server of the attacker.
Also robots.txt was modified with a reference to a spam sitemap. Within a few hours Google had crawled hundreds of spam URLs, like /?word/123456789012 and /?c=code. In the search results they even appeared with prices, star ratings and a recent date. That data did not come from Google, but from the fake HTML itself.
4. What I found
- About 15 webshells and backdoors, spread over among others
/images/, its subfolders,/administrator/templates/,/components/and/libraries/. - Fake folders with random names in the root, and fake folders deep in the website with an existing name that was repeated one more time (for example
.../com_jedchecker/com_jedchecker/). - Encrypted payloads disguised as
.png,.bmp,.mp3and.jpg, with a PHP loader next to them. - A replaced
index.phpwith the SEO spam script, and a modifiedrobots.txt. - A
php.iniin/images/that tried to disable the PHP security. - About 5,600
.htaccessfiles. - Five legitimate library files that were emptied.
I compared all PHP and JavaScript files with the official Joomla 6.2.0 package. Apart from index.php, the .htaccess files and the emptied files, the Joomla core was untouched. In the Joomla administrator I found no evidence at all that someone had logged in. The attackers also did not need that: via the webshells they could already do everything.
5. How I fixed it
5.1 First secure everything
- I moved all hacked files and folders completely to a separate folder and set the permissions on it to
000. In this way the malicious files could not be opened or executed anymore, but they were still kept for further investigation. - I temporarily placed a "website in maintenance" message with logo and contact information for visitors.
- I copied all log files to my own computer. Without those logs I could never have found the exact moment of the break-in and the attack method.
5.2 Analyse locally
I investigated the hacked files and log files on my own computer. I did that in an isolated Docker environment, in which I could run a separate webserver with the website. In this way I could safely and calmly reconstruct what had happened, compare the files with a clean Joomla installation and map all backdoors (webshells), IP addresses of attackers and spam URLs.
5.3 Restore a clean backup, but which one?
I decided not to clean up the website file by file. With three or more attackers and encrypted payloads in fake folders, the chance is too big that you miss something. That is why I restored a backup.
But there is a pitfall. On 21 October I made two backups with Akeeba Backup, but both were made after the break-in. So they already contained the backdoor. That is why I used the last full backup from before the hack, from 13 October. Always check that the file of the first backdoor is not in the backup before you restore it.
I restored the backup in a completely empty webroot. If you restore a backup over the existing files, then the webshells, fake folders and .htaccess files just stay there. A few articles that were published after 13 October, I copied manually from the database and put them back in the clean website.
One day later, on 23 October, YOOtheme advised in an e-mail to restore a backup from 19 October or earlier in case of a hack, or from before the first suspicious request in the log files, and to update YOOtheme Pro immediately after that.
5.4 Update everything immediately
A backup from before the hack also contains the vulnerable version of YOOtheme Pro. That is why I updated everything before the website was publicly accessible again: YOOtheme Pro 5.0.53, JCE, Akeeba, the YourSites client and so on. Scanners are still trying the vulnerability, so this really has to be done first.
After that I checked that the restored website is clean:
- the Joomla core is identical to the official package;
- there are no webshells and no PHP files in
/images/; - there are no unknown users or extensions;
- there are no publicly writable files anymore.
5.5 Replace passwords and secrets
Assume that everything that PHP could read, could also have ended up in the hands of the attacker. That is why I replaced the following:
- a new database, database user, password and table prefix;
- a new Joomla secret.
The Joomla secret is a secret security key that Joomla uses to protect certain data and security codes; - a new Akeeba Secret Word;
- a new FTP password for the backup server;
- a new YourSites password.
With YourSites I monitor, manage and update Joomla websites remotely; - a new SMTP password (requested).
5.6 Remove the spam URLs from Google
After restoring the backup, the spam pages are gone. But Google does not know that yet. Most spam URLs are query strings on the homepage. Joomla ignores those and just shows the homepage with status 200. Because of that the URLs stay in the index for a long time.
That is why I configured nginx so that these URLs return an HTTP status code 410 Gone. With that your website tells Google that the page was removed on purpose. A 410 disappears faster from the index than a 404.
# Spam URLs from the hack: let search engines know they are gone
if ($args ~* "^([a-z-]+/[0-9]{8,}|c=[a-z0-9]{10,})$") { return 410; }
location ~* "^/(inx_sitemap\.xml|[a-z_-]*sub[a-z0-9_-]*\.xml)$" { return 410; }
Adjust the patterns to what you see in your own log files, and check that your real URLs do not match them. Do not block the spam URLs via robots.txt: Google must be able to crawl them to see the 410.
In Google Search Console and Bing Webmaster Tools you then do the following:
- Remove unknown owners and users.
Attackers can give themselves access to your Google Search Console and Bing Webmaster Tools by placing an HTML verification file on the hacked website. - Remove unknown sitemaps and submit your real sitemap again.
- Request a temporary removal for all URLs with the prefix
https://yourdomain.com/?. - Check under "Security issues" and "Manual actions" if the website is flagged, and request a review if needed.
Expect that it takes two to eight weeks before all spam has disappeared from the search results.
6. What I learned from it
6.1 Up to date is not enough
This website was updated automatically, and that closed the vulnerability on the same day. But the attack came before the update. Against such a zero-day only extra security layers can help. But updating is still necessary: without the update the vulnerability would have been open much longer.
6.2 No PHP in upload folders
The whole hack was about one thing: PHP was allowed to run in /images/. If PHP had been blocked there, the placed file would have been harmless. Note: if you run nginx, you have to configure this in the nginx configuration, because .htaccess does not work there:
location ~* ^/(images|media|cache|tmp)/.*\.(php|phtml|phar|php\d?)$ { deny all; }
6.3 File permissions and PHP settings
- Folders
755, files644,configuration.phpon444. No publicly writable files. - Use
disable_functionsandopen_basedir, and ignorephp.iniand.user.inifiles per folder.
6.4 Notice it faster
The first backdoor was there for more than a day before I discovered the hack during maintenance work. Because of the cloaking, normal visitors did not see anything special. With monitoring for new .php files or modified core files I would have known it within a few hours. For example with a cronjob that compares checksums. A Web Application Firewall (WAF) could also have stopped requests with ../ and an external URL in the parameters.
6.5 Keep your log files and your backups
Without log files of a complete year I could never have seen exactly when and how the attacker came in. And without a backup of more than one week old I could not have restored a clean version. Make sure that your backups go back far enough, and that they are stored outside the server.
7. Do you use YOOtheme Pro 5.x? Check your website!
Do not only check if you have installed YOOtheme Pro 5.0.53 or higher. Also check if attackers already got in before you did the update. The vulnerability is registered as CVE-2026-102785 and has the highest risk score: 10 out of 10.
- Search for attack attempts in the access logs.
YOOtheme advises to search for
yootheme,cache,FROMFILEandSAVE. Start with the two most suspicious terms:grep -Ei 'FROMFILE=|SAVE=' access.log*For a broader check:
grep -Ei 'yootheme|cache|FROMFILE|SAVE' access.log*Especially requests with
option=com_ajax,FROMFILE=andSAVE=are suspicious. The broader search can return a lot of normal requests, because yootheme and cache also appear in normal website traffic. - Search for suspicious PHP files.
Especially check
media/yootheme/cache. According to YOOtheme the first malicious files can be placed there. After that, attackers can spread more files over the website.find images media cache tmp assets -type f -iname "*.php*" 2>/dev/nullAlso check other folders. YOOtheme mentions
assets/images/accesson.phpas an example of a backdoor (webshell). Not all attackers use the same file names or locations. - Count the .htaccess files.
A normal Joomla installation usually has only a few of them, depending on the configuration and extensions.
find . -name .htaccess | wc -lA sudden large number can indicate a hack. On an nginx server these files have no effect, but they can be a clear trace of the attackers.
- Check index.php and robots.txt.
Compare these files with a clean Joomla installation of the same version. Look for unknown code, suspicious changes and unexpected modification dates. Keep in mind that robots.txt can also contain legitimate, website specific changes.
- Check Google for search engine spam.
Search in Google with
site:yourdomain.comfor strange search results, for example Japanese texts, unknown product pages or other content that does not belong to your website. Also check Google Search Console and Bing Webmaster Tools. - Check images that are not images.
Attackers can hide malicious PHP code in files that look like images. A first check:
file images/* | grep -v -i imageThis command only checks files directly in the
imagesfolder. For a more extensive check, including subfolders:find images media -type f -exec file {} + | grep -Ei 'PHP|script|text'This check can also show harmless text files and does not find every hidden webshell. A file that is recognised as an image can still contain malicious code.
Important: an update does not remove backdoors
YOOtheme Pro 5.0.53 closes the vulnerability, but does not remove malicious files that attackers placed before. So a website can still be hacked, even if the newest version is installed and everything seems to work normally.
You did not find anything suspicious? That is no guarantee that your website is clean. Attackers can have removed their traces or placed files on other locations. So also check the modification dates of files and, if in doubt, compare the website with a clean installation.
If you find signs of a hack, it is wise to take the website offline temporarily and to restore a backup from before the attack that is proven to be clean. Do this in a completely empty webroot, so that remaining malicious files do not stay behind. After that, update Joomla, YOOtheme Pro and all extensions, and replace passwords and security keys that may have been stolen.
More information: CVE-2026-102785 and the official YOOtheme Pro changelog, and What to do if your Joomla website is hacked.
Need help?
Do you think that your Joomla website was hacked via this vulnerability, or do you want to know for sure that it was not? Contact me. I am happy to help you with the investigation, the recovery and better securing your website.

Peter is a Joomla specialist en a Linux admin for fast, secure and scalable websites..









