Создание базового переполнения стека с использованием 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
,
Стек перед тем, как пользователь ввел пароль
strcat
вtext
,Стек после
strcat
, Заметить, чтоpassword_match
изменилось.