Переполнение буфера в стеке - вызов в C с использованием scanf с ограниченным вводом

В рамках курса по безопасности CS мой класс получил задание использовать уязвимость, чтобы пройти проверку пароля, используя переполнение стека / буфера. Код с уязвимостью выглядит следующим образом:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <openssl/md5.h>

int main(int argc, char **argv) {
    char correct_hash[16] = {
        0xd0, 0xf9, 0x19, 0x94, 0x4a, 0xf3, 0x10, 0x92,
        0x32, 0x98, 0x11, 0x8c, 0x33, 0x27, 0x91, 0xeb
    };
    char password[16];

    printf("Insert your password: ");
    scanf("%29s", password);

    MD5(password, strlen(password), password);

    if (memcmp(password, correct_hash, 16) == 0) {
        printf("Correct Password!\n");
    } else {
        printf("Wrong Password, sorry!\n");
    }
    return 0;
}

Я понимаю классический принцип "разбивания стека" (я думаю), и здесь есть явная уязвимость переполнения, где первые 14 байтов correct_hash массив можно перезаписать, введя пароль длиной более 15 символов при запросе. Тем не менее, я не понимаю, как использовать это, чтобы позволить memcmp проверьте, чтобы пройти, завершив вызов. Некоторые из вещей, которые я обнаружил / попытался:

  • настройка password быть эквивалентом correct_hash не работает, как password хешируется с помощью MD5() (установить два равных невозможно в любом случае, так как scanf вставит точно один уникальный ASCII NUL символ в 30 доступных ему пробелах, что означает, что эти два массива никогда не могут быть эквивалентными. NUL символы дополнительно (насколько мне известно) не могут быть вставлены в середине scanf строка).

  • Перезаписать максимальное количество байтов с scanf (который всегда будет добавлять NUL символ) означает последние 3 байта correct_hash всегда будет 0x00 0x91 0xeb, Попытка случайным образом сгенерировать 16-символьный пароль, который затем хэшируется к чему-либо с этими последними 3 байтами / символами (достаточно простыми в вычислительном отношении, учитывая использование MD5), однако, не работает из-за использования strlen(password) (что даст значение 29 вместо чего-то удобного, например, 16, благодаря тому, что счетчик длины только заканчивается NUL характер) в призыве к MD5(), Это означает, что вместо хеширования 16-символьного пароля для получения ожидаемого вывода, вызов MD5() будет хэш 16 символов из password а затем 13 символов из correct_hash, производя другое окончательное хэшированное значение.

    • Чтобы обойти эту проблему, я считаю, что нужно было бы найти строку из 29 символов (назовем ее S), где первые 16 символов S-хэша строки R, состоящей из последних 13 символов S, сопровождаются 0x00 0x91 0xeb, Я не уверен, насколько жизнеспособным будет найти это с помощью случайных хеш-вычислений MD5, но мне не нужны мои шансы.

Некоторые примечания (упомянутые в пояснениях выше):

  • scanf ограничено 29-символьной строкой, но добавит ASCII NUL всего 30 символов (16 из password массив, 14 из correct_hash массив) перезаписать.

  • ASCII NUL символы не могут быть введены через scanf Итак strlen(password) в призыве к MD5() (если используется максимальная длина пароля) будет 29.

Итак, вопрос в том, как еще я могу это сделать? Я что-то упускаю чрезвычайно очевидное? Является ли случайное поколение жизнеспособным решением или даже единственным решением?

Решение должно использовать переполнение буфера (в противном случае я думаю, что я мог бы сделать что-то вроде предварительной загрузки memcmp всегда возвращает 0) и должен быть исполняемым как сценарий оболочки (если это имеет какое-либо значение).

1 ответ

Решение

Просто чтобы объединить комментарии в ответ здесь:

Суть в том, что scanf с радостью примет нулевой байт как часть строки и не будет рассматривать его как пробел (таким образом, не прекратит чтение дополнительных байтов в строку).

Перенаправить файл или использовать echo -ne "\x00" | program или т.п. Перенаправление входного файла, вероятно, является предпочтительным способом здесь.

Последняя команда, которую я использовал, была: echo -ne "\x49\x5a\x4e\x52\x48\x49\x41\x56\x5a\x43\x54\x52\x51\x4c\x4‌​3\x00\x81\xae\xf3\xd‌​f\xa2\x45\xb1\x57\x1‌​9\xb3\xa9\xb8\x7d\x0‌​0\x91\xeb" | ./vulnerable где первый набор из 16 байтов (до первого включительно \x00) были случайно сгенерированной строкой, которая при хешировании вызвала второй набор из 16 байтов с требуемым \x00\x91\xeb окончание. Последние 3 байта все равно не были скопированы, но я оставил их, чтобы показать строку и хэш.

Другие вопросы по тегам