
Этот репозиторий содержит пост в блоге о моём анализе CVE-2018-19987 — аутентифицированной инъекции команд ОС, затрагивающей несколько маршрутизаторов D-Link.
Всем привет!
В этой короткой записи в блоге я разберу быстрый анализ, который я провел, чтобы лучше понять детали CVE-2018-19987. Сначала я занялся этой CVE, потому что думал, что нет никакой публичной информации или эксплойта. Вскоре после первого анализа я нашел два публичных PoC на GitHub, которые можно найти здесь и здесь.
Так как я уже сделал большую часть работы и не нашел полного анализа, я решил всё же написать пост в блоге.
Надеюсь, вам понравится!
Обновление: Ну... после гугления, когда я застрял с этим постом, я нашел следующую статью, описывающую очень похожую уязвимость (CVE-2018-19986) в D-Link DIR-818. Анализ уязвимостей маршрутизаторов (серия 5): CVE-2018-19986 DIR-818LW&828 анализ и воспроизведение уязвимости внедрения команд (перевод Google) от Ogur1.
Я начал свой анализ со сбора всей возможной информации об устройстве:
Примечание: Весь анализ я проводил с прошивкой v3.01B02.
Публичная информация об этой уязвимости, доступная на странице Mitre, предоставила достаточно информации для начала:
Устройства D-Link DIR-822 Rev.B 202KRb06, DIR-822 Rev.C 3.10B06, DIR-860L Rev.B 2.03.B03, DIR-868L Rev.B 2.05B02, DIR-880L Rev.A 1.20B01_01_i3se_BETA и DIR-890L Rev.A 1.21B02_BETA неправильно обрабатывают IsAccessPoint в /HNAP1/SetAccessPointMode. В исходном коде SetAccessPointMode.php параметр IsAccessPoint сохраняется в файле скрипта ShellPath без какой-либо проверки регулярных выражений. После выполнения файла скрипта происходит внедрение команд. Уязвимое XML-сообщение /HNAP1/SetAccessPointMode может содержать метасимволы оболочки в элементе IsAccessPoint, такие как строка `telnetd`.
Для начала анализа мне нужно было что-то изучить, поэтому я приступил к извлечению файловой системы маршрутизатора.
binwalk -eM DIR822C1_FW301WWb02.bin
DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
0 0x0 DLOB firmware header, boot partition: "dev=/dev/mtdblock/1"
10380 0x288C LZMA compressed data, properties: 0x5D, dictionary size: 8388608 bytes, uncompressed size: 4213444 bytes
1376372 0x150074 PackImg section delimiter tag, little endian size: 10505216 bytes; big endian size: 5021696 bytes
1376404 0x150094 Squashfs filesystem, little endian, version 4.0, compression:lzma, size: 5019773 bytes, 2282 inodes, blocksize: 131072 bytes, created: 2016-03-18 09:35:31
Как видим, Binwalk обнаруживает файловую систему Linux. Если присмотреться к имеющейся информации, то можно заметить, что в бюллетене упоминается путь /HNAP1. Итак, мы можем начать с изучения того, как маршрутизатор обрабатывает эти URL-адреса.
Как объясняется в отличном посте в блоге /dev/tty0, эти URL-адреса в конечном итоге обрабатываются бинарным файлом cgibin, расположенным в htdocs/cgibin. Но я хотел лучше понять, как это настроено, и в итоге мне пришлось разобраться, как настроен HTTP-сервер для обработки запросов. Я начал искать имя файла httpd.conf, предполагая, что он используется для настройки HTTP-сервера, и нашел файл с именем HTTP.php в папке /etc/services/, содержащий, среди прочего, следующие интересные строки:
$httpd_conf = "/var/run/httpd.conf";
fwrite("a",$START, "xmldbc -P /etc/services/HTTP/httpcfg.php > ".$httpd_conf."\n");
fwrite("a",$START, "event PREFWUPDATE add /etc/scripts/prefwupdate.sh\n");
fwrite("a",$START, "httpd -f ".$httpd_conf."\n");
fwrite("a",$START, "event HTTP.UP\n");
fwrite("a",$START, "exit 0\n");
Я предположил, что в какой-то момент этот файл выполняется и записывает файл /var/run/httpd.conf. Чтобы узнать, как настроен веб-сервер, я проанализировал файл /etc/services/HTTP/httpcfg.php (я включил только соответствующие части):
if ($hnap > 0)
{
echo
" Control". "\n".
" {". "\n".
" Alias /HNAP1". "\n".
" Location /htdocs/HNAP1". "\n".
" External". "\n".
" {". "\n".
" /usr/sbin/hnap { hnap }". "\n".
" }". "\n".
" IndexNames { index.hnap }". "\n".
" }". "\n";
}
Теперь у нас есть все фрагменты! Можно предположить, что веб-сервер настроен на обработку HTTP-запросов к /HNAP1 с помощью /usr/sbin/hnap, и, основываясь на посте /dev/tty0, мы знаем, что это будет ссылка на бинарный файл htdocs/cgibin.
Следующим шагом было изучение бинарного файла cgibin, чтобы понять, как он обрабатывает запросы HNAP. На следующем изображении мы видим часть декомпилированной функции main, где переданный путь URL сравнивается с различными путями (такими как session.cgi, authentication.cgi, captcha.cgi и другими), пока не будет найден hnap и не будет вызвана наша функция hnap_main.
примечание: При анализе функции main у меня возникли некоторые проблемы с идентификацией строки hnap, так как Ghidra не определил её как строку во время начального анализа.
Следующим шагом было понять, что делает эта функция. Я предоставлю только фрагменты кода для соответствующих частей, которые помогут лучше понять, что делает функция, а также дополню другие соответствующие части псевдокодом — я переименовал некоторые переменные для ясности их назначения.
...
HTTP_SOAPACTION = getenv("HTTP_SOAPACTION");
REQUEST_METHOD = getenv("REQUEST_METHOD");
HNAP_AUTH = getenv("HTTP_HNAP_AUTH");
__haystack = getenv("HTTP_COOKIE");
pcVar1 = getenv("HTTP_REFERER");
...
if (HTTP_SOAPACTION != "") {
if (HTTP_SOAPACTION == GetDeviceSettings) {
...
} else {
// Эти действия будут происходить во время аутентификации. Процесс
if ("GetCAPTCHAsetting" in HTTP_SOAPACTION) {
sess_generate_captcha();
} else {
if ("Login" in HTTP_SOAPACTION) {
perform_login();
}
// Мы попадем сюда после аутентификации.
if (HNAP_AUTH != "") {
if ("uid=" in HTTP_COOKIE){
is_valid_auth = perform_auth_process()
if (is_valid_auth) {
if("logout" in HTTP_SOAPACTION){
perform_logout();
// Если мы аутентифицированы и НЕ пытаемся выйти
// код попытается выполнить запрошенное действие
} else {
// Если мы попали сюда, мы победили
// интересный код ниже
goto LAB_004141d4;
}