Существует множество разных видов атак, направленных на эскалацию привилегий локального пользователя. Результатом такой атаки может являться как получение прав суперпользователя (или другого пользователя), так и разрушение важной системной информации. В данной статье будет рассмотрен один из видов атаки, направленной на эскалацию привилегий — атака при помощи символических ссылок. Что интересно, данный вид атаки довольно хорошо описан в литературе, однако зачастую игнорируется разработчиками (несколько реальных примеров из жизни будет в конце статьи) и системными администраторами.

Начнем с простого примера

Предположим что в системе имеется каталог, доступный на запись для всех пользователей. Этот каталог используется для создания и хранения в нем временных файлов и используется всеми пользователями и администратором (root) в том числе.
В классической UNIX-сиcтеме этот каталог называется /tmp. Предположим что администратор написал такой вот скрипт бакапа /etc:

Код:
0   #!/bin/bash
1  
2   DATE=`date +%s`
3   tar -cz /etc > /tmp/backup.etc.$DATE.tgz
4  
5   cp /tmp/backup.etc.$DATE.tgz /backup1/dir
6   mv /tmp/backup.etc.$DATE.tgz /backup2/dir

Пример конечно надуманный, однако та «болезнь», которую мы изучаем довольно часто встречается в реальной жизни. Я решил написать данную статью после того как очередной раз наткнулся на эту проблему на очередном хостинге.
Итак, что мы видим на примере? Мы видим использование предсказуемого имени временного файла.

Как это может быть использовано злоумышленником?

Например: атакующий создает множество символических ссылок вида /tmp/backup.etc.$DATE.tgz, указывающих на файл /etc/passwd. Первый запуск приведенного выше скрипта разрушает один из важнейших файлов системы. Если скрипт запускается хотя-бы один раз в сутки то достаточно создать 86400 ссылок, чтобы уничтожить систему со 100% вероятностью.

Неправильные методы борьбы с данной проблемой

Я несколько раз сталкивался с тем, что когда человеку указываешь на данную проблему, первое что он делает, это — модифицирует скрипт следующим образом:

Код:
0   #!/bin/bash
1  
2   DATE=`date +%s`
3   rm -f /tmp/backup.etc.$DATE.tgz
4   tar -cz /etc > /tmp/backup.etc.$DATE.tgz
5   ...

Однако этот вариант способен защитить лишь от статической атаки симлинками. То есть от атаки вида «симлинки были однажды созданы и просто ждут своего часа». Жизнь обычно гораздо сложнее. Каждая строка в скрипте выполняется фиксированный промежуток времени. Между выполнением команд процессор может переключиться (и он в большинстве случаев так и делает, поскольку каждая команда — неявный вызов функций fork — exec — exit — waitpid) на другой процесс, который может создать симлинк заново.
После указания на то что проблема и на этом шаге не решена, раздосадованный админ пишет нечто вроде:

Код:
0   #!/bin/bash
1  
2   DATE=`date +%s`
3   test -e /tmp/backup.etc.$DATE.tgz && exit -1
4   tar -cz /etc > /tmp/backup.etc.$DATE.tgz
5   ...

Проблема с атаками решена (как кажется), однако теперь стала насущной проблема с бакапами: атакующий блокирует выполнение бакапов.
Имеется даже традиционный неправильный метод формирования имени временного файла с включением в это имя PID ($$) процесса, который с данным файлом работает. Например: /tmp/temp_file.$$. На большинстве UNIX-систем PID процесса который будет запущен через несколько секунд можно предсказать с довольно большой точностью, а в тех UNIX, где эта проблема «решена» специальной рандомизацией PID новых процессов использование PID в имени файла так же не решает проблемы: злоумышленник может создавать миллионы символических ссылок, а так же просто ждать своего часа: «в этот раз не получилось — получится в следующий». Атаковать всегда легче чем защищаться.

Методы борьбы с проблемой

Наиболее очевидный и… методически неверный:

Использование отдельного каталога для временных файлов

То есть у администратора — свой /tmp, у пользователей — свой. Метод работает, однако во первых это может увеличить расходы на администрирование (вспомним, что над стандартным /tmp зачастую имеется некоторый набор в виде скриптов очистки, отдельной файловой системы итп), во вторых перекладывание проблемы в более глубокий карман — это именно перекладывание, а не решение проблемы: ружье может когда-то снова выстрелить в новой инкарнации. Кроме того, если у каждого пользователя имеется свой личный каталог $TMP то централизовано почистить их из тривиальной задачи превращается в задачу непростую.

Использование стандартных средств для создания временных файлов

Для создания временных файлов во всех языках имеются необходимые средства. Как правило применять их весьма просто. Например для случая Shell утилита mktemp (1) или утилита tempfile (1) предоставляют удобный интерфейс для создания временных файлов и каталогов. На языке Perl можно использовать стандартный модуль File::Temp, который позволит вам безопасно создать временный файл или каталог. Для языка Python есть модуль с очевидным названием tempfile, и так далее. Перепишем описанный выше пример в безопасном виде:

Код:
0   #!/bin/bash
1  
2   DATE=`date +%s`
3   TMPFILE=`mktemp`
4   tar -cz /etc > $TMPFILE
5  
6   cp $TMPFILE /backup1/dir/backup.etc.$DATE.tgz
7   mv $TMPFILE /backup2/dir/backup.etc.$DATE.tgz

Некоторые примеры из жизни

Как уже я говорил выше, я периодически натыкаюсь на эту проблему изучая или дорабатывая чужой код в скриптах. Однажды меня попросили зааплоадить очередной пакет в Debian. Postinst-скрипт в этом пакете «страдал» описанной выше проблемой. Продемонстрировав будущему майнтенеру эту проблему я от нечего делать набросал простенький скрипт, который обходил все дерево пакетов Debian и исследовал их на предмет наличия данной уязвимости. Результатом работы этого скрипта было написание более двухсот багов уровня security на множество Deb-пакетов в том числе и на пакеты с высокой популярностью.
Один из этих был совсем занимательный: майнтенер одного из пакетов с вики создавал скриптом временный каталог, затем устанавливал на него права 0777 и запускал несколько дочерних скриптов, которые работали с файлами в этом каталоге. В итоге установка симлинка на /etc/shadow приводила к тому, что атакующий мог получить доступ к произвольному аккаунту на сервере.
Все эти баги в настоящее время уже исправлены.

Windows

Система Windows в настоящее время так же поддерживает символические ссылки. Инструмент для Windows это «относительно» новый, а потому о данной проблеме множество Windows-разработчиков не особо задумывается. Я исследовал каталог $TMP на примерно 20 разных Win-компьютерах (все администрируются разными людьми, от профи-админов, до простых пользователей) и обнаружил на всех в этом каталоге файлы с предсказуемыми именами: Множество инсталляторов различных программ просто распаковывают файлы новой программы в эту директорию или создают временные с жестко заданными именами. Очевидно, что если не установлен явный запрет на запись в «админский $TMP», то данная проблема встает во весь рост и в системе Windows: устанавливают ПО обычно от имени администратора. Более подробные исследования на эту тему оставляю специалистам по Windows :)

Автор - Обухов Дмитрий Эдуардович