SETUID на уровне файловой системы
Предположим, мы смотрим на следующий сценарий:
Файл saymyname.c (включая пропущенный)
int main(int argc, char** argv){
system("whoami");
}
Построить и установить биты разрешений:
cake@lie> gcc saymyname.c -o saymyname
cake@lie> sudo chown root:root saymyname
cake@lie> sudo chmod u+s saymyname
cake@lie> ./saymyname
cake
Каждый ресурс под солнцем говорит мне, что установка s
разрешение на столбец пользователя должно запускать программу с привилегиями владельца, а не вызывающего пользователя. Почему system("whoami");
вернуть cake
?
Модификация программы для установки UID вручную следующим образом:
int main(int argc, char** argv){
setuid(geteuid());
system("whoami");
}
Дает ожидаемый результат
cake@lie> ./saymyname
root
Некоторые ресурсы утверждают, что биты SUID и GUID часто игнорируются. Поэтому происходит наблюдаемое поведение? Если так, есть ли способ заставить его вести себя так, как будто он был выполнен пользователем root без setuid(.)
?
2 ответа
Технически правильный ответ Георга 1, но стоит отметить, что system(3)
Страница man прямо заявляет, что использование system()
в программах setuid не рекомендуется:
Не используйте system() из программы с привилегиями set-user-ID или set-group-ID, потому что странные значения для некоторых переменных среды могут использоваться для нарушения целостности системы. Вместо этого используйте семейство функций exec(3), но не execlp(3) или execvp (3). На самом деле system() не будет работать должным образом из программ с привилегиями set-user-ID или set-group-ID в системах, в которых /bin/sh является версией bash [>=]2, поскольку bash 2 отбрасывает привилегии при запуске, (Debian использует модифицированный bash, который не делает этого, когда вызывается как sh.)
Это особенно актуально в вашем примере, когда вы звоните whoami
без полного пути. Представьте себе следующий сценарий (как непривилегированный пользователь):
> whoami cat << 'EOF'
#!/bin/not-bash :)
echo "I'm root! let's clean up some trash ..."
# rm -rf /
EOF
chmod +x whoami
PATH="${PWD}" ./saymyname
Это означает, что вместо изменения системной оболочки (или использования Debian) код должен просто использовать exec()
, как это:
int main(int argc, char** argv){
execl("/usr/bin/whoami", "whoami", NULL);
}
1 более новые версии dash
на Ubuntu также будут сбрасывать привилегии
Похоже, что bash, который выполняется system(), удаляет привилегии. В моих тестах замена символической ссылки /bin/sh, указывающей на тире (вместо bash), заставила его работать как положено.
Также с Bash,
execl("/bin/bash", "bash", "-c", "whoami", NULL);
дает торт, тогда как
execl("/usr/bin/whoami", "whoami", NULL);
дает корень.