проблема безопасности с set-uid и относительным путем для INTERP (динамического компоновщика) в ELF

Комбинация set-uid и относительного пути в разделе INTERP двоичного файла ELF очень опасна.

Я не совсем уверен, как и где следует сообщать об этой проблеме, но мне кажется, что это общая проблема безопасности, связанная с тем, как работает динамическая компоновка в linux/glibc, поэтому позвольте мне объяснить, что это такое:

Рассмотрите возможность создания динамически компонуемого бинарного файла и указания относительного пути в разделе ELF INTERP (используя параметр --dynamic-linker gcc), чтобы вы могли распространять пользовательскую версию glibc с вашим динамически компонуемым коммерческим приложением (где вам не разрешено статично компоновать против LGPL glibc, но вам все равно нужно, чтобы ваш двоичный файл работал в разных дистрибутивах Linux, имеющих разные версии glibc).

Если вы присвоите бинарному файлу права root и установите для него флаг set-uid, он фактически станет руткитом. Выполняя его из другого каталога, вы можете заменить исполняемый файл динамического компоновщика, который будет выполняться с правами root.

Чтобы продемонстрировать это, взгляните на следующий код C (issue.c):

#include <stdio.h> 

// 
// build with: 
//   gcc -DNAME=\"vulnarable\" -o issue -Wl,--dynamic-linker,.lib64/ld-linux-x86-64.so.2 issue.c 
//   sudo chown root issue 
//   sudo chmod u+s issue 
// now build some code to be executed with root permissions (we use the same issue.c): 
//   mkdir -p .lib64/ 
//   gcc -DNAME=\"rootkit\" -o .lib64/ld-linux-x86-64.so.2 --static issue.c 
// 

int main(int argc, char* argv[]) 
{ 
    printf("(%s) euid:%d\n", NAME, geteuid()); 
} 

Если вы сейчас выполните двоичный файл set-uid следующим образом

./issue

или даже просто делать это

ldd issue

вместо того, чтобы получить то, что вы могли бы ожидать, например:

(vulnarable) euid:0

ты получаешь:

(rootkit) euid:0

Суть в том, что вы можете заменить двоичный файл ld-linux-x86-64.so.6 чем угодно.

Подобные проблемы, по-видимому, были решены путем отказа от разрешения $ORIGIN в RPATH или игнорирования LD_LIBRARY_PATH, если установлен флаг set-uid.

Поэтому мне интересно, нужно ли игнорировать INTERP в ELF всякий раз, когда установлен флаг set-uid (т.е. с использованием динамического компоновщика по умолчанию - /lib32/ld-linux.so.2 или /lib64/ld-linux-x86- 64.so.2)?

Так что вы думаете, где это должно быть исправлено или сообщено - в glibc или в ядре?


person siddhadev    schedule 26.01.2012    source источник
comment
Вы предлагаете способ получить root-доступ, который включает использование sudo. Это означает, что вам нужен root-доступ, чтобы сделать это.   -  person ugoren    schedule 04.02.2012
comment
На каком ядре и дистрибутиве вы это тестировали? У меня возникли проблемы с воспроизведением проблемы в Ubuntu 10.04 с ядром 2.6.32-38-generic: все, что я получаю, это (./issue) euid:0, что, как и ожидалось, для программы setuid.   -  person Ilmari Karonen    schedule 04.02.2012
comment
@ugoren: Не совсем так. Если предположить, что уязвимость реальна, все, что нужно злоумышленнику, — это найти уязвимую программу setuid. Команды sudo в инструкциях предназначены только для его создания, чтобы можно было продемонстрировать проблему.   -  person Ilmari Karonen    schedule 04.02.2012
comment
@Ilmari: я только что обновил инструкцию, она несколько вводила в заблуждение.   -  person siddhadev    schedule 04.02.2012
comment
Вы должны четко указать, кто атакует, а кто подвергается нападению. Судя по описанию, злоумышленник создает программу setuid, чтобы воспользоваться этой проблемой.   -  person ugoren    schedule 04.02.2012


Ответы (1)


Да, небезопасно иметь двоичный файл setuid, указывающий интерпретатор в небезопасном месте (вы упоминаете относительный путь, но фиксированный путь, доступный для записи во всем мире, имеет ту же проблему). Но это логическое завершение дизайна ELF, и добавление специального случая к обработке INTERP для двоичных файлов setuid - не лучший путь. Это случай «Доктор, мне больно, когда я это делаю. — Не делайте этого». Да, эта комбинация небезопасна, поэтому просто не используйте ее! В любом случае вмешательство в интерпретатор ELF является чем-то довольно сложным, поэтому вам не следует этого делать, если вы не понимаете, что делаете, и в этом случае вы можете логически сделать вывод, что безопасно, а что нет.

Расширенные функции ELF/POSIX/Unix/что-то еще дают вам мощные способы выстрелить себе в ногу, потому что они мощные. Если бы вы хотели избавиться от всех возможных плохих ситуаций, у вас была бы система, гораздо менее гибкая и для нее гораздо труднее программировать, но все же с некоторыми дырами в ней.

person F'x    schedule 03.02.2012
comment
Согласованный. Это похоже на вызов system() в программе setuid с командой, которая не является абсолютным путем. Я должен добавить, что если кто-то хочет использовать пользовательскую версию libc в программе, не используйте для этого относительный INTERP; просто свяжите его статически. Обычно LGPL разрешает это. - person Dolda2000; 04.02.2012
comment
@F'x: хороший ответ, я согласен быть осторожным с тем, где вы помещаете флаг set-uid, однако ваш аргумент также применим к RPATH/RUNPATH в ELF или к переменной окружения LD_LIBRARY_PATH - в этом случае (флаг set-uid) , RPATH/RUNPATH и LD_LIBRARY_PATH вместе игнорируются. Так почему бы не игнорировать INTERP? - person siddhadev; 04.02.2012
comment
Это не относится к LD_LIBRARY_PATH, поскольку может быть установлено пользователем, а не создателем исполняемого файла. Во всяком случае, мне кажется странным, что RUNPATH действительно обрабатывается, а не наоборот. - person Dolda2000; 04.02.2012
comment
@ dolda2000: я не думаю, что статические ссылки разрешены, если вы не распространяете свой исходный код или объектные файлы. LGPL фактически требует, чтобы кто-то другой мог заменить glibc, например. связать другой. - person siddhadev; 04.02.2012
comment
@ dolda2000: установка пользователем LD_LIBRARY_PATH в приведенном выше сценарии соответствует выполнению уязвимого двоичного файла из другого каталога (в случае относительного пути) или замене бинарного файла dyn-linker на общедоступный фиксированный путь. Точно так же, как игнорируется RUNPATH в ELF, было бы просто также игнорировать INTERP. - person siddhadev; 04.02.2012
comment
Это правда, что LGPL требует, чтобы вы разрешали пользователям повторно связываться с другим glibc. Но разве это проблема? Если вы распространяете исполняемый двоичный файл, и вам уже нужно распространять свои изменения в glibc, есть ли у вас что-то потери от распространения вместе с ними перелинковываемого объектного файла вашей программы? - person Dolda2000; 04.02.2012
comment
Это не очень хорошо соответствует, потому что раздел INTERP — это то, над чем создатель двоичного файла может контролировать, в то время как он не может контролировать LD_LIBRARY_PATH среды, в которой он работает. - person Dolda2000; 04.02.2012
comment
@ dolda2000: (мы могли бы добавить еще одну тему в LGPL) Я не уверен - должен ли я распространять все объектные файлы (из действительно большого проекта)? На самом деле я не меняю glibc, просто нужна конкретная версия, чтобы бинарник мог работать везде. - person siddhadev; 04.02.2012
comment
Вам не нужно распространять все объектные файлы — просто свяжите их вместе в один большой промежуточный объектный файл с чем-то вроде ld -r -o almost-executable.o a.o b.o c.o.... Конечно, я не знаю, как это будет связано с вашим процессом сборки, но я думаю, что это не обязательно должно быть сложнее, чем связать их все вместе в окончательный исполняемый файл. - person Dolda2000; 04.02.2012