В чем уязвимость access в С/C++?
Я находил похожий вопрос на stackoverflow, но я так и не понимаю в чем смысл подмены ? Вот мы хотим проверить что файл доступен для чтения и если доступен -> открыть. Отлично, мы проверили, файл доступен -> открыли. Другая ситуация: проверяем, доступен ли файл на чтение -> нам его подменяют -> отрываем другой файл(проблема). Чем вторая ситуация отличается от того, что я успею подменить файл в таком коде:
###Псевдокод 1###
int main()
{
delay(5) #delay 5 sec
open("substitutedFile", "r")
}
###Псевдокод 2###
int main()
{
....
.... # Slow code, low performance
open("substitutedFile", "r")
}
Вопрос в том, почему уязвимый именно access ? Когда такую подмену можно сделать в любом из выше приведенных сценариев. Зачем вообще нужен access если возвращаемое значение open даст мне знать, доступен ли файл на чтение/запись, а execv в возвращаемом значении даст понять, доступен ли файл на выполнение https://www.usenix.org/legacy/publications/library/proceedings/sec04/tech/full_papers/dean/dean_html/accessopen.html
https://stackoverflow.com/questions/7925177/access-security-hole
Ответы (2 шт):
Зачем вообще нужен access если возвращаемое значение open даст мне знать, доступен ли файл на чтение/запись, а execv в возвращаемом значении даст понять, доступен ли файл на выполнение
В абсолютном большинстве случаев так и надо поступать: просто пробовать открыть файл и обрабатывать ошибки, если это не удалось (с exec() это может быть затруднительно).
Как говорит тётя маня, access() в отличии от open() использует не эффективные, а реальные UID/GID, т.е. их результат может быть разным для процессов с suid битом и именно в таких случаях нужно использовать access():
Другими словами,
access()не отвечает на вопрос «могу я прочитать/записать/выполнить этот файл?». Он отвечает на немного другой вопрос: «(предположим я исполняемый файл с setuid) может ли пользователь, запустивший меня прочитать/записать/выполнить этот файл?», что даёт программам с set-user-ID возможность не дать злонамеренным пользователям прочитать файлы, которые пользователи не должны читать.
Когда возникает уязвимость
Допустим есть процесс с suid, который должен проверить, есть ли у вызвавшего его пользователя право на чтение заданного файла:
if(access("/path/to/file", R_OK) == 0) {
int fd = open("/path/to/file", O_RDONLY);
assert(fd>=0);
print_file(fd);
close(fd)
}
В этом случае возможна следующая последовательность действий:
- т.к. изначальный файл принадлежал пользователю, то проверка
access()выполняется успешно. - Злоумышленник удаляет исходный файл и заменить его ссылкой на файл к которому у него нет доступа, например, ссылкой на
/etc/shadow. - т.к. процесс работает с повышенными привилегиями, то вызов
open()успешно открывает файл по ссылке и выводит его.
man в принципе рекомендует «избегать использования данного системного вызова».
Зачем вообще нужен access если возвращаемое значение open даст мне знать, доступен ли файл на чтение/запись
Как было указано выше, по стандарту access() проверяет права исходя из реальных UID/GID, а результат open() зависит от эффективных.
Т.е. в программах с повышенными или пониженными привилегиями результат может отличаться.
Вопрос в том, почему уязвимый именно access ?
Сам access() - неуязвим. Но, программы с повышением или понижением привилегий, которые его неправильно используют, могут иметь весьма неопределённое поведение.
В частности, варианты связки access() и open()
https://www.usenix.org/legacy/publications/library/proceedings/sec04/tech/full_papers/dean/dean_html/accessopen.html Fixing Races for Fun and Profit: How to use access(2)
Выглядит корректным.
https://stackoverflow.com/questions/7925177/access-security-hole
Не выглядит полным, в части прав на поиск по пути.
Для однопоточных программ, вероятно, самым надёжным и простым является способ: ... seteuid(); setegid(); open(); ...