Создание базового переполнения стека с использованием IDA

Эта программа работает с привилегиями root на моем компьютере, и мне нужно выполнить атаку переполнения стека на следующий код и получить привилегии root:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <openssl/sha.h>

void sha256(char *string, char outputBuffer[65])
{
    unsigned char hash[SHA256_DIGEST_LENGTH];
    int i = 0;
    SHA256_CTX sha256;
    SHA256_Init(&sha256);
    SHA256_Update(&sha256, string, strlen(string));
    SHA256_Final(hash, &sha256);

    for(i = 0; i < SHA256_DIGEST_LENGTH; i++)
    {
        sprintf(outputBuffer + (i * 2), "%02x", hash[i]);
    }
    outputBuffer[64] = 0;
}

int password_check(char *userpass)
{
    char text[20] = "thisisasalt";
    unsigned int password_match = 0;
    char output[65] = { 0, };
    // >>> hashlib.sha256("Hello, world!").hexdigest()
    char pass[] = "315f5bdb76d078c43b8ac0064e4a0164612b1fce77c869345bfc94c75894edd3"; 

    text[0] = 'a';
    text[1] = 't';
    text[2] = 'j';
    text[3] = '5';
    text[4] = '3';
    text[5] = 'k';
    text[6] = '$';
    text[7] = 'g';
    text[8] = 'f';
    text[9] = '[';
    text[10] = ']';
    text[11] = '\0';
    strcat(text, userpass);
    sha256(text, output);

    if (strcmp(output, pass) == 0)
    {
        password_match = 1;
    }

    return (password_match == 1);
}

int main(int argc, char **argv)
{
    if (argc < 3)
    {
        printf("Usage: %s <pass> <command>\n", argv[0]);
        exit(1);
    }
    if (strlen((const char *) argv[1]) > 10)
    {
        printf("Error: pasword too long\n");
        exit(1);
    }

    if (password_check(argv[1]))
    {
        printf("Running command as root: %s\n", argv[2]);
        setuid(0);
        setgid(0);
        system(argv[2]);
    }
    else
    {
        printf("Authentication failed! This activity will be logged!\n");
    }

    return 0;
}

Поэтому я пытаюсь проанализировать программу с помощью IDA и вижу текстовый сегмент, идущий от младших адресов к старшим адресам, выше, чем те, которые я вижу, и затем команды bss и, наконец, внешние команды.

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

Второй вопрос касается длины ввода, как мне обойти эту проверку в коде:

if (strlen((const char *) argv[1]) > 10)
    {
        printf("Error: pasword too long\n");
        exit(1);
    }

Можно ли как-нибудь передать строку в программу по ссылке? Если да, то как мне это сделать? (Опять же, надеясь, что я не совсем невежественен)

1 ответ

Решение

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

Расположение стека все время меняется - вам нужно посмотреть на значение ESP/RSP регистр, его значением является текущий адрес вершины стека. Как правило, адресация переменной будет основана на EBP скорее тогда ESP, но они оба будут указывать на одну и ту же общую область памяти.

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

Ты это видишь text прямо рядом с password_matchи, судя по адресам, есть 0x14 байты выделены для text, как и следовало ожидать. Однако это не гарантируется, и компилятор может свободно перемешивать переменные вокруг, дополнять их или оптимизировать их в регистры.

Второй вопрос касается длины ввода, как мне обойти эту проверку в коде:

if (strlen((const char *) argv[1]) > 10)
{
    printf("Error: pasword too long\n");
    exit(1);
}

Вам не нужно обходить этот чек, он уже достаточно сломан. Там ошибка по одному.

Хватит читать здесь, если вы хотите сами определить переполнение.


Действительный диапазон показателей для text охватывает от text[0] через text[19], В коде пользовательский ввод записывается в область памяти, начиная с text[11], Максимальная длина ввода, разрешенная strlen проверка - 10 символов + терминатор NULL. К сожалению это значит text[19] содержит 9-й введенный пользователем символ, а 10-й символ + терминатор переполнены в соседнюю область памяти. При определенных обстоятельствах это позволяет перезаписывать младший значащий байт password_match с произвольным значением, а второй младший байт с 0,
Ваша функция принимает пароль, если password_match равняется 1, что означает, что 10-й символ в вашем пароле должен быть '\x01' (обратите внимание, что это не тот же символ, что и '1').

Вот два скриншота из IDA, работающего в качестве отладчика. text выделено желтым цветом, password_match в зеленом.

Пароль, который я ввел, был 123456789\x01,

  1. Стек перед тем, как пользователь ввел пароль strcatв text,переменные стека перед переполнением

  2. Стек после strcat, Заметить, что password_match изменилось.переменные стека после переполнения

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