Использование бага Webroot Type Confusion
В этой статье подробно говорится о том, что такое CVE-2020–5754 или баг Webroot Type Confusion.
Когда этот баг был первый раз обнаружен хакерами, его считали простой уязвимостью для DoS, которая была способна удаленно отключить антивирус. Однако через какое-то время был создан рабочий PoC, который наделил этот недостаток огромным потенциалом: способностью удаленно считывать память процесса. Использование данной уязвимости может способствовать удаленной краже лицензионных ключей Webroot, токенов HTTP или даже чтению файлов, которые недавно были проверены самой службой Webroot. Это, в свою очередь, возможно, приведет к получению доступа к секретной информации.
Уязвимость
Служба Webroot имеет библиотеку dll («WrUrl.dll»), которая связана с TCP-портом 27019 (для всех интерфейсов) и принимает входящие POST-запросы, содержащие данные JSON. В этом протоколе связи есть поле «OP» (предполагается, что оно обозначает операцию), позволяющее запускать различные функции службы. Есть одна важная вещь, которую служба должна выполнить при разборе предоставленных клиентом данных JSON, — проверить типы объектов, так как без этого возникнет путаница типов (т.е. int будут восприняты как строки). Как видно на картинке ниже, Webroot верно определяет ожидаемый тип, вернувшийся после манипулирования данными JSON, и отклоняет определенные типы, если они не совпадают с ожидаемыми.

Однако одна часть этой службы не проверила тип объекта, и это обнаруживается в ветке OPERATION 1, которая повторяет список данных JSON, предоставленных пользователем. Вот пример ожидаемого запроса POST, который будет обработан с помощью этой процедуры.
curl -X POST — header “Content-Type:application/urltree; charset=utf-8” -d “{\”VER\”:1, \”OP\”:1, \”DATA\”:[{\”URL\”:\”http://test\”}, {\”URL\”:\”http://test\”}], \”BRWSR\”:\”Chrome\”}”
В ветке OP 1 служба, перечисляющая список «данных», предполагает, что каждый элемент имеет тип «объект JSON», а затем пытается ввести ключ в объект, вызывая функцию JSON_Key_Value с нужным «ключом» для извлечения его значения. Ниже можно увидеть, что этот элемент передается из списка данных в JSON_Key_Value вместе со строкой URL для того, чтобы ввести значение URL из объекта JSON.

Итак, что же произойдет, если элемент списка данных не является объектом JSON? К примеру:
… \”DATA\”:[\”NotJSON\”]…
Нехорошие действия
Ниже показано, как выглядит список данных, который содержит объект JSON в памяти Webroot.

Однако, поскольку пользователь собирается передать список данных, не содержащий ожидаемого объекта JSON, процедура ошибочно передаст запись списка данных, как если бы это был объект JSON, в который нужно ввести ключ. Например, происходит отправка данных…
curl -X POST — header “Content-Type:application/urltree; charset=utf-8” -d “{\”VER\”:1, \”OP\”:1, \”DATA\”:[\”NotJSON\”], \”BRWSR\”:\”Chrome\”}”
….которая закончится разыменованием строки «NotJSON», как если бы это был объект JSON.
Данный процесс, конечно, может быть мгновенно сломан внутри «JSON_Key_Value».

Использование уязвимости
Сперва кажется, что очень трудно использовать это в свою пользу. Пользователю нужно будет не только получить доступ к действительному адресу памяти через допустимый адрес памяти, закодированный как «строка» (чтобы он не вызывал сбоев), но и сам этот адрес памяти должен указывать на строку URL. Схема работает именно так, ведь JSON_Key_Value пытается войти в этот объект со строкой URL, и данный процесс проверяет, что переданный объект JSON содержит указатель на строку “URL” (ключ), прежде чем вернуть соответствующее «значение». К счастью, существует прекрасный способ получить не только действительный указатель, но и тот, который указывает на строку URL — строку” URL» внутри вложенного списка для входа.
curl -X POST — header “Content-Type:application/urltree; charset=utf-8” -d “{\”VER\”:1, \”OP\”:1, \”DATA\”:[[\”URL\”]], \”BRWSR\”:\”Chrome\”}”
Теперь JSON_Key_Value получит что-то похожее на переданные ему уже данные.

Это, по своей сути, указатель на строку URL для проверки Key/Value, которую JSON_Key_Value будет пытаться выполнить, когда станет искать ключ URL. Для того чтобы соответствующее значение вернулось, JSON_Key_Value возвращает 0x10 байт за пределами этого ключа (String Obj), потому что именно там обычно находится указатель на элемент Value. Поскольку это не объект Key/Value, что же будет храниться в этой ячейке памяти 0x10 байт за ее пределами?
0x10 байт за пределами строки Obj станет содержимым объекта, если добавить еще одну запись в список данных. Это размещение памяти строится как объекты JSON, и анализ показывает, что оно детерминировано. Таким образом, у пользователя появляется возможность предоставить второй объект списка в списке данных с указателем, который кодируется как строка, и обмануть Webroot, чтобы разыменовать его как адрес памяти, когда он попытается получить значение URL.
curl -X POST — header “Content-Type:application/urltree; charset=utf-8” -d “{\”VER\”:1, \”OP\”:1, \”DATA\”:[[\”URL\”], [\”<encodedPointer>\”]], \”BRWSR\”:\”Chrome\”}”
Стоит взглянуть на этот запрос POST в отладчике. Ниже, в регистре ecx, показан список [“URL”], передаваемый в JSON_Key_Value, который является просто указателем на строку URL, длину строки (3) и код типа объекта (0x300005). Однако за пределами 0x10 байт находятся данные следующей записи списка.

Это смещение 0x10 байт будет возвращено, когда будет найдет ключ URL, и позже разыменовано службой Webroot. Его содержимое возвращается в ответе службы Webroot, так как она думает, что это соответствующее значение для ключа URL. Служба считает, что она успешно отключила объект JSON.
Это все-таки не так просто: когда Webroot попытается разыменовать строку, он выполнит проверку типа, чтобы убедиться, что нужный объект, который он разыменовывает, имеет тип String OBJ, поэтому следует заполнить закодированный указатель данными. Это делается для того, чтобы этот «объект» выглядел как указатель на String OBJ. Кодируя 32-битный указатель в виде строки и заполняя его «1», пользователь ждет момента, пока он не получит смещение 0xf, которое «стаптывает» информацию о типе и успешно подделывает тип строки, когда он проверяется Webroot.
curl -X POST — header “Content-Type:application/urltree; charset=utf-8” -d “{\”VER\”:1, \”OP\”:1, \”DATA\”:[[\”URL\”], [\”\\u0041\\u0041\\u0041\\u004111111111111111\”]], \”BRWSR\”:\”Chrome\”}”
Отправка вышеизложенного приведет к тому, что служба Webroot вернет обратно содержимое памяти в расположение закодированного указателя (0x41414141) в ответе URL.
Однако сперва стоит попробовать прочитать реальный адрес памяти. Например, это можно сделать, используя содержимое загруженной библиотеки DLL по адресу памяти 0x73640000.
Выполнение клиентских команд:
curl -X POST — header “Content-Type:application/urltree; charset=utf-8” -d “{\”VER\”:1, \”OP\”:1, \”DATA\”:[[\”URL\”], [\”\\u004c\\u0000\\u0064\\u007311111111111111111\”]], \”BRWSR\”:\”Chrome\”}” 10.0.2.5:27019
Ответ сервера:
{“VER”:1,”OP”:1,”ERR”:0,”DATA”:[{“URL”:”!This program cannot be run in DOS mode.\r\r\n$”,”CAT.CONF”:[“0.0”],”BCRI”:40,”ALCAT”:0,”RTAP”:0,”BLK”:0,”REF”:0}]}
Заключение
Одна из сложностей выполнения данного процесса заключается в том, что служба Webroot UTF-8 декодирует символы выше 0x7f, что делает текущий PoC неспособным отправлять адреса памяти с байтами больше 0x7f. Если злоумышленник попытается использовать это и получит неправильный адрес, служба снова запустится, чтобы дать преступнику еще одну попытку, что было бы вполне резонно. Однако Webroot уже исправил этот баг в своей последней версии.
Автор переведенной статьи: David Wells.
Важно! Информация исключительно в учебных целях. Пожалуйста, соблюдайте законодательство и не применяйте данную информацию в незаконных целях.


