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

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

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

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

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

Категории

Все категории
Loading categories
CVE-2025-40634 — Эксплойт для стекового переполнения буфера, обнаруженного в бинарном файле conn-indicator в роутере TP-Link Archer AX50 | Kitploit
Инструменты/GitHubGitHub/hacefresko/cve-2025-40634
Безопасность встроенных системБезопасность IoTЭксплуатацияФаззингТестирование на ПроникновениеАппаратная БезопасностьИнструмент Удаленного ДоступаЭксплуатация Бинарных Файлов
GitHubhacefresko/cve-2025-40634

CVE-2025-40634

Эксплойт для стекового переполнения буфера, обнаруженного в бинарном файле conn-indicator в роутере TP-Link Archer AX50

3181211 месяцев назадПроверено Kitploit

Популярное

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

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

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

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

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

CVE-2025-40634

Маршрутизатор TP-Link Archer AX50 уязвим к переполнению стека в прошивке версии 1.0.14 Build 20240108 rel.42655(4555), что приводит к удаленному выполнению кода как в локальной сети (LAN), так и в глобальной сети (WAN). Эта уязвимость имеет ту же первопричину, что и CVE-2020-10881, обнаруженная командой Flashback и подробно описанная в их серии видео (см. ссылки). Однако процесс эксплуатации несколько отличается, поэтому потребовалось написать новый эксплойт.

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

Уязвимость возникает в бинарном файле conn-indicator, который отвечает за проверку подключения маршрутизатора к интернету путем периодической отправки DNS-запросов и прослушивания ответов на случайном UDP-порту от 32000 до 61000.

Функция, которая принимает и обрабатывает эти DNS-ответы, называется TPDns_RecvAndResolve(), расположена по адресу 0x00405e3c. При получении пакета через recvfrom он сохраняется в buf, размер которого составляет 2960 байт. Затем проверяется, что код возврата корректен (RCODE == 0), а также проверяется количество вопросов и ответов в пакете (QDCOUNT и ANCOUNT). Для обработки ответов вызывается process_resolved_IP(), которой передаются указатель на buf, answer_ptr (указатель на местонахождение ответов внутри пакета в buf), количество ответов (ANCOUNT) и другие флаги:

undefined4 * TPDns_RecvAndResolve(int socket,void *param_2,int param_3){
  
  [...]
  
  byte buf [2960];
  
  [...]
  
      while( true ) {
        recv_bytes = recvfrom(socket,buf + total_recv_bytes,0xb90 - total_recv_bytes,0,&sStack_50,
                              local_38);
        piVar2 = __errno_location();
        if (recv_bytes == 0) goto RECV_ERROR;
        if (recv_bytes < 0) break;
        total_recv_bytes = total_recv_bytes + recv_bytes;
        if (2959 < total_recv_bytes) goto PROCESS_DNS_RESP;
      }

  [...]

                    /* Check that ANCOUNT is not 0 (answer contains at least one domain) */
          puVar5 = (undefined4 *)0x0;
          if (buf._6_2_ != 0) {
            local_40 = 0;
            puVar5 = process_resolved_IP(buf,answer_ptr,(uint)buf._6_2_,&local_3c,&local_40);
            answer_ptr = answer_ptr + local_40;
          }

  [...]

Функция process_resolved_IP(), расположенная по адресу 0x00405818, перебирает каждый ответ и вызывает DNS_answer_parser() для каждого из них. В качестве аргументов передаются тот же указатель на buf, answer (указатель на текущий обрабатываемый ответ внутри исходного пакета в buf) и указатель на current_answer (буфер размером 256):

undefined4 * process_resolved_IP(byte *buf,byte *answer_ptr,uint ANCOUNT,undefined4 *param_4,int *param_5){
  
  [...]
  
  byte current_answer [256];
  ushort answer_flags [5];
  
  i = 0;
  puVar9 = (undefined4 *)0x0;
  puVar7 = (undefined4 *)0x0;
  answer = answer_ptr;
  do {
                    /* Check if all answers have been parsed already */
    if (i == ANCOUNT) {
      if (param_4 != (undefined4 *)0x0) {
        *param_4 = puVar7;
      }
      if (param_5 != (int *)0x0) {
        *param_5 = (int)answer - (int)answer_ptr;
      }
      return puVar9;
    }
    bytes_processed = DNS_answer_parser(buf,answer,current_answer,1);
    memcpy(answer_flags,answer + bytes_processed,10);
    uVar1 = answer_flags._4_4_;
    bytes_processed = bytes_processed + 10;
    uVar6 = (uint)answer_flags[4];
    uVar8 = (uint)answer_flags[0];
    if (uVar8 == 2) {
LAB_00405924:
      DNS_answer_parser(buf,answer + bytes_processed,abStack_240,1);
    }
    else if (uVar8 < 3) {
      pbVar2 = answer + bytes_processed;
      if (uVar8 == 1) {
        sprintf((char *)abStack_240,"%u.%u.%u.%u",(uint)*pbVar2,(uint)pbVar2[1],(uint)pbVar2[2],
                (uint)pbVar2[3]);
      }
    }
    else {
      if (uVar8 == 5) goto LAB_00405924;
      if (uVar8 == 0x1c) {
        inet_ntop(10,answer,(char *)abStack_240,0xff);
      }
    }
    answer = answer + bytes_processed + uVar6;
  
  [...]

    i = i + 1;
  } while( true );
}

Функция DNS_answer_parser(), расположенная по адресу 0x004054e0, анализирует каждый ответ отдельно. Ответ состоит из доменного имени, представленного в формате <длина><домен><длина><домен>.... Например, example.com будет представлен как 7example3com. Функция проходит по каждой паре <длина><домен>, составляющей доменное имя в ответе, и проверяет, что <длина меньше 63 (domain_name & 0xc0 != 0). Затем она вызывает memcpy и копирует <длина> байт из answer, соответствующих <домен>, в current_answer. Процесс повторяется для следующей пары <длина><домен> в доменном имени.

int DNS_answer_parser(byte *buf,byte *answer,byte *current_answer,int flag){
  int iVar1;
  uint __n;
  int iVar2;
  uint uVar3;
  ushort flag_and_offset;
  byte domain_name;
  
  iVar2 = 0;
  do {
    domain_name = *answer;
    __n = (uint)domain_name;
    iVar1 = 1;
    if (__n == 0) {
      *current_answer = 0;
LAB_004055b0:
      return iVar2 + iVar1;
    }
                    /* Check if compression mode is used */
    if ((domain_name & 0xc0) != 0) {
      flag_and_offset = CONCAT11(domain_name,answer[1]);
      DNS_answer_parser(buf,buf + (flag_and_offset & 0x3fff),current_answer,flag);
      iVar1 = 2;
      goto LAB_004055b0;
    }
    uVar3 = __n + 1;
    if (flag == 0) {
      *current_answer = '.';
      memcpy(current_answer + 1,answer + 1,__n);
      __n = uVar3;
    }
    else {
      memcpy(current_answer,answer + 1,__n);
    }
    answer = answer + uVar3;
    current_answer = current_answer + __n;
    iVar2 = iVar2 + uVar3;
    flag = 0;
  } while( true );
}

Поскольку current_answer имеет размер всего 256 байт, злоумышленник может отправить пакет с ответом, содержащим доменное имя достаточной длины, чтобы переполнить буфер.

Эксплуатация

Процесс эксплуатации очень похож на тот, что описан командой Flashback для CVE-2020-10881 в их серии видео (см. ссылки), с некоторыми ключевыми отличиями:

  1. Адреса не совпадают.
  2. Код не совсем одинаков, поэтому смещения и размер стека в некоторых моментах также отличаются.
  3. Хотя первый и третий ROP-гаджеты те же (расположены по разным адресам), второй отличается инструкцией move: теперь регистр, перемещаемый в $a2 в качестве аргумента count для memcpy(), — это $s1, а не $s0.
  4. Переменная i в process_resolved_IP() загружается в $s5, а не в $s8. Затем при сравнении с $v1 значение $v1 берется из $sp+616, что находится очень далеко от переполненного буфера, поэтому пришлось дополнительно повредить стек дальше за счет ответа и команды.
  5. Файловая система доступна только для чтения, за исключением /tmp, поэтому все операции записи и чтения должны выполняться там. Это делает невозможным reverse shell длиной менее 62 символов, поэтому для развертывания его на цели необходимо раздать его с веб-сервера и отправить на цель команду на загрузку и выполнение.

Учитывая это, я всё равно объясню весь процесс.

Бинарник conn-indicator имеет включенный NX и ASLR на всем, кроме себя самого:

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