tl; dr: Вот что делает ваш код в виде сценария оболочки:
#!/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/html
Sun 3 Jun 2018 21:48:56
Это даст нам время запуска первого процесса, командная строка которого не включаетфраза «иници».Так что теперь вы можете догадаться, что моя система работала со вчерашнего дня ...
Наконец, как сетевой парень, вы, вероятно, понимаете mumbo-jumbo "Content-type", а double-newline - это заголовок MIME,поэтому этот вывод, вероятно, предназначен для использования в качестве ответа HTTP.Вероятно, это задумано как некий сценарий CGI.
Уязвимости безопасности
- В исходном коде размер буфера был произвольно ограничен 1024 - хотя не было ничего, что QUERY_SIZE не ограничивало быбыть длиннее этого.Если он длиннее, у вас может быть повреждение памяти, что может иметь последствия для безопасности;и злоумышленник, скорее всего, сможет выяснить расположение вашей памяти, так что это более опасно.Это не относится к версии C ++.
Вторая уязвимость связана с командой 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 ++ для запуска произвольного кода в вашей системе.Очень плохо.
- Даже если вы очистите QUERY_STRING только допустимым шаблоном grep, может возникнуть атака типа «отказ в обслуживании», если кто-то предоставит сложные, сверхдлинные шаблоны grep'ing.как-то.Поэтому ограничение длины также является хорошей идеей.