В этом блоге рассказывается, как сочетание уязвимости обхода пути и включения локального файла приводит к удаленному выполнению кода в ядре WordPress. Уязвимость оставалась открытой в ядре WordPress более 6 лет.
Злоумышленник, получивший доступ к учетной записи с как минимум привилегиями author на целевом сайте WordPress, может выполнить произвольный код PHP на базовом сервере, что приведет к полной удаленной передаче.
Уязвимость, описанная в этом посте, стала неиспользуемой другим исправлением безопасности в версиях 4.9.9 и 5.0.1. Однако обход пути все еще возможен и в настоящее время не исправлен. Любой сайт WordPress с установленным плагином, который неправильно обрабатывает записи Post Meta, может сделать использование еще доступным.
Согласно странице загрузки WordPress, Разработанные сайты на вордпресс используется более чем 33% на всех веб-сайтов в Интернете. Принимая во внимание, что плагины могут вновь вызвать проблему и принимая во внимание такие факторы, как устаревшие сайты, количество уязвимых установок по-прежнему исчисляется миллионами.
Когда изображение загружается на сайт WordPress, оно сначала перемещается в каталог загрузки ( wp-content/uploads). WordPress также создаст внутреннюю ссылку на изображение в базе данных, чтобы отслеживать метаинформацию, такую как владелец изображения или время загрузки.
Эта мета-информация хранится в базе данных Post Meta. Каждая из этих записей является парой ключ/значение, назначенной определенному идентификатору.
MariaDB [wordpress]> SELECT * FROM wp_postmeta WHERE post_ID = 50; +---------+-------------------------+----------------------------+ | post_id | meta_key | meta_value | +---------+-------------------------+----------------------------+ | 50 | _wp_attached_file | evil.jpg | | 50 | _wp_attachment_metadata | a:5:{s:5:"width";i:450 ... | ... +---------+-------------------------+----------------------------+
В этом примере изображению было присвоено значение post_ID50. Если в будущем пользователь захочет использовать или отредактировать изображение с указанным идентификатором, WordPress будет искать соответствующую мета-запись _wp_attached_file и использовать ее значение, чтобы найти файл в каталоге wp-content/uploads.
Проблема с этими записями Post Meta до WordPress 4.9.9 и 5.0.1 заключается в том, что можно было изменять любые записи и устанавливать для них произвольные значения.
Когда изображение обновляется (например, изменяется его описание), вызывается функция edit_post(). Эта функция напрямую воздействует на массив $_POST.
function edit_post( $post_data = null ) { if ( empty($postarr) ) $postarr = &$_POST; ⋮ if ( ! empty( $postarr['meta_input'] ) ) { foreach ( $postarr['meta_input'] as $field => $value ) { update_post_meta( $post_ID, $field, $value ); } }
Как видно, можно вводить произвольные записи мета- постов. Поскольку не проверяется, какие записи изменены, злоумышленник может обновить мета-запись _wp_attached_file и установить для нее любое значение. Это никоим образом не переименовывает файл, оно просто изменяет файл, который будет искать WordPress при попытке отредактировать изображение. Это приведет к обходу пути позже.
Обход пути происходит в функции wp_crop_image(), которая вызывается, когда пользователь обрезает изображение.
Функция берет идентификатор изображения для crop ( $attachment_id) и извлекает соответствующую запись _wp_attached_file мета-поста из базы данных.
Помните , что из – за дефекта в edit_post(), $src_file может быть установлен на что-нибудь.
function wp_crop_image( $attachment_id, $src_x, ...) { $src_file = $file = get_post_meta( $attachment_id, '_wp_attached_file' ); ⋮
На следующем шаге WordPress должен убедиться, что изображение действительно существует, и загрузит его. WordPress имеет два способа загрузки данного изображения. Первый – просто найти имя файла, предоставленное записью _wp_attached_file Meta Post в каталоге wp-content/uploads (строка 2 следующего фрагмента кода).
В случае сбоя этого метода WordPress попытается загрузить изображение со своего собственного сервера в качестве запасного варианта. Для этого будет сгенерирован URL-адрес для загрузки, состоящий из URL-адреса каталога wp-content/uploads и имени файла, хранящегося в записи _wp_attached_file мета-поста (строка 6).
Чтобы привести конкретный пример: если значение, сохраненное в записи _wp_attached_file Post Meta, было evil.jpg, то WordPress сначала попытается проверить, существует ли файл wp-content/uploads/evil.jpg. Если нет, то он будет пытаться загрузить файл со следующего URL: https://targetserver.com/wp-content/uploads/evil.jpg.
Причиной попытки загрузить изображение вместо локального его поиска является случай, когда какой-то плагин генерирует изображение на лету при посещении URL.
Обратите внимание, что здесь не проводится никакой дезинфекции. WordPress просто объединит каталог загрузки и URL с пользовательским вводом $src_file.
Как только WordPress успешно загрузит правильное изображение через wp_get_image_editor(), он будет обрезать изображение.
⋮ if ( ! file_exists( "wp-content/uploads/" . $src_file ) ) { // If the file doesn't exist, attempt a URL fopen on the src link. // This can occur with certain file replication plugins. $uploads = wp_get_upload_dir(); $src = $uploads['baseurl'] . "/" . $src_file; } else { $src = "wp-content/uploads/" . $src_file; } $editor = wp_get_image_editor( $src );
⋮
Обрезанное изображение затем сохраняется обратно в файловую систему (независимо от того, было ли оно загружено или нет). Полученное имя файла $src_file будет возвращено пользователем get_post_meta(), который находится под контролем злоумышленника. Единственное изменение, внесенное в результирующую строку имени файла, заключается в том, что к базовому имени файла добавляется cropped-(строка 4 следующего фрагмента кода). Чтобы следовать примеру evil.jpg, результирующее имя файла будет cropped-evil.jpg.
WordPress затем создает любые каталоги в результирующем пути, которые еще не существуют через wp_mkdir_p() (строка 6).
Затем он наконец записывается в файловую систему с использованием save()метода объекта редактора изображений. Метод save() также не выполняет Path Traversal проверки по данному имени файла.
⋮ $src = $editor->crop( $src_x, $src_y, $src_w, $src_h, $dst_w, $dst_h, $src_abs ); $dst_file = str_replace( basename( $src_file ), 'cropped-' . basename( $src_file ), $src_file ); wp_mkdir_p( dirname( $dst_file ) ); $result = $editor->save( $dst_file );
До сих пор мы обсуждали, что можно определить, какой файл загружается в редактор изображений, поскольку никакие проверки очистки не выполняются. Однако редактор изображений выдает исключение, если файл не является допустимым изображением. Первое предположение может заключаться в том, что тогда можно обрезать изображения только вне каталога загрузки.
Однако тот факт, что WordPress пытается загрузить изображение, если оно не найдено, приводит к уязвимости удаленного выполнения кода.
Локальный файл | Скачать HTTP | |
---|---|---|
Загруженный файл | evil.jpg | evil.jpg |
_wp_attached_file | evil.jpg? shell.php | evil.jpg? shell.php |
Результирующий файл, который будет загружен | WP-содержание / добавления / evil.jpg? shell.php | https://targetserver.com/wp-content/uploads/evil.jpg?shell.php |
Фактическое местоположение | WP-содержание / добавления / evil.jpg | https://targetserver.com/wp-content/uploads/evil.jpg |
Результирующее имя файла | Нет – загрузка изображения не удалась | evil.jpg? обрезаны-shell.php |
Идея заключается в том, чтобы установить _wp_attached_fileв evil.jpg?shell.php, что привело бы к запросу HTTP Делаем по следующему адресу: https://targetserver.com/wp-content/uploads/evil.jpg?shell.php. Этот запрос вернул бы действительный файл изображения, так как все, что после, ? игнорируется в этом контексте. Полученное имя файла будет evil.jpg?shell.php.
Однако, хотя метод save() редактора изображений не проверяет атаки Path Traversal, он добавляет расширение типа mime изображения, загружаемого к результирующему имени файла. В этом случае результирующее имя файла будет evil.jpg?cropped-shell.php.jpg. Это делает вновь созданный файл безвредным снова.
Тем не менее, все еще возможно поместить полученное изображение в любой каталог, используя полезную нагрузку, такую как evil.jpg?/../../evil.jpg.
Каждая тема WordPress представляет собой просто каталог, расположенный в каталоге wp-content/themes в WordPress, и предоставляет файлы шаблонов для разных случаев. Например, если посетитель блога хочет просмотреть сообщение в блоге, WordPress ищет файл post.php в каталоге текущей активной темы. Если он найдет шаблон, он сделает include().
Чтобы добавить дополнительный уровень настройки, можно выбрать собственный шаблон для определенных сообщений. Для этого пользователь должен установить запись _wp_page_template Post Meta в базе данных с таким пользовательским именем файла. Единственным ограничением здесь является то, что редактируемый файл include() должен находиться в каталоге текущей активной темы.
Обычно к этому каталогу нельзя получить доступ, и никакие файлы не могут быть загружены. Однако, злоупотребляя вышеописанным обходом пути, можно внедрить вредоносное изображение в каталог используемой в настоящее время темы. Затем злоумышленник может создать новую запись и использовать ту же ошибку, которая позволила ему обновить запись _wp_attached_file мета-поста в соответствии include() с изображением. Внедрив PHP-код в изображение, злоумышленник получает произвольное удаленное выполнение кода.
WordPress поддерживает два расширения для редактирования изображений для PHP: GD и Imagick. Разница между ними заключается в том, что Imagick вовсе не разделяет метаданных изображения EXIF, в котором PHP код может быть сохранен. GD сжимает каждое редактируемое изображение и удаляет все метаданные exif. Это результат того, как GD обрабатывает изображения.
Тем не менее, эксплуатация все еще возможна, если создать изображение, содержащее созданные пиксели, которые будут перевернуты таким образом, что это приведет к выполнению PHP-кода после того, как GD завершит обрезку изображения.
В этом посте подробно рассказывается об удаленном выполнении кода в ядре WordPress, которое присутствовало более 6 лет. Оно стало непригодным для использования с патчем для другой уязвимости, о которой сообщает RIPS в версиях 5.0.1 и 4.9.9. Однако обход пути все еще возможен и может использоваться, если установлен плагин, который все еще позволяет перезаписывать произвольные данные поста.
Мы хотели бы поблагодарить добровольцев из команды безопасности WordPress, которые были очень дружелюбны и действовали профессионально, работая с нами над этой проблемой.