2016-02-26 10 views
4

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

#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) (что даст значение вместо того, чтобы что-то удобное, как 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.

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

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

+3

Суть дела заключается в том, что scanf с радостью примет нулевой байт как часть строки и не будет рассматривать его как пробел (таким образом, не перестанет считывать дальнейшие байты в строку). С этой информацией ваша задача должна быть простой. – Ctx

+1

'char password [16]; ... scanf («% 29s», пароль); 'не использует уязвимость. Это намеренно создает одно. –

+1

@Ctx: это правда, но ввод 'NUL' байт из терминала нелегкий. – chqrlie

ответ

3

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

Суть дела в том, что 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 байта в любом случае не были скопированы, но я их оставил, чтобы показать строку и хэш.