• XSS.stack #1 – первый литературный журнал от юзеров форума

Статья CVE-2022-0435: удаленное переполнение стека в ядре Linux

timeshout

RAID-массив
Пользователь
Регистрация
29.06.2022
Сообщения
62
Реакции
83
Введение:
В ноябре 2021 года SentinelLabs публично сообщила об удаленном переполнении кучи, которое они обнаружили в сетевом модуле ядра Linux для протокола Transparent Inter-Process Communication (TIPC) (CVE-2021-43267).

TL;DR on TIPC
Прозрачная межпроцессная связь (TIPC) — это механизм IPC, предназначенный для связи внутри кластера. Топология кластера управляется вокруг концепции узлов и связей между этими узлами.

Возьмем пример из Руководства по началу работы с TIPC:
Код:
tipc bearer enable media eth dev eth0

eth0 Здесь мы настраиваем узел для использования однонаправленного канала с типом носителя Ethernet на интерфейсе.
Теперь TIPC знает, что он может использовать eth0 для связи через Ethernet.

Время добраться до самой уязвимости.


CVE-2022-0435
Одной из многих функций модуля TIPC является его структура мониторинга. Представленный в ядре в июне 2016 года, фреймворк использует распределенный «алгоритм контроля перекрывающихся колец» для мониторинга соседних узлов в одном домене.

Статус пира отслеживается метко названной structtipc_peer, определяемой следующим образом:

Код:
 /* struct tipc_peer: state of a peer node and its domain
     * @addr: tipc node identity of peer
     * @head_map: shows which other nodes currently consider peer 'up'
     * @domain: most recent domain record from peer
     * @hash: position in hashed lookup list
     * @list: position in linked list, in circular ascending order by 'addr'
     * @applied: number of reported domain members applied on this monitor list
     * @is_up: peer is up as seen from this node
     * @is_head: peer is assigned domain head as seen from this node
     * @is_local: peer is in local domain and should be continuously monitored
     * @down_cnt: - numbers of other peers which have reported this on lost
     */
    struct tipc_peer {
        u32 addr;
        struct tipc_mon_domain *domain;
        struct hlist_node hash;
        struct list_head list;
        u8 applied;
        u8 down_cnt;
        bool is_up;
        bool is_head;
        bool is_local;
    };


Как мы видим, среди прочего, мы сохраняем ссылку на структуру tipc_mon_domain. Эта структура представляет записи домена, используемые для определения представления топологии TIPC, такого как известное количество членов. См. определение ниже:

Код:
    #define MAX_MON_DOMAIN       64
    ...

    /* struct tipc_mon_domain: domain record to be transferred between peers
     * @len: actual size of domain record
     * @gen: current generation of sender's domain
     * @ack_gen: most recent generation of self's domain acked by peer
     * @member_cnt: number of domain member nodes described in this record
     * @up_map: bit map indicating which of the members the sender considers up
     * @members: identity of the domain members
     */
    struct tipc_mon_domain {
        u16 len;
        u16 gen;
        u16 ack_gen;
        u16 member_cnt;
        u64 up_map;
        u32 members[MAX_MON_DOMAIN];
    };


Копии этих записей домена передаются между одноранговыми узлами, чтобы сообщить друг другу их соответствующие топологические представления. Затем каждый узел сохраняет копию через tipc_peer->domain field.


В TIPC сообщения между узлами классифицируются по полям заголовка в общий «пользователь сообщения» (то есть, какая часть стека TIPC использует это сообщение), а затем далее в «типы сообщений».


Эти записи домена используют пользователя сообщения LINK_PROTOCOLTIPC и типы сообщений STATE_MSG для связи между ссылками.


При получении STATE_MSG, если оно проходит проверку заголовка и некоторые другие проверки, тело сообщения передается функции tipc_mon_rcv.


Звучит просто, правда? Давайте углубимся в код, чтобы увидеть, где проблема:

Код:
/* tipc_mon_rcv - process monitor domain event message
 *
 * @data: STATE_MSG body
 * @dlen: STATE_MSG body size (taken from TIPC header)
 */
void tipc_mon_rcv(struct net *net, void *data, u16 dlen, u32 addr,
    struct tipc_mon_state *state, int bearer_id)
{
    ...
    struct tipc_mon_domain *arrv_dom = data;
    struct tipc_mon_domain dom_bef;                                   [0]
    ...

    /* Sanity check received domain record */                         [1]
    if (dlen < dom_rec_len(arrv_dom, 0))                              [2]
        return;
    if (dlen != dom_rec_len(arrv_dom, new_member_cnt))                [3]
        return;
    if (dlen < new_dlen || arrv_dlen != new_dlen)                     [4]
        return;
    ...

    /* Drop duplicate unless we are waiting for a probe response */
    if (!more(new_gen, state->peer_gen) && !probing)                  [5]
        return;
    ...

    /* Cache current domain record for later use */
    dom_bef.member_cnt = 0;
    dom = peer->domain;
    if (dom)                                                          [6]
        memcpy(&dom_bef, dom, dom->len);                              [7]

    /* Transform and store received domain record */
    if (!dom || (dom->len < new_dlen)) {
        kfree(dom);
        dom = kmalloc(new_dlen, GFP_ATOMIC);                          [8]
        peer->domain = dom;                                           [9]
        if (!dom)
            goto exit;
    }
    ...

Как злоумышленник, мы можем ответить на один из широковещательных пакетов и установить связь, притворившись пиром. Затем мы сможем отправить созданную запись домена размером 1072 байта (с 264 участниками) и пройти проверку.

Нам нечего кэшировать [6], так как это была первая запись домена, которую мы отправили с вредоносного узла, но теперь узел без проблем выделил место для нашей 1072-байтовой записи [8], наша пара структур и т. д. теперь ссылается на нее. [9].

Код:
    dom = {
        len = 1072,
        gen = 3,
        ack_gen = 3,
        member_cnt = 264;
        up_map = 0xffffffffffffffff;
        u32 members[264] = 0x1337...
    };

Что такое &dom_bef? Это локальная структура [0], и поскольку она ожидает, что members будет массивом из 64 элементов, она размещается в стеке как 272-байтовый буфер. Мы собираемся скопировать в него 1072 байта.

Единственными ограничениями для нашей структуры полезной нагрузки tipc_mon_domain являются: len = dlen = sizeof(len,gen,ack_gen,member_cnt,up_map) + member_cnt * sizeof(u32) -gen должен быть больше, чем последняя принятая запись - должна подходить для MTU носителя носителя.

Поскольку members находится в конце структуры, теперь мы можем свободно перезаписывать что угодно в стеке с произвольными полезными нагрузками, со значительными требованиями к размеру. Мы обсудим возможность использования этого в следующей статье.


Резюмируя все, что мы рассмотрели: CVE-2022-0435 позволяет локальному или удаленному злоумышленнику вызвать переполнение стека в сетевой подсистеме TIPC — размер переполнения ограничен MTU включенной несущей среды (Ethernet/Infiniband/UDP). .
Ограничения на фактическое содержание полезной нагрузки очень малы и в основном произвольны.

Скопировав более 272 байт в буфер стека dom_bef, мы смогли перезаписать все, что находится после него в стеке, включая стековую канарейку, а также базовый указатель и адрес возврата, что могло привести к перехвату потока управления.


CVE-2022-0435: A Remote Stack Overflow in The Linux Kernel
 


Напишите ответ...
  • Вставить:
Прикрепить файлы
Верх