Linux — нужна помощь, чтобы понять эту короткую программу C ++ и ее уязвимость

Я был бы рад, если бы кто-то мог объяснить мне, что именно делает код. Я знаю, что существует уязвимость, связанная с переполнением буфера и выполнением команд bash, но, поскольку я работаю в сети, а не программистом, я действительно могу использовать некоторую помощь для понимания всего кода. Заранее спасибо!

int main () {
int status;
char t[1024]="ps -eo lstart,cmd | grep ";
cout << "Content-type:text/html\r\n\r\n"<<endl;
char *value = getenv("QUERY_STRING");
strcat(t,value);
status = system(strcat(t," | grep -v grep | head -n 1 | awk '{ print $1\" \"$3\" \"$2\" \"$5\" \"$4}'"));

return 0;
}

0

Решение

ТЛ; др: Вот что делает ваш код в виде сценария оболочки:

#!/bin/bash
echo -en "Content-type:text/html\r\n\r\n"ps -eo lstart,cmd | grep init | grep -v $QUERY_STRING | \
head -n 1 | awk '{ print $1" "$3" "$2" "$5" "$4}'

Теперь ответ дольше.

Переписать код

Во-первых, давайте сделаем это в C ++, а не в C (как показывает ваш тег, о котором вы спрашиваете) с небольшой обработкой ошибок, а затем поговорим о том, что происходит:

#include <iostream>
#include <string>
#include <string_view>

int main () {
auto query_string = getenv("QUERY_STRING");
if (query_string == nullptr) {
std::cerr << "Couldn't obtain QUERY_STRING environment variable\n";
return EXIT_FAILURE;
}
if (std::string_view{query_string}.empty()) {
std::cerr << "Empty query string (QUERY_STRING environment variable)\n";
return EXIT_FAILURE;
}
std::stringstream command_line;
command_line
<< "ps -eo lstart,cmd | grep "<< query_string
<< " | grep -v grep | head -n 1 | awk '{ print $1\" \"$3\" \"$2\" \"$5\" \"$4}'";
std::cout << "Content-type:text/html\r\n\r\n";
return system(command_line.str()); // security vulnerability, see below
}

Что мы тут делаем?

Итак, мы создаем здесь командную строку, которую затем выполняем, используя system() функция. Это вызов ps команда с некоторыми переключателями с последующей обработкой текста с grep, head а также awk — использование механизма конвейера для перемещения вывода каждой команды на следующую. Их ключевой частью является то, что мы используем переменную среды QUERY_STRING фильтровать ps результаты, т.е. мы перечисляем процессы, которые соответствуют какой-то фразе. Если мы скомпилируем эту программу, установим переменную окружения и запустим, это выглядит так:

$ export QUERY_STRING=init
$ ./the_program
Content-type:text/htmlSun 3 Jun 2018 21:48:56

Это дало нам время запуска первого процесса, командная строка которого не включает фразу «init». Так что теперь вы можете догадаться, что моя система работала со вчерашнего дня …

Наконец, как сетевой парень, вы, вероятно, понимаете «контент-тип» mumbo-jumbo, а double-newline — это заголовок MIME, так что этот вывод, вероятно, предназначен для использования в качестве ответа HTTP. Вероятно, это задумано как своего рода CGI-скрипт.

Уязвимости безопасности

  1. В исходном коде размер буфера был произвольно ограничен 1024 — хотя ничто не ограничивало QUERY_SIZE, чтобы он не был длиннее этого. Если оно длиннее, у вас может быть повреждение памяти, что может иметь последствия для безопасности; и злоумышленник, скорее всего, сможет выяснить расположение вашей памяти, так что это более опасно. Это ушло с версией C ++.
  2. Вторая уязвимость связана с system команда. Мы вводим произвольную строку в строку, которую создаем; и ничто не мешает кому-то установить

    $export QUERY_STRING="dummy; rm -rf $HOME ; echo"

    в этом случае вы будете запускать:

    ps -eo lstart,cmd | grep dummy; rm -rf $HOME ; echo | grep -v init | head -n 1 | awk '{ print $1" "$3" "$2" "$5" "$4}'
    

    и это приведет к удалению всего в домашнем каталоге эффективного пользователя. Или это может быть любая команда, включая компиляцию пользовательской программы на C / C ++ для запуска произвольного кода в вашей системе. Очень плохой.

  3. Даже если вы очистите QUERY_STRING только допустимым шаблоном grep, все равно может произойти атака типа «отказ в обслуживании», если кто-то каким-то образом предоставит сложные, сверхдлинные шаблоны grep. Так что ограничение длины тоже хорошая идея.
1

Другие решения

Это просто объявляет переменную status

int status;

Это объявило t, с C-String «ps -eo lstart, cmd | grep».
t имеет максимум 1024 байта в емкости (1023 для строки + 1 байт для \0)

char t[1024]="ps -eo lstart,cmd | grep ";

Распечатать строку ниже

cout << "Content-type:text/html\r\n\r\n"<<endl;

получить переменную среды QUERY_STRING

char *value = getenv("QUERY_STRING");

объединить значение выше, чтобы t, Здесь у вас может быть переполнение буфера, потому что вы не знаете размер строки в value и это может быть больше, чем 1024 байта

strcat(t,value);

Вызвать system() функция для другого объединения предыдущего t со всеми этими командами grep, etc, etc … здесь может произойти переполнение буфера, один раз t может также переполнить свои 1024 байта здесь.

status = system(strcat(t," | grep -v grep | head -n 1 | awk '{ print $1\" \"$3\" \"$2\" \"$5\" \"$4}'"));
0