Подтвердить что ты не робот

Объяснение указателя в коде эксплойта

В некоторых эксплоитах для получения корневой оболочки я часто вижу такой указатель:

int i;
unsigned *p = *(unsigned**)(((unsigned long)&i) & ~8191); 

Может кто-нибудь объяснить этот указатель немного? Я думаю, что 8191 - это размер стека ядра. p указывает на нижнюю часть стека ядра ? Вот как используется указатель p:

int i; 
unsigned *p = *(unsigned**)(((unsigned long)&i) & ~8191); 
for (i = 0; i < 1024-13; i++) { 
    if (p[0] == uid && p[1] == uid && 
        p[2] == uid && p[3] == uid && 
        p[4] == gid && p[5] == gid && 
        p[6] == gid && p[7] == gid) { 
            p[0] = p[1] = p[2] = p[3] = 0; 
            p[4] = p[5] = p[6] = p[7] = 0; 
            p = (unsigned *) ((char *)(p + 8) + sizeof(void *)); 
            p[0] = p[1] = p[2] = ~0; 
            break; 
        } 
    p++; 
} 
4b9b3361

Ответ 1

Код берет адрес локальной переменной i, чтобы получить указатель на текущий стек стека. Затем он выравнивает адрес на странице 8K (это то, что вы делаете с x & ~8191: 8191 равно 2 ^ 13 - 1, что означает, что ~8191 - все, кроме низких 13 бит, поэтому ANDing с номером очистит низкие 13 бит, то есть выровняйте число до ближайшего более низкого кратного 2 ^ 13, другими словами, выровняйте границу 8K).

Затем он берет этот адрес и интерпретирует его как указатель на указатель и загружает с него выделенный адрес. См. Понимание получения указателя task_struct из стека ядра процесса для получения дополнительной информации.

После этого он пытается найти определенную структуру, хранящуюся где-то после этого адреса: он просматривает следующие 1024-13 unsigned s, пытаясь найти место в памяти, где хранится текущая информация о процессе (возможно): Когда он находит часть памяти, содержащую несколько копий текущего UID и GID, он предполагает, что он ее нашел. В этом случае он изменяет его так, что текущий процесс получает UID и GID 0, заставляя процесс работать под root (плюс он сохраняет all-ones в следующие флаги возможностей).

Cf. struct cred.

Ответ 2

Я собираюсь опубликовать еще один ответ, потому что здесь есть что добавить.

unsigned *p = *(unsigned**)(((unsigned long)&i) & ~8191); 

приводит к тому, что p является указателем на начало блока памяти размером 8192 байт. Однако код неправильный. Если p превышает INT_MAX (какой он может быть или он будет отличен без знака, а не без знака long), высокие бит обрезаются маской. Правильный код выглядит следующим образом:

unsigned *p = *(unsigned**)(((ptrdiff_t)&i) & ~(ptrdiff_t)8191);

или используя uintptr_t:

unsigned *p = *(unsigned**)(((uintptr_t)&i) & ~(uintptr_t)8191U);

Необходимо направить целое число и вернуться к указателю для работы кода; однако, чтобы гарантировать, что указатель int-size требует использования ptrdiff_t (напомним, что подписанные и unsigned ведут себя одинаково для побитовых операций). Что касается того, почему они не записывают их с шестнадцатеричными константами, кого это волнует. Ребята, которые этим занимаются, знают свои способности 2 наизусть. Быстрее читать 8191, затем 0x1FFF.