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

Статья Heap exploitation, Overflows (часть 3)

timeshout

RAID-массив
Пользователь
Регистрация
29.06.2022
Сообщения
62
Реакции
83
Введение в эксплуатацию двоичных файлов x64 Linux (часть 1)

Введение в бинарную эксплуатацию x64 Linux (часть 2)


Давайте применим на практике то, что мы видели до сих пор, на простом примере. В приведенной ниже программе у нас есть два выделения размером 12 и 30 байт соответственно. Затем мы копируем значения LoremIpsum1 и LoremIpsum2 в переменные val1 и val2 и, наконец, освобождаем выделенную память с помощью функции free:

1657599819566.png






еперь скомпилируем и загрузим программу в gdb, установив одну точку останова после вызова malloc и еще одну после вызова функции free:


1657599852172.png

b *main+89, b *main+109

Before Free

Нажмите run в gdb и введите heap arenas в gef, чтобы отследить структуру malloc_state. Вспомните изображение, которое мы использовали во вступительном сообщении, помня, что у нас один поток:

1657599921465.png


Мы запросили 12 и 30 байт, а получили 0x21 и 0x31, почему? Из-за выравнивания! Помните, что размер выделения должен быть выровнен по 8-байтовой (или 16-байтовой на 64-битной) границе? Итак, для 12 байт у нас выделено 32, а для 30 байт - 32+16. '1' в добавленном размере относится к флагу PREV_INUSE, поэтому он не "учитывается" в фактическом размере. Давайте, например, рассмотрим чанк по адресу 0x5555555592a0:

1657599962494.png




1657600016278.png




Вы, вероятно, также заметили наши данные (LoremIpsum1 и LoremIpsum2) по адресам 0x555555555592a0 и 0x5555555592c0 . Наконец, у нас есть чанк размером 0x00000000000000000290 в начале кучи и еще один размером 0x0000000000020d20 (top chunk) в конце.

After Free
Чтобы получить лучшее представление о последствиях работы функции free, давайте изменим исходную программу, добавив еще несколько выделений, и рассмотрим, как ptmalloc обрабатывает их.

1657600090903.png


Скомпилируйте приведенную выше программу и загрузите ее в gdb, установив точку останова после вызовов free.

1657600113554.png


Каждое дополнительное выделение 0x10 или меньше байт будет интегрировано в Fastbins, так как оно превышает максимально допустимое (в нашем случае это 7) в tcache для этого размера:

1657600161289.png


Теперь куча выглядит так, как показано ниже:

1657600179059.png



Basic Heap Overflow

Посмотрите на приведенный ниже фрагмент кода. Обратите внимание, что программа допускает ввод данных размером, превышающим выделенное пространство (строки 9,10,15). Удобно для нас, что функция system примет в качестве параметра строку, на которую указывает переменная p1, и выполнит ее как системную команду. pwd - это жестко закодированная команда, которая будет выполнена, подразумевая, что функция system просто выведет текущий каталог:

Код:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

void main()
{
    char *p0,*p1;

    p0 = malloc(0x10);
    p1 = malloc(0x10);

    strcpy(p1,"pwd");

    printf("Enter your name:");
    scanf("%100s",p0);

    printf("Hello %s \n",p0);

    system(p1);

    free(p0);
    free(p1);
}



При этом, если мы скомпилируем и запустим программу, то получим следующий результат:

1657600257023.png


Давайте загрузим программу в gdb и установим точку останова после функции scanf. Введя правильный входной сигнал, получим следующий результат:

1657600277578.png

Продолжаем программу, все идет как положено:

1657600364223.png



Как показано ниже, нам нужно 32 байта, чтобы добраться до строки pwd и перезаписать ее:

1657600387685.png




Итак, давайте воспользуемся следующей строкой и посмотрим, что произойдет:

1657600411974.png


Посмотрите на этот беспорядок!!! Сначала обратите внимание на код программы, что буфер выделяется после буфера, который может быть переполнен. Между прочим, размер и флаги были изменены, но самое главное команда pwd теперь заменена на/bin/sh :

1657600442050.png




Запустив программу и введя вышеупомянутые данные, мы получим Shell:

1657600473320.png


From Integer Overflow to Code Execution

Теперь рассмотрим более интересный случай целочисленного переполнения:

C:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

typedef int(*fp) (const char *);

fp *toInt()
{
    fp *ti;
    ti = malloc(sizeof(*ti));
    *ti = atoi;
    return ti;
}

int main(int argc, char **argv)
{
    unsigned int *array, length, size, i;

    length = atoi(argv[1]);

    size = sizeof(unsigned int) * length;
    array = malloc(size);

    fp *nt = toInt();

    i = 0;
    while(i < length)
    {
        scanf("%ld",&array[i]);
        if(array[i] == 1)
            break;
        i++;
    }

    (*nt)(argv[2]);

    return 0;
}

Начиная со строки 5, мы определяем fp как указатель на функцию, которая принимает const char * в качестве параметра и возвращает целое число. Функция toInt в строках 7-13 выделяет место для переменной ti (строка 10) и присваивает ей адрес функции int atoi(const char *str) Си. Позже в main мы определяем переменную nt в строке 24, которая может быть использована для вызова atoi с помощью объявления(*nt)(const char *str).

Кроме того, функция main создает массив беззнаковых целых чисел длины argv[1] и заполняет этот массив в строках 27-33. Цикл while прерывается, когда пользователь вводит значение 1 или когда i == length. Наконец, в строке 35 вызывается atoi с параметром, принятым за аргумент argv[2].

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

Давайте загрузим программу в gdb, установив две точки останова в начале и конце главной функции. Теперь запустим программу и зададим несколько допустимых параметров (например, gef> r 5 3 ):


1657600596306.png



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

1657600618439.png



Обратите внимание на указатель массива по адресу 0x555555555592a0, а также на указатель функции по адресу 0x5555555592c0 . Рассмотрим содержимое кучи по этим адресам:

1657600642274.png




Для длины, равной 5, запрашиваемый размер составляет 5*4 байта, поэтому malloc выделил (минимально) 32 байта:

1657600664795.png



Аналогично, второй вызов malloc (см. функцию toInt()) привел к следующему распределению:

1657600694869.png


Размер unsigned int для данной системы составляет 4 байта, поэтому максимально допустимое значение будет 0xffffffffff == 4294967295 . Поскольку ввод пользователя умножается на размер беззнакового целого, см. строку 21, мы предполагаем, что размер, превышающий 4294967295/4, вызовет переполнение:

1657600728004.png


Действительно, обратите внимание, что во втором случае выделенный размер равен 0, но цикл wile в строке 27 позволит нам выйти за пределы выделенного пространства. Давайте проверим это с помощью gdb:

1657600750494.png


1657600758448.png





Обратите внимание, что второй вызов malloc буквально исчез из кучи, поскольку выделенное пространство было перезаписано значениями, которые мы вставили (2,3,4,5,...). В том числе и адрес функции atoi, который был заменен значением 0x00000001000000. Продолжая выполнение, мы получим ошибку SIGSEGV, так как наша программа будет использовать указатель функции, который хранится по адресу 0x00005555555592c0 // Строка 35: (*nt)(argv[2]) . Поскольку этот указатель был заменен на недопустимый адрес, результатом будет крах программы:

1657600797829.png




Exploitation

Можете ли вы придумать функцию, подобную atoi, которая принимает строку в качестве параметра и выполняет команду?

Предполагая, что ASLR отключен, мы должны заменить адрес atoi (0x7ffff7e115e0 в моем случае) на 0x7ffff7e1f2c0, который является адресом системной функции. Поскольку всего у нас 6 байт, а на одном входе мы можем перезаписать только 4 (из-за массива, определяемого unsigned int), мы сначала перезапишем часть f7e1f2c0 (десятичная запись: 4158780096), а затем часть 7ffff (десятичная запись: 32767). Запустим программу с gef➤ r 1073741824 /bin/sh и вставим следующие значения:

1657600865318.png



Адрес atoi был перезаписан адресом системной функции, а в качестве параметра был указан /bin/sh:

1657600885669.png


Таким образом, нажатие клавиши c в gef приведет к появлению оболочки:

1657600903185.png



Это все для этой части. Оставайтесь с нами для следующей части!




 

Вложения

  • 1657600300944.png
    1657600300944.png
    4.6 КБ · Просмотры: 8


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